How to use CDC VCOM example with printf?

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

How to use CDC VCOM example with printf?

Jump to solution
2,908 Views
ashesman
Contributor III

Hi,

 

Using KDS3.2, SDK 2.0.  I would like to implement a virtual com port in our project.  I have the dev_cdc_vcom_lite_bm_frdmk22f example project working fine.  I see where and how it reads the received characters and writes them back.  I cant see it anywhere obvious but is there an example project or easy way of redirecting some sort of formatted input and output to the virtual com port.  Currently standard IO is redirected to the debug UART which I am also using.  So...  I would like to be able to use formatted output for multiple UARTs.  Actually I have three I need to send to!

 

This would make a good advancement on the example demo project...

 

Any help would be appreciated

 

Regards

 

Ashley

Labels (1)
1 Solution
1,600 Views
jorge_a_vazquez
NXP Employee
NXP Employee

Hi Ashley Duncan

If you are still interested of this, I have changed the SDK 2.0 dev_cdc_vcom_bm_frdmk22f example, You have to add some code that is not implemented in sdk:

Debug console is configured to be used with UART, so you need to change its default configuration to use USB. in fsl_debug_console.c I added USB library:

#include "usb_device_config.h"
#include "usb.h"
#include "usb_device.h"
#include "usb_device_class.h"
#include "usb_device_descriptor.h"
#include "virtual_com.h"
‍‍‍‍‍‍

If you check in this file you will find a DebugConsoleOperationFunctions structure that define functions for debug console, so inside here I define a USB_Send and a USB_Receive:

pastedImage_1.png

DbgConsole_Init function is called to set the function pointers to the function that will handler the read and write function, inside this function you can find a switch case that use for example DEBUG_CONSOLE_DEVICE_TYPE_UART or DEBUG_CONSOLE_DEVICE_TYPE_LPSCI. This macros are defined in fsl_common.h, here you can also find a DEBUG_CONSOLE_DEVICE_TYPE_USBCDC, so I used this to add the case branch.

case DEBUG_CONSOLE_DEVICE_TYPE_USBCDC:
{
     //VirtualCom_Init();
     s_debugConsole.base = s_cdcVcom.deviceHandle;
     s_debugConsole.ops.tx_union.USB_Send = VirtualCom_SendDataBlocking;
     s_debugConsole.ops.rx_union.USB_Receive = VirtualCom_ReceiveDataBlocking;
}
break;‍‍‍‍‍‍‍‍

As you can see, I comment the VirtualCom_Init, this is because I going to initiate this in the application. So, my USB_Send and USB_Receive points to two function that will send through USB and they are user defined in other part of the code (I going to defined in the main file), so I need to defined as extern.

extern usb_cdc_vcom_struct_t s_cdcVcom;
extern void VirtualCom_SendDataBlocking (uint32_t base, const uint8_t *buf, uint32_t count);
extern usb_status_t VirtualCom_ReceiveDataBlocking (uint32_t base, uint8_t *buf, uint32_t count);‍‍‍

There is also a DbgConsole_Deinit function, in this case you can also add the case for USB deinit.

This is all that you need to do to point some functions to USB application, now in virtual_com.c file I define my function as:

void VirtualCom_SendDataBlocking (uint32_t base, const uint8_t *buf, uint32_t count) {
     usb_status_t error = kStatus_USB_Success;
     if ((buf == NULL) || (base == NULL))
     {
          error = kStatus_USB_InvalidParameter;
     }
     else
     {
          if ((1 != s_cdcVcom.attach) && (1 != s_cdcVcom.startTransactions))
          {
               error=kStatus_USB_ControllerNotFound;
          }
          else
          {
               memcpy(s_currSendBuf, buf, count > DATA_BUFF_SIZE ? DATA_BUFF_SIZE : count);
               if (USB_DeviceCdcAcmSend(s_cdcVcom.cdcAcmHandle, USB_CDC_VCOM_BULK_IN_ENDPOINT, s_currSendBuf, count > DATA_BUFF_SIZE ? DATA_BUFF_SIZE : count) != kStatus_USB_Success)
               {
                    /* Failure to send Data Handling code here */
               } else {
                    /* Wait until transmission are done */
                    while (!g_sendFinished) {};
                    g_sendFinished = false;
               }
          }
     }
}

usb_status_t VirtualCom_ReceiveDataBlocking (uint32_t base, uint8_t *buf, uint32_t count) {
     usb_status_t error = kStatus_USB_Success;
     if ((buf == NULL) || (base == NULL)) {
          error = kStatus_USB_InvalidParameter;
     } else {
          /* validate that count is not greater than bulk max size */
          if (USB_DeviceCdcAcmRecv(s_cdcVcom.cdcAcmHandle, USB_CDC_VCOM_BULK_OUT_ENDPOINT,
                    s_currRecvBuf, g_UsbDeviceCdcVcomDicEndpoints[0].maxPacketSize) != kStatus_USB_Success)
          {
               error = kStatus_USB_TransferFailed;
               /* Failure to received Data Handling code here */
          } else {
               while (!g_receiveDone) {};
               memcpy(buf, s_currRecvBuf, g_UsbDeviceCdcVcomDicEndpoints[0].maxPacketSize != count ? g_UsbDeviceCdcVcomDicEndpoints[0].maxPacketSize : count);
               g_receiveDone = false;
          }
     }
     return error;
}‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

