Hello Forum! I am working on a FRDM-KL25Z dev board and hopign to have it come up as a virtual com port on a windows machine and then interact with the incoming data inside the ARM. I didn't see a straightforward example of doing a VCOM port on this processor so I started out with the USB HID example for the tower system with the same processor (Keil\ARM\Boards\Freescale\XTWR-KL25Z48M\RL\USB\Device\HID) and then copied the usb_config.c and usbd_user_cdc.c from another example (Keil\ARM\Boards\Keil\MCBSTR9\RL\USB\Device\CDC). The project compiles, I load it on the target, and then I see the virtual com port on my system (after installing the driver). I have stripped out the code from usbd_user_cdc.c to remove UART functionality. The problem I am having is when I try to open the virtual com port on the windows side. I tried 9600 baud, which seemed to be the default rate in the demo. I tried Putty and RealTerm, both applications hang when I try to open the specific com port. I was wondering if you folks had an idea what I was doing wrong or how I can begin to troubleshoot this. I have debugging access and it seems that USB works enough to identify its self on my system. I also included the CM1 usb library. I added some relevant code below. (I verified usb_config.c with the wizard and it seems to make sense) Thank you in advance!
I had to put the files on pastebin to meet the 7000 character limit
usb_config.c: http://pastebin.com/4vtczSUB usbd_user_cdc.c: http://pastebin.com/RrSrH3ML usbd_MKL25Z4.c: http://pastebin.com/D9WGuqnD
Thanks to your screenshots.
In the sniffer log, your firmware processes Get_Line_Coding and Set_Control_Line_State successfully. But It fails Set_Line_Coding response. Now that we should focus on the process of this request on your firmware.
Set_Line_Coding process should end up with call to usbd_cdc_ser_initport() (usbd_user_cdc.c), to set up UART baudrate, data bits, parity and stop bits. That is, cross reference to usbd_cdc_ser_initport() on the map file will give us some clue on the bug.
Here is a couple of map file analysis.
C:\Keil\ARM\Boards\Keil\MCB2300\RL\USB\Device\RTX\CDC\Lst\VirtualCOM.map - USB_ARM_L.lib Section Cross References usbd_cdc.o(i.USBD_CDC_GetSerialState) refers to usbd_user_cdc.o(.text) for usbd_cdc_ser_linestate Adding Veneers to the image Adding TA veneer (8 bytes, Short) for call to 'usbd_cdc_ser_linestate' from usbd_cdc.o(i.USBD_CDC_GetSerialState). Adding TA veneer (8 bytes, Short) for call to 'usbd_cdc_ser_initport' from usbd_cdc.o(i.USBD_CDC_SetLineCoding).
USB_ARM_L.lib - usbd_cdc_ser_linestate() appears cross references, but usbd_cdc_ser_initport() doesn't - Both of functions are refered over TA veneer
C:\Keil\ARM\Boards\Keil\MCB1700\RL\USB\Device\RTX\CDC\Lst\VirtualCOM.map - USB_CM3.lib Section Cross References usbd_cdc.o(i.USBD_CDC_GetSerialState) refers to usbd_user_cdc.o(.text) for usbd_cdc_ser_linestate
USB_CM3.lib - usbd_cdc_ser_linestate() appears cross references - usbd_cdc_ser_initport() is not referred from anywhere
Apparently, USBD_CDC_SetLineCoding() implementation on the library source (usbd_user_cdc.c) has a bug, which doesn't call usbd_cdc_ser_initport(), depending on compilation switch.
Unfortunately, the source code of usbd_user_cdc.c is not open. Contact to Keil for the fix of this bug.
Tsuneo
Ah, the bug lies on USBD_CDC_SetLineCoding() in usbd_cdc.c in the library, not on usbd_user_cdc.c
Tsuneo, Thanks for all or your help. I loaded code on my target and verified that both usbd_cdc_ser_openport() and usbd_cdc_ser_initport() are called once when the unit starts up and one more time when I try to open the VCOM serial port with e.g. putty. Can you let me know what you think CM1 USB lib is not doing correctly so I can try to be more specific in the Keil service request? Also, if this is an issue where USB stack is not replying to a packet type correctly, would it be possible to modify USB interrupt service routine to route the SetLineCoding request to my own code to bypass Keil's USB lib to have correct operation? Thanks.
Unfortunately, user code can't override USBD_CDC_SetLineCoding() in the library. We have to override the caller of this routine, USBD_EndPoint0_Out_CDC_ReqToIF(), to fix this bug. Keil had once exposed the primitive version of current RL-USB in RL v4.12 This snippet derives from RL v4.12, modified to fit to current RL-USB.
#include <..\..\RL\USB\INC\usb.h> extern CDC_LINE_CODING USBD_CDC_LineCoding; extern U8 USBD_EP0Buf[]; BOOL MyUSBD_CDC_SetLineCoding(void) { USBD_CDC_LineCoding.dwDTERate = (USBD_EP0Buf[0] << 0) | (USBD_EP0Buf[1] << 8) | (USBD_EP0Buf[2] << 16) | (USBD_EP0Buf[3] << 24); USBD_CDC_LineCoding.bCharFormat = USBD_EP0Buf[4]; USBD_CDC_LineCoding.bParityType = USBD_EP0Buf[5]; USBD_CDC_LineCoding.bDataBits = USBD_EP0Buf[6]; usbd_cdc_ser_closeport(); usbd_cdc_ser_openport(); usbd_cdc_ser_initport(USBD_CDC_LineCoding.dwDTERate, USBD_CDC_LineCoding.bDataBits, USBD_CDC_LineCoding.bParityType, USBD_CDC_LineCoding.bCharFormat); return (__TRUE); } extern const U8 usbd_cdc_cif_num; extern BOOL USBD_CDC_SendEncapsulatedCommand( void ); extern BOOL USBD_CDC_SetCommFeature( U16 wFeatureSelector ); BOOL USBD_EndPoint0_Out_CDC_ReqToIF(void) { if (USBD_SetupPacket.wIndex != usbd_cdc_cif_num) return (__FALSE); switch (USBD_SetupPacket.bRequest) { case CDC_SEND_ENCAPSULATED_COMMAND: if (USBD_CDC_SendEncapsulatedCommand()) { USBD_StatusInStage(); return (__TRUE); } break; case CDC_SET_COMM_FEATURE: if (USBD_CDC_SetCommFeature(USBD_SetupPacket.wValue)) { USBD_StatusInStage(); return (__TRUE); } break; case CDC_SET_LINE_CODING: if (MyUSBD_CDC_SetLineCoding()) { USBD_StatusInStage(); return (__TRUE); } break; default: break; } return (__FALSE); }
Hi Tsuneo, I added your code to my project, started debugging, and verified that we enter MyUSBD_CDC_SetLineCoding() when trying to open the serial port with putty. The function does get called, however, I still have the same issue with cancelled Set Line Coding (oi49.tinypic.com/10iex5d.jpg). I have tried opening the port with different speeds and verified that the _initport() function gets the correct speed value, for example. I have also uploaded the capture if this will be helpful for you here: routed.net/VCOM-20121118a.ulz . Thanks again for looking into this.
I have also uploaded the project source code here for reference: routed.net/kl25z-cdc-test02.zip . Thanks!
I thought above snippet should fix your problem, but it didn't. My first assumption, bug on USBD_CDC_SetLineCoding() implementation, was wrong.
Then, getting back to your sniffer logs, closely examining them, I found that Set_Line_Coding doesn't always fail. Fail always occurs on Set_Line_Coding, but some Set_Line_Coding success. It likely means timing issue.
Please take this "hub" test. 1) Connect the device directly to your PC USB port. 2) Connect the device to PC over a USB2.0 hub.
If direct connection results in no error, but if connection over a hub causes error, your device has critical timing problem.
Of course, timing issue is well observed with a hardware bus analyzer. "Hub test" is a handy method ;-)
Hi Tsuneo, I do not have an additional USB hub near me at the time, however, the device is directly connected to USB ports on my machine. I am doing development inside of a VMWare VM, but I tried to open the port in the VM and on the native OS with the same result. Is it possible that multiple USB requests are getting placed into an incoming buffer together and only the first one is processed? The only thing I am doing in the code is polling the USB library:
while (1) { // Loop forever usbd_vcom_serial2usb(); // write to USB VCOM Port usbd_vcom_chkserstate(); // check status usbd_vcom_usb2serial(); // write to Serial Port }
Not sure what can be causing timing issues there. I don't have access to a hardware USB sniffer either so I was thinking of adding some way to log USB requests to the USB0 ISR to compare sequence numbers of requests (wValueH,wValueL) and storing them in RAM then checking with a debugger to see if I can match them up with what I see in the software USB capture. Does this this sound like an OK idea or should I try a different approach? Thanks.
Hi, same problem on Freescale’s Board TWR-K60N512. There is no example how to use CDC. So I tried to convert one of other examples (HID - which worked), into CDC. Basically I removed usbd_user_hid.c , KBD.c files from project. Copied usbd_user_cdc.c and usb_config.c from c:\Keil\ARM\Boards\Keil\MCB2300\RL\USB\Device\CDC\ to project folder and added those 2 files. After that I modified USBD_Demo.c and usbd_user_cdc.c. On Windows 7 Professional 64 bit, I used : mcb2300-vcom.inf, mcb2300-vcom_x86.cat, mcb2300-vcom_amd64.cat files from placed in c:\Keil\ARM\Boards\Keil\MCB2300\RL\USB\Device\CDC\ to install drivers. (Or its modifications, basically all text in mcb2300-vcom.inf and usb_config.c were set to “Keil”,and PID, VID changed to corresponding values in usb_config.c). Everything is compiling, and running. But when I am trying open virtual serial port on my Windows application Putty, usbd_cdc_ser_initport(U32 baudrate, U32 databits, U32 parity, U32 stopbits) is called – with proper parameter values (set in Putty). After that Putty (same with other similar applications), disappears or is locked. So it does not work at all. I have installed USBlyzer and all communication looks very similar.
CODE: *USB_Demo.c
#include <RTL.h> #include <rl_usb.h> #include "LED.h" OS_TID task_id_write; U64 write_stack [1200/8]; __task void writeTask (void); __task void init (void) { os_tsk_prio_self(1); usbd_init(); usbd_connect(__TRUE); os_tsk_prio_self(1); task_id_write = os_tsk_create_user (writeTask, 1, &write_stack, sizeof (write_stack)); while (!usbd_configured()){}; LED_Val(0x0); while (1) { usbd_vcom_serial2usb(); usbd_vcom_chkserstate(); usbd_vcom_usb2serial(); os_tsk_pass (); } } /* buffer type */ typedef struct __SER_BUF_T { unsigned int wrIdx; unsigned int rdIdx; unsigned char data[128]; } SER_BUF_T; extern unsigned long ser_txRestart; /* NZ if TX restart is required */ extern unsigned short ser_lineState; /* ((msr << 8) | (lsr)) */ extern volatile SER_BUF_T ser_out; /* Serial data buffers */ extern volatile SER_BUF_T ser_in; #define SER_BUF_SIZE (128) /* serial buffer in bytes (power 2) */ #define SER_BUF_MASK (SER_BUF_SIZE-1ul) /* buffer size mask */ /* Buffer read / write macros */ #define SER_BUF_RESET(serBuf) (serBuf.rdIdx = serBuf.wrIdx = 0) #define SER_BUF_WR(serBuf, dataIn) (serBuf.data[SER_BUF_MASK & serBuf.wrIdx++] = (dataIn)) #define SER_BUF_RD(serBuf, dataOut) serBuf.rdIdx++; dataOut = serBuf.data[SER_BUF_MASK & (serBuf.rdIdx-1)]; #define SER_BUF_EMPTY(serBuf) (serBuf.rdIdx == serBuf.wrIdx) #define SER_BUF_FULL(serBuf) ((SER_BUF_MASK & serBuf.rdIdx) == (SER_BUF_MASK & (serBuf.wrIdx+1))) #define SER_BUF_COUNT(serBuf) (SER_BUF_MASK & (serBuf.wrIdx - serBuf.rdIdx)) __task void writeTask (void) { SER_BUF_RESET(ser_in); while(1) { SER_BUF_WR(ser_in, 'a'); os_tsk_pass (); } } int main (void) { LED_Init(); LED_Val(0xFF); os_sys_init(init); /* Init RTX and start 'init' */ }
By the way I can see in USBlyzer characters ('a') which I want to sent to PC Host, just after I connect the device to USB.
Hi John, As you, I tried to convert a HID example into CDC with same problems that you run through. Did you find the solution? Thanks,
Hi Ezequiel Brizzio,
There is new version of RL library, but I did not manage to make it work (I tried once to port example). So I downloaded Freescale's USB stack, and using that solution.
Regards, John