I'm working with an LPC1768(FBD100) and need to connect to a PC through a serial USB. As a starting point, I used the sample package USB virtual com port.
Installed packages:
My code:
#include "includes.h" int main (void) { MenuEntryVType* MenuEntry; GUI_Init(); WM_SetCreateFlags(WM_CF_MEMDEV); FRAMEWIN_SetDefaultSkinClassic(); PROGBAR_SetDefaultSkinClassic(); SCROLLBAR_SetDefaultSkinClassic(); FRAMEWIN_SetDefaultFont(StdFont); InitOutputs(); InitADCs(); InitDAC(); InitPWM(100); InitI2C(); GetConfig(); osDelay(1500); // Works as expected ; new COMx device shows in dev manager. USBD_Initialize (0U); // USB Device 0 Initialization USBD_Connect (0U); // USB Device 0 Connect // menu handling code // actual functionality start servos, etc.) // does not get executed. Why? return 0; }
Why does USBD_Connect() hang? no code gets executed past that point; the lpc1768 does connect, however, to the PC:
rl_usb.h contains the definitions of USBD_Initialize() and USBD_Connect():
/// \brief Initialize USB Device stack and controller /// \param[in] device index of USB Device. /// \return status code that indicates the execution status of the function as defined with \ref usbStatus. extern usbStatus USBD_Initialize (uint8_t device); /// \brief Activate pull-up on D+ or D- line to signal USB Device connection on USB Bus /// \param[in] device index of USB Device. /// \return status code that indicates the execution status of the function as defined with \ref usbStatus. extern usbStatus USBD_Connect (uint8_t device);
These are function declarations; I don't know where the implementations are (likely in USB_CM3_L.lib).
Read/write functions are defined in USBD_User_CDC_ACM_UART_0.c
/*------------------------------------------------------------------------------ * MDK Middleware - Component ::USB:Device * Copyright (c) 2004-2019 ARM Germany GmbH. All rights reserved. *------------------------------------------------------------------------------ * Name: USBD_User_CDC_ACM_UART_0.c * Purpose: USB Device Communication Device Class (CDC) * Abstract Control Model (ACM) USB <-> UART Bridge User module * Rev.: V1.0.3 *----------------------------------------------------------------------------*/ /** * \addtogroup usbd_cdcFunctions * * USBD_User_CDC_ACM_UART_0.c implements the application specific * functionality of the CDC ACM class and is used to demonstrate a USB <-> UART * bridge. All data received on USB is transmitted on UART and all data * received on UART is transmitted on USB. * * Details of operation: * UART -> USB: * Initial reception on UART is started after the USB Host sets line coding * with SetLineCoding command. Having received a full UART buffer, any * new reception is restarted on the same buffer. Any data received on * the UART is sent over USB using the CDC0_ACM_UART_to_USB_Thread thread. * USB -> UART: * While the UART transmit is not busy, data transmission on the UART is * started in the USBD_CDC0_ACM_DataReceived callback as soon as data is * received on the USB. Further data received on USB is transmitted on * UART in the UART callback routine until there is no more data available. * In this case, the next UART transmit is restarted from the * USBD_CDC0_ACM_DataReceived callback as soon as new data is received * on the USB. * * The following constants in this module affect the module functionality: * * - UART_PORT: specifies UART Port * default value: 0 (=UART0) * - UART_BUFFER_SIZE: specifies UART data Buffer Size * default value: 512 * * Notes: * If the USB is slower than the UART, data can get lost. This may happen * when USB is pausing during data reception because of the USB Host being * too loaded with other tasks and not polling the Bulk IN Endpoint often * enough (up to 2 seconds of gap in polling Bulk IN Endpoint may occur). * This problem can be solved by using a large enough UART buffer to * compensate up to a few seconds of received UART data or by using UART * flow control. * If the device that receives the UART data (usually a PC) is too loaded * with other tasks it can also loose UART data. This problem can only be * solved by using UART flow control. * * This file has to be adapted in case of UART flow control usage. */ //! [code_USBD_User_CDC_ACM] #include <stdbool.h> #include "rl_usb.h" #if defined(RTE_CMSIS_RTOS2) #include "cmsis_os2.h" #if defined(RTE_CMSIS_RTOS2_RTX5) #include "rtx_os.h" #endif #endif #if defined(RTE_CMSIS_RTOS) #include "cmsis_os.h" #endif #include "Driver_USART.h" // UART Configuration ---------------------------------------------------------- #define UART_PORT 1 // UART Port number #define UART_BUFFER_SIZE 512 // UART Buffer Size //------------------------------------------------------------------------------ #define _UART_Driver_(n) Driver_USART##n #define UART_Driver_(n) _UART_Driver_(n) extern ARM_DRIVER_USART UART_Driver_(UART_PORT); #define ptrUART (&UART_Driver_(UART_PORT)) // External functions #ifdef USB_CMSIS_RTOS extern void CDC0_ACM_UART_to_USB_Thread (void const *arg) __attribute((noreturn)); #endif // Local Variables static uint8_t uart_rx_buf[UART_BUFFER_SIZE]; static uint8_t uart_tx_buf[UART_BUFFER_SIZE]; static volatile int32_t uart_rx_cnt = 0; static volatile int32_t usb_tx_cnt = 0; static void *cdc_acm_bridge_tid = 0U; static CDC_LINE_CODING cdc_acm_line_coding = { 0U, 0U, 0U, 0U }; // Called when UART has transmitted or received requested number of bytes. // \param[in] event UART event // - ARM_USART_EVENT_SEND_COMPLETE: all requested data was sent // - ARM_USART_EVENT_RECEIVE_COMPLETE: all requested data was received static void UART_Callback (uint32_t event) { int32_t cnt; if (event & ARM_USART_EVENT_SEND_COMPLETE) { // USB -> UART cnt = USBD_CDC_ACM_ReadData(0U, uart_tx_buf, UART_BUFFER_SIZE); if (cnt > 0) { ptrUART->Send(uart_tx_buf, (uint32_t)(cnt)); } } if (event & ARM_USART_EVENT_RECEIVE_COMPLETE) { // UART data received, restart new reception uart_rx_cnt += UART_BUFFER_SIZE; ptrUART->Receive(uart_rx_buf, UART_BUFFER_SIZE); } } // Thread: Sends data received on UART to USB // \param[in] arg not used. #ifdef USB_CMSIS_RTOS2 __NO_RETURN static void CDC0_ACM_UART_to_USB_Thread (void *arg) { #else __NO_RETURN void CDC0_ACM_UART_to_USB_Thread (void const *arg) { #endif int32_t cnt, cnt_to_wrap; (void)(arg); while (1) { // UART - > USB if (ptrUART->GetStatus().rx_busy != 0U) { cnt = uart_rx_cnt; cnt += ptrUART->GetRxCount(); cnt -= usb_tx_cnt; if (cnt >= UART_BUFFER_SIZE) { // Dump data received on UART if USB is not consuming fast enough usb_tx_cnt += cnt; cnt = 0U; } if (cnt > 0) { cnt_to_wrap = (int32_t)(UART_BUFFER_SIZE - ((uint32_t)usb_tx_cnt & (UART_BUFFER_SIZE - 1))); if (cnt > cnt_to_wrap) { cnt = cnt_to_wrap; } cnt = USBD_CDC_ACM_WriteData(0U, (uart_rx_buf + ((uint32_t)usb_tx_cnt & (UART_BUFFER_SIZE - 1))), cnt); if (cnt > 0) { usb_tx_cnt += cnt; } } } osDelay(10U); } } #ifdef USB_CMSIS_RTOS2 #ifdef USB_CMSIS_RTOS2_RTX5 static osRtxThread_t cdc0_acm_uart_to_usb_thread_cb_mem __SECTION(.bss.os.thread.cb); static uint64_t cdc0_acm_uart_to_usb_thread_stack_mem[512U / 8U] __SECTION(.bss.os.thread.stack); #endif static const osThreadAttr_t cdc0_acm_uart_to_usb_thread_attr = { "CDC0_ACM_UART_to_USB_Thread", 0U, #ifdef USB_CMSIS_RTOS2_RTX5 &cdc0_acm_uart_to_usb_thread_cb_mem, sizeof(osRtxThread_t), &cdc0_acm_uart_to_usb_thread_stack_mem[0], #else NULL, 0U, NULL, #endif 512U, osPriorityNormal, 0U, 0U }; #else extern const osThreadDef_t os_thread_def_CDC0_ACM_UART_to_USB_Thread; osThreadDef (CDC0_ACM_UART_to_USB_Thread, osPriorityNormal, 1U, 0U); #endif // CDC ACM Callbacks ----------------------------------------------------------- // Called when new data was received from the USB Host. // \param[in] len number of bytes available to read. void USBD_CDC0_ACM_DataReceived (uint32_t len) { int32_t cnt; (void)(len); if (ptrUART->GetStatus().tx_busy == 0U) { // Start USB -> UART cnt = USBD_CDC_ACM_ReadData(0U, uart_tx_buf, UART_BUFFER_SIZE); if (cnt > 0) { ptrUART->Send(uart_tx_buf, (uint32_t)(cnt)); } } } // Called during USBD_Initialize to initialize the USB CDC class instance (ACM). void USBD_CDC0_ACM_Initialize (void) { ptrUART->Initialize (UART_Callback); ptrUART->PowerControl (ARM_POWER_FULL); #ifdef USB_CMSIS_RTOS2 cdc_acm_bridge_tid = osThreadNew (CDC0_ACM_UART_to_USB_Thread, NULL, &cdc0_acm_uart_to_usb_thread_attr); #else cdc_acm_bridge_tid = osThreadCreate (osThread (CDC0_ACM_UART_to_USB_Thread), NULL); #endif } // Called during USBD_Uninitialize to de-initialize the USB CDC class instance (ACM). void USBD_CDC0_ACM_Uninitialize (void) { if (osThreadTerminate (cdc_acm_bridge_tid) == osOK) { cdc_acm_bridge_tid = NULL; } ptrUART->Control (ARM_USART_ABORT_RECEIVE, 0U); ptrUART->PowerControl (ARM_POWER_OFF); ptrUART->Uninitialize (); } // Called upon USB Bus Reset Event. void USBD_CDC0_ACM_Reset (void) { ptrUART->Control (ARM_USART_ABORT_SEND, 0U); ptrUART->Control (ARM_USART_ABORT_RECEIVE, 0U); } // Called upon USB Host request to change communication settings. // \param[in] line_coding pointer to CDC_LINE_CODING structure. // \return true set line coding request processed. // \return false set line coding request not supported or not processed. bool USBD_CDC0_ACM_SetLineCoding (const CDC_LINE_CODING *line_coding) { uint32_t data_bits = 0U, parity = 0U, stop_bits = 0U; int32_t status; ptrUART->Control (ARM_USART_ABORT_SEND, 0U); ptrUART->Control (ARM_USART_ABORT_RECEIVE, 0U); ptrUART->Control (ARM_USART_CONTROL_TX, 0U); ptrUART->Control (ARM_USART_CONTROL_RX, 0U); switch (line_coding->bCharFormat) { case 0: // 1 Stop bit stop_bits = ARM_USART_STOP_BITS_1; break; case 1: // 1.5 Stop bits stop_bits = ARM_USART_STOP_BITS_1_5; break; case 2: // 2 Stop bits stop_bits = ARM_USART_STOP_BITS_2; } switch (line_coding->bParityType) { case 0: // None parity = ARM_USART_PARITY_NONE; break; case 1: // Odd parity = ARM_USART_PARITY_ODD; break; case 2: // Even parity = ARM_USART_PARITY_EVEN; break; default: return false; } switch (line_coding->bDataBits) { case 5: data_bits = ARM_USART_DATA_BITS_5; break; case 6: data_bits = ARM_USART_DATA_BITS_6; break; case 7: data_bits = ARM_USART_DATA_BITS_7; break; case 8: data_bits = ARM_USART_DATA_BITS_8; break; default: return false; } status = ptrUART->Control(ARM_USART_MODE_ASYNCHRONOUS | data_bits | parity | stop_bits | ARM_USART_FLOW_CONTROL_NONE , line_coding->dwDTERate ); if (status != ARM_DRIVER_OK) { return false; } // Store requested settings to local variable cdc_acm_line_coding = *line_coding; uart_rx_cnt = 0; usb_tx_cnt = 0; ptrUART->Control (ARM_USART_CONTROL_TX, 1U); ptrUART->Control (ARM_USART_CONTROL_RX, 1U); ptrUART->Receive (uart_rx_buf, UART_BUFFER_SIZE); return true; } // Called upon USB Host request to retrieve communication settings. // \param[out] line_coding pointer to CDC_LINE_CODING structure. // \return true get line coding request processed. // \return false get line coding request not supported or not processed. bool USBD_CDC0_ACM_GetLineCoding (CDC_LINE_CODING *line_coding) { // Load settings from ones stored on USBD_CDC0_ACM_SetLineCoding callback *line_coding = cdc_acm_line_coding; return true; } // Called upon USB Host request to set control line states. // \param [in] state control line settings bitmap. // - bit 0: DTR state // - bit 1: RTS state // \return true set control line state request processed. // \return false set control line state request not supported or not processed. bool USBD_CDC0_ACM_SetControlLineState (uint16_t state) { // Add code for set control line state (void)(state); return true; } //! [code_USBD_User_CDC_ACM]
Is it possible to run the lpc1768 as a USB listener and also perform a set of tasks in parallel? If yes, how should I go about it?
>> you're using RTOS1?
Yes.
>> I chose to use a thread to manage the USB interface and communications, so 'yes' to the question of being a listener and doing other things in >> parallel.
In my case, I can either launch all application threads as defined below
// Stack requirements #define UIThreadStack 1024 #define KeyboardThreadStack 1024 // Total stack size: 2048 bytes extern void UIThread(void const *argument); osThreadDef(UITask, osPriorityNormal, 1, UIThreadStack); extern void KeyboardThread(void const *argument); osThreadDef(KeyboardTask, osPriorityNormal, 1, KeyboardThreadStack); extern void BThread(void const *argument); osThreadDef(BTask, osPriorityNormal, 1, 0); // default size extern void CThread(void const *argument); osThreadDef(CTask, osPriorityNormal, 1, 0); // default size
or just connect as a USB device;
This code creates a serial device but fails to launch the other threads:
int main() { // Init and connect to USB 0 USBD_Initialize(0U); USBD_Connect(0U); // No threads are created past this point tid_UIThread = osThreadCreate(osThread(UITask), NULL); tid_KeyboardThread = osThreadCreate(osThread(KeyboardTask), NULL); tid_BThread = osThreadCreate(osThread(BTask), NULL); tid_CThread = osThreadCreate(osThread(CTask), NULL); for(;;) { } return 0; }
Reversing the order, this code creates the application threads; the application runs fine except that no serial port is created:
int main() { tid_UIThread = osThreadCreate(osThread(UITask), NULL); tid_KeyboardThread = osThreadCreate(osThread(KeyboardTask), NULL); tid_BThread = osThreadCreate(osThread(BTask), NULL); tid_CThread = osThreadCreate(osThread(CTask), NULL); //Everything's fine except that no serial port is created USBD_Initialize(0U); USBD_Connect(0U); for(;;) { } return 0; }
>> When the USB manager thread launches, it first calls USBD_Initialize() and then calls USBD_Connect. Possibly due to a quirk of our board, >> we have to manually set the VC bit in the OTGSC register after connect() completes.
I still have to try this. Do you know where it's mentioned in the docs/datasheet?
>> Even if 'nothing executes' after USBD_Connect(), the core must be doing something. Where it ends up can tell you a lot about what went >>wrong. Hard fault handler? At the exit infinite loop?
I meant to say no threads are executed after USBD_Connect(); I tried displaying a string and it worked, There are no problems in the USB related calls.
>>The System and Thread Viewer will help find issues with lack of memory causing dynamic resources to fail (e.g., threads) or if a thread has >>stalled out.
Debugging is somewhat difficult since I'm working remotely and am only running the firmware on a local device (development's on a remote workstation).
>>You're creating a thread inside your USBD_CDC0_ACM_Initialize() function (hopefully after osKernelInitialize() has been called). That thread >>could cause problems if it tries to push data over USB before USBD_Connect() completes. Have you tried creating that >>thread after USBD_Connect()? Or using USBD_Configured() to only use it once configuration is complete?
Strangely that function never gets called; I deleted the file with the implementation of USBD_CDC0_ACM_Initialize() and the project still builds and works as before. The USB API seems to be separate from that file.
>>You're bridging USB to serial -- do you get past USB_Connect() if you disable (comment out, etc.) everything to do with the UART >>initialization and setup?
Yes. All non-thread related code executes as expected.
>>The USB thread stack sizes are set in USBD_Config*.c (core thread) and USBD_Config_CDC*.h (endpoint threads). I used 512 bytes for >>each and made sure that the RTOS had a lot of dynamic memory (RTX_Config.h) for the thread stacks, queues, and timers that I use. I also >>allocated 1024 bytes each to the send and receive buffers in USBD_Config_CDC*.h. (Really overkill for the 64-byte packets I was using!)
I played around with various sizes (put 1536 bytes for the USB thread) and it still failed. What the stack/heap layout? max size?
>>I can't remember if it's a requirement, but you should probably call USBD_CDC_ACM_Notify_ResponseAvailable() after the >>USBD_CDC_ACM_WriteData succeeds, to notify the host to collect the data. I found that USBD_CDC_ACM_WriteData can fail occasionally >>(busy), so a little more code to handle retries was worth it.
Still have to try this.
>>Finally, I recommend getting rid of the osDelay(10) in your CDC thread and the direct call to USB CDC Write in the UART callback, instead >>switching to a model that uses events to signal when work needs to be done and queues to buffer data while waiting
Right now, the USB code has been trimmed down to:
USBD_Initialize(0U); USBD_Connect(0U);
It sounds like the IDE is running on a different PC than the one the board is connected to (how do you download images to the board?). If so, have you considered either contacting ARM to move the license the PC next to the board (it's faster than you might expect), or using a USB-over-Ethernet solution like VirtualHere to allow the remote IDE to use the local debugger?
The project will still build and work as before if your local USBD_CDC0_ACM_Initialize() is deleted, because there's already a definition in the USB library -- it's just weakly linked and your local version overrides it.
The USB threads don't need more than 512 bytes, just not less. (The default RTOS1 stack allocation is 200 bytes unless/until you change that in RTX_Conf_CM.c .)
I see now that your example code is really the code from the USB help/manual pages. I believe that ARM test their code before including the fragments in the help!
My project targets an LPC1857, not an LPC1769. The OTG registers are slightly different, but the VBUS charge setting is discussed in Chapter 13 of the LPC1769 manual (with examples).
I really think the key is to find out what the CPU is doing (not doing) after Connect.
Alright, I've managed to create the USB and it's working as expected, except that I now no longer have any stack space left to create the remaining threads. Newly defined threads are:
#define UIStack 1024 #define KeyboardStack 1024 #define USBStack 1526 // Gives error code 34 upon USBD_Connect when using 2048 stack size; 512/1024 results in black screen // 1526, 2048 -- USBD init returns 16 usbThreadError, USBD connect 34 usbDriverError. 3072 jumps directly out of the usb init function // 2560, 3072 black screen USBD init. The thread is created successfully when using the default size ... It shouldn't extern void UITask (void const *argument); //osThreadDef(UITask, osPriorityNormal, 1, UIStack); osThreadDef(UITask, osPriorityNormal, 1, 0); // <= defaults to 512 stack size works; fails if manually set to 512 which is the // current default size extern void KBTask (void const *argument); osThreadDef(KBTask, osPriorityNormal, 1, KeyboardStack); // <= fails // osThreadDef(KBTask, osPriorityNormal, 1, 200); extern void USBTask (void const *argument); // osThreadDef(USBTask, osPriorityAboveNormal, 1, USBTaskStack); // <= fails if non default stack size is specified even if it's set to the defaulted expected value osThreadDef(USBTask, osPriorityAboveNormal, 1, 0); // <= works with 512 stack size; extern void BTask (void const *argument); osThreadDef(BTask, osPriorityNormal, 1, 0); // fails regardless whether default or specified stack size are provided; stack limit reached seemingly after creating // USB and UI threads extern void CTask (void const *argument); osThreadDef(CTask, osPriorityNormal, 1, 0); // fails regardless whether default or specified stack size are provided; stack limit reached seemingly after creating // USB and UI threads
RTX_Conf_CM.c
/*---------------------------------------------------------------------------- * RTX User configuration part BEGIN *---------------------------------------------------------------------------*/ //-------- <<< Use Configuration Wizard in Context Menu >>> ----------------- // // <h>Thread Configuration // ======================= // // <o>Number of concurrent running user threads <1-250> // <i> Defines max. number of user threads that will run at the same time. // <i> Default: 6; initially 5 #ifndef OS_TASKCNT #define OS_TASKCNT 6 #endif
I'm not sure if my understanding is correct, but does OS_TASKCNT represent the number of all threads?
If yes, then there are 5 application threads defined above, 1 main thread and the threads created by the USB module.
The number of threads needed by usb are defined here:
http://www.keil.com/pack/doc/mw/USB/html/usb_resource_requirements.html#usbd_res_req
I;m using CDC (Virtual COM port emulation) which needs two threads bringing the total number of threads to:
5 (application threads including the thread that launches the USB module) + 1 main thread (that launches the previous threads) + 2 (USB threads) = 8.
Is this correct? Does arm have a preallocated number of threads that after which is exhausted, no further threads can be created?
What is the maximum stack size? I also can't seem to find where the heap is configured.
Moving to OS_STKSIZE:
// <o>Default Thread stack size [bytes] <64-4096:8><#/4> // <i> Defines default stack size for threads with osThreadDef stacksz = 0 // <i> Default: 200; initially 50 #ifndef OS_STKSIZE #define OS_STKSIZE 512 // this stack size value is in words #endif
this is technically clear; if 0 is specified as the default stack for any user defined thread, then this stack size is used. Seems clear ...
OS_MAINSTKSIZE:
// <o>Main Thread stack size [bytes] <64-32768:8><#/4> // <i> Defines stack size for main thread. // <i> Default: 200; initially 512 #ifndef OS_MAINSTKSIZE #define OS_MAINSTKSIZE 512 // this stack size value is in words #endif
This should also be clear; one question (absurd) still, can the main thread create threads with a larger stack size?
OS_PRIVCNT:
// <o>Number of threads with user-provided stack size <0-250> // <i> Defines the number of threads with user-provided stack size. // <i> Default: 0; initially 2; doesn't work 1, 2, 3 all the same #ifndef OS_PRIVCNT #define OS_PRIVCNT 1 #endif
This one's beyond me. Is a default stack size still a user provided stack size?
Changing this value does not seem to have an impact on how many threads get created. Tried 1, 2, 3 with the same outcome (no other changes to the code except this value). My initial assumption was that, this number refers to non default sized threads defined in osThreadDef(). Not sure if it's right.
OS_PRIVSTKSIZE:
// <o>Total stack size [bytes] for threads with user-provided stack size <0-1048576:8><#/4> // <i> Defines the combined stack size for threads with user-provided stack size. // <i> Default: 0; initially 512 #ifndef OS_PRIVSTKSIZE #define OS_PRIVSTKSIZE 512 // this stack size value is in words #endif
Confused about this one too. Should this value be the sum of all non-default user defined thread stack sizes?
How much stack space does the lpc1768 actually have?
I've managed to get it to run using these settings:
/*---------------------------------------------------------------------------- * RTX User configuration part BEGIN *---------------------------------------------------------------------------*/ //-------- <<< Use Configuration Wizard in Context Menu >>> ----------------- // // <h>Thread Configuration // ======================= // // <o>Number of concurrent running user threads <1-250> // <i> Defines max. number of user threads that will run at the same time. // <i> Default: 6; initially 5 #ifndef OS_TASKCNT #define OS_TASKCNT 12 #endif // <o>Default Thread stack size [bytes] <64-4096:8><#/4> // <i> Defines default stack size for threads with osThreadDef stacksz = 0 // <i> Default: 200; initially 50 #ifndef OS_STKSIZE #define OS_STKSIZE 512 // this stack size value is in words #endif // <o>Main Thread stack size [bytes] <64-32768:8><#/4> // <i> Defines stack size for main thread. // <i> Default: 200; initially 512 #ifndef OS_MAINSTKSIZE #define OS_MAINSTKSIZE 512 // this stack size value is in words #endif // <o>Number of threads with user-provided stack size <0-250> // <i> Defines the number of threads with user-provided stack size. // <i> Default: 0; initially 2; doesn't work 1, 2, 3 all the same #ifndef OS_PRIVCNT #define OS_PRIVCNT 0 #endif // <o>Total stack size [bytes] for threads with user-provided stack size <0-1048576:8><#/4> // <i> Defines the combined stack size for threads with user-provided stack size. // <i> Default: 0; initially 512 #ifndef OS_PRIVSTKSIZE #define OS_PRIVSTKSIZE 2048 // this stack size value is in words #endif // <q>Stack overflow checking // <i> Enable stack overflow checks at thread switch. // <i> Enabling this option increases slightly the execution time of a thread switch. #ifndef OS_STKCHECK #define OS_STKCHECK 1 #endif
The line that made it work is:
#define OS_PRIVSTKSIZE 2048 // this stack size value is in words
It was mostly trial and error; the documentation is misleading and potentially unclear:
http://www.keil.com/support/man/docs/rlarm/rlarm_ar_cfgtask.htm
http://www.keil.com/support/man/docs/rlarm/rlarm_ar_cfgstack.htm
There is no clear distinction between a thread with a default stack and one with a user defined stack. The OS stackspace can be smaller than that of a thread which is somewhat couterintuitive since the thread is managed by the OS.
Oh well ...
Have you ever managed to read USB traffic from C#? Right now I'm stuck at https://stackoverflow.com/questions/63114656/serialport-fails-to-connect-to-mcu-lpc1768
Yes, my C# project used USB VCOM serial to talk to the LPC1857 MCU. It worked faster and more reliably than using HID.
I used a System.Management.ManagementEventWatcher to watch for the event when the MCU was connected to the host PC. Then a ManagementObjectSearcher query gave me the COM port matching the PID/VID of my device. Then it was just a matter of setting the SerialPort configuration and calling Open(). The configuration I used looks very similar to your code:
m_serial_port = new SerialPort(); m_serial_port.PortName = portname; m_serial_port.ReadTimeout = 500; m_serial_port.WriteTimeout = 500; m_serial_port.BaudRate = 115200; m_serial_port.Parity = Parity.None; m_serial_port.Handshake = Handshake.None; m_serial_port.DataBits = 8; m_serial_port.DtrEnable = true;
Note that I'm only setting DtrEnable here, but I recall it was critical to getting data moving. If there's an exception on the serial port open, I call Dispose() on the serial port, not Close().
I didn't use the SerialPort.DataReceived method -- I wrapped SerialPort.BaseStream.ReadAsync() with an async Task instead.
On the MCU side, changing the line settings should result in calls to USBD_CDC0_ACM_SetLineCoding() with the parameters you've chosen.
After the data was successfully written using USBD_CDC_ACM_WriteData(), I call USBD_CDC_ACM_Notify_ResponseAvailable() to let the host PC know the data is ready for pickup. Sometimes the write fails on the MCU side (resource busy; waiting for the host, I guess), so a retry was needed.
About the OS stackspace: the 'bare metal' stack space (defined in the startup.s file) is distinct from the thread stack space (defined in RTX_CM_lib.h). OS_PRIVSTKSIZE is in bytes, not words. RTOS1 is confusing, which is one of the reasons I much prefer RTOS2. You need four user-defined stacks for USB CDC: one for the CDC thread you've defined in ACM_Initialize(), and one each for USB Bulk, USB Core, and USB Interrupt endpoints. These threads are created during USBD_Initialize(), and the debugger makes seeing this easy. Are you able to debug locally?
Adam Lins said:About the OS stackspace: the 'bare metal' stack space (defined in the startup.s file) is distinct from the thread stack space (defined in RTX_CM_lib.h). OS_PRIVSTKSIZE is in bytes, not words. RTOS1 is confusing, which is one of the reasons I much prefer RTOS2. You need four user-defined stacks for USB CDC: one for the CDC thread you've defined in ACM_Initialize(), and one each for USB Bulk, USB Core, and USB Interrupt endpoints. These threads are created during USBD_Initialize(), and the debugger makes seeing this easy. Are you able to debug locally?
Thanks! That cleared it up; I'm still learning :P . I've managed to get the lpc to connect to PC USB host and send a byte stream. Unfortunately, I cannot debug locally (I build the project on a remote machine (keil licensing ...)); I can only display error codes and messages on an LCD. I've used SerialPortStream to read data directly from the lpc1768:
https://stackoverflow.com/questions/63114656/serialport-fails-to-connect-to-mcu-lpc1768
I have to make the lpc1768 talk (listen initially) to a dspic30f6013a over VirtualCOM (over USB). Right now both the lpc and dspic can talk to a PC host over serial USB. I'm trying to get the lpc to run into host mode (so that it can directly listen to the dspic) but there seems to be no host component in the USB components' menu:
These are the components of the USB Host Keyboard sample:
In my project the USB host component is missing altogether:
Should I try removing the USB device components? Shouldn't the lpc be able to switch between device and host mode?
MDK-Pro is required to enable/provide RTE Manager support for USB hosts. If you change the value of the USB drop-down (partially covered by the arrow in your last screenshot) to MDK-Pro, the Host component will appear. If you only have MDK-Plus then I imagine the path to a functional USB Host is much harder, especially with your licensing arrangement.
The LPC1759 USB controller supports both device and host modes, although I don't know how much work has to be done to effect the switch at run-time. If you have MDK-Pro it could be as easy as calling USBx_Initialize(), etc. It's not clear how you'd connect to the DSPIC30F6013A over USB when (per Microchip) that part doesn't have a USB port.
I still think it's worth pursuing either a change to the license to allow local debug, or getting USB virtualization software like VirtualHere to enable debug from the machine with the MDK installed.
USBH_CDC_ACM_GetStatus crashes at some point.
Adam Lins said:MDK-Pro is required to enable/provide RTE Manager support for USB hosts. If you change the value of the USB drop-down (partially covered by the arrow in your last screenshot) to MDK-Pro, the Host component will appear. If you only have MDK-Plus then I imagine the path to a functional USB Host is much harder, especially with your licensing arrangement.
Thanks! I do have MDK Pro on a remote machine. I was unable to get the lpc to switch from device to host;
Adam Lins said:The LPC1759 USB controller supports both device and host modes, although I don't know how much work has to be done to effect the switch at run-time. If you have MDK-Pro it could be as easy as calling USBx_Initialize(), etc. It's not clear how you'd connect to the DSPIC30F6013A over USB when (per Microchip) that part doesn't have a USB port.
dspic30f6013a only does UART over serial.
Adam Lins said:I still think it's worth pursuing either a change to the license to allow local debug, or getting USB virtualization software like VirtualHere to enable debug from the machine with the MDK installed.
I'll try to debug remotely through VirtualHere ...