As you can see here, I use two global variable, g_sendFinished and g_receiveDone (they are initialized as false). In usb libraries there is a function that handle CDC specific requests, here you can find a kUSB_DeviceCdcEventSendResponse and a kUSB_DeviceCdcEventRecvResponse. This functions are implemented to work with the example code of a loopback, I will modify them with my own receive and send event:

case kUSB_DeviceCdcEventSendResponse:
/*More code.......*/
#if Original
                    USB_DeviceCdcAcmRecv(handle, USB_CDC_VCOM_BULK_OUT_ENDPOINT, s_currRecvBuf,g_UsbDeviceCdcVcomDicEndpoints[0].maxPacketSize);
#elif My_app_debug
                    g_sendFinished = true;
#endif
/*More code.......*/


case kUSB_DeviceCdcEventRecvResponse:
     {
          if ((1 == s_cdcVcom.attach) && (1 == s_cdcVcom.startTransactions))
          {
               s_recvSize = epCbParam->length;
#if My_app_debug
               g_receiveDone = true;
#endif
/*More code.......*/‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

So, my two functions will be blocked until this events occurs (using my global variables).

And this is all that you have to do to defined your send and recieve functions, now in main() you find a BOARD_InitDebugConsole(); and inside you see that they used the DbgConsole_Init that we have previously modify, so I changed as:

void BOARD_InitDebugConsole(void)
{
    uint32_t uartClkSrcFreq = BOARD_DEBUG_UART_CLK_FREQ;

   // DbgConsole_Init(BOARD_DEBUG_UART_BASEADDR, BOARD_DEBUG_UART_BAUDRATE, BOARD_DEBUG_UART_TYPE, uartClkSrcFreq);

    DbgConsole_Init(BOARD_DEBUG_USBCDC_INSTANCE, BOARD_DEBUG_USBCDC_BAUD, DEBUG_CONSOLE_DEVICE_TYPE_USBCDC,0);
}‍‍‍‍‍‍‍‍

(you going to need to define BOARD_DEBUG_USBCDC_INSTANCE and BOARD_DEBUG_USBCDC_BAUD)

Now as this function used s_cdcVcom.deviceHandle I need VirtualCom_Init() to be called first, so in main

pastedImage_2.png

APPInit will call it.

I implemented a basic PRINTF("Hello\n\r"); to prof my program:

pastedImage_4.png

Hope this could help you, I have attached my code as reference (you need to locate it in the same path of the example code because it use USB and SDK linked paths).

Best Regards

Jorge Alcala

View solution in original post

2 Replies
1,601 Views
jorge_a_vazquez
NXP Employee
NXP Employee

Hi Ashley Duncan

If you are still interested of this, I have changed the SDK 2.0 dev_cdc_vcom_bm_frdmk22f example, You have to add some code that is not implemented in sdk:

Debug console is configured to be used with UART, so you need to change its default configuration to use USB. in fsl_debug_console.c I added USB library:

#include "usb_device_config.h"
#include "usb.h"
#include "usb_device.h"
#include "usb_device_class.h"
#include "usb_device_descriptor.h"
#include "virtual_com.h"
‍‍‍‍‍‍

If you check in this file you will find a DebugConsoleOperationFunctions structure that define functions for debug console, so inside here I define a USB_Send and a USB_Receive:

pastedImage_1.png

DbgConsole_Init function is called to set the function pointers to the function that will handler the read and write function, inside this function you can find a switch case that use for example DEBUG_CONSOLE_DEVICE_TYPE_UART or DEBUG_CONSOLE_DEVICE_TYPE_LPSCI. This macros are defined in fsl_common.h, here you can also find a DEBUG_CONSOLE_DEVICE_TYPE_USBCDC, so I used this to add the case branch.

case DEBUG_CONSOLE_DEVICE_TYPE_USBCDC:
{
     //VirtualCom_Init();
     s_debugConsole.base = s_cdcVcom.deviceHandle;
     s_debugConsole.ops.tx_union.USB_Send = VirtualCom_SendDataBlocking;
     s_debugConsole.ops.rx_union.USB_Receive = VirtualCom_ReceiveDataBlocking;
}
break;‍‍‍‍‍‍‍‍

As you can see, I comment the VirtualCom_Init, this is because I going to initiate this in the application. So, my USB_Send and USB_Receive points to two function that will send through USB and they are user defined in other part of the code (I going to defined in the main file), so I need to defined as extern.

extern usb_cdc_vcom_struct_t s_cdcVcom;
extern void VirtualCom_SendDataBlocking (uint32_t base, const uint8_t *buf, uint32_t count);
extern usb_status_t VirtualCom_ReceiveDataBlocking (uint32_t base, uint8_t *buf, uint32_t count);‍‍‍

There is also a DbgConsole_Deinit function, in this case you can also add the case for USB deinit.

This is all that you need to do to point some functions to USB application, now in virtual_com.c file I define my function as:

void VirtualCom_SendDataBlocking (uint32_t base, const uint8_t *buf, uint32_t count) {
     usb_status_t error = kStatus_USB_Success;
     if ((buf == NULL) || (base == NULL))
     {
          error = kStatus_USB_InvalidParameter;
     }
     else
     {
          if ((1 != s_cdcVcom.attach) && (1 != s_cdcVcom.startTransactions))
          {
               error=kStatus_USB_ControllerNotFound;
          }
          else
          {
               memcpy(s_currSendBuf, buf, count > DATA_BUFF_SIZE ? DATA_BUFF_SIZE : count);
               if (USB_DeviceCdcAcmSend(s_cdcVcom.cdcAcmHandle, USB_CDC_VCOM_BULK_IN_ENDPOINT, s_currSendBuf, count > DATA_BUFF_SIZE ? DATA_BUFF_SIZE : count) != kStatus_USB_Success)
               {
                    /* Failure to send Data Handling code here */
               } else {
                    /* Wait until transmission are done */
                    while (!g_sendFinished) {};
                    g_sendFinished = false;
               }
          }
     }
}

usb_status_t VirtualCom_ReceiveDataBlocking (uint32_t base, uint8_t *buf, uint32_t count) {
     usb_status_t error = kStatus_USB_Success;
     if ((buf == NULL) || (base == NULL)) {
          error = kStatus_USB_InvalidParameter;
     } else {
          /* validate that count is not greater than bulk max size */
          if (USB_DeviceCdcAcmRecv(s_cdcVcom.cdcAcmHandle, USB_CDC_VCOM_BULK_OUT_ENDPOINT,
                    s_currRecvBuf, g_UsbDeviceCdcVcomDicEndpoints[0].maxPacketSize) != kStatus_USB_Success)
          {
               error = kStatus_USB_TransferFailed;
               /* Failure to received Data Handling code here */
          } else {
               while (!g_receiveDone) {};
               memcpy(buf, s_currRecvBuf, g_UsbDeviceCdcVcomDicEndpoints[0].maxPacketSize != count ? g_UsbDeviceCdcVcomDicEndpoints[0].maxPacketSize : count);
               g_receiveDone = false;
          }
     }
     return error;
}‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

As you can see here, I use two global variable, g_sendFinished and g_receiveDone (they are initialized as false). In usb libraries there is a function that handle CDC specific requests, here you can find a kUSB_DeviceCdcEventSendResponse and a kUSB_DeviceCdcEventRecvResponse. This functions are implemented to work with the example code of a loopback, I will modify them with my own receive and send event:

case kUSB_DeviceCdcEventSendResponse:
/*More code.......*/
#if Original
                    USB_DeviceCdcAcmRecv(handle, USB_CDC_VCOM_BULK_OUT_ENDPOINT, s_currRecvBuf,g_UsbDeviceCdcVcomDicEndpoints[0].maxPacketSize);
#elif My_app_debug
                    g_sendFinished = true;
#endif
/*More code.......*/


case kUSB_DeviceCdcEventRecvResponse:
     {
          if ((1 == s_cdcVcom.attach) && (1 == s_cdcVcom.startTransactions))
          {
               s_recvSize = epCbParam->length;
#if My_app_debug
               g_receiveDone = true;
#endif
/*More code.......*/‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

So, my two functions will be blocked until this events occurs (using my global variables).

And this is all that you have to do to defined your send and recieve functions, now in main() you find a BOARD_InitDebugConsole(); and inside you see that they used the DbgConsole_Init that we have previously modify, so I changed as:

void BOARD_InitDebugConsole(void)
{
    uint32_t uartClkSrcFreq = BOARD_DEBUG_UART_CLK_FREQ;

   // DbgConsole_Init(BOARD_DEBUG_UART_BASEADDR, BOARD_DEBUG_UART_BAUDRATE, BOARD_DEBUG_UART_TYPE, uartClkSrcFreq);

    DbgConsole_Init(BOARD_DEBUG_USBCDC_INSTANCE, BOARD_DEBUG_USBCDC_BAUD, DEBUG_CONSOLE_DEVICE_TYPE_USBCDC,0);
}‍‍‍‍‍‍‍‍

(you going to need to define BOARD_DEBUG_USBCDC_INSTANCE and BOARD_DEBUG_USBCDC_BAUD)

Now as this function used s_cdcVcom.deviceHandle I need VirtualCom_Init() to be called first, so in main

pastedImage_2.png

APPInit will call it.

I implemented a basic PRINTF("Hello\n\r"); to prof my program:

pastedImage_4.png

Hope this could help you, I have attached my code as reference (you need to locate it in the same path of the example code because it use USB and SDK linked paths).

Best Regards

Jorge Alcala

1,600 Views
ashesman
Contributor III

To add to this... I wondered if I could uses fprintf and have handles to my various ports in the and _write function.  But, I get a hard fault if I try call fprintf.  Calling printf works fine and calls the _write function.  However, there does seem to be some sort of built in internal buffering in the debug printf implementation as the _write function is not hit until you try resume execution!

0 Kudos