CH32V203 + FreeRTOS + USB CDC - Page 1
Forum section: electronics / general

author: ali_asadzadeh
author_rank: Super Contributor
timestamp: December 31, 2024, 03:24:42 pm
content: Hi,In my quest to be able to have FreeRTOS + USB CDC I got so many problems and here How I'm going there,I created a new project in MRS for CH32V203 with FreeRTOS support.Then I enable timer 3 for free running to enable Delay_Us in debug.c modulethe code for the time is like thisCode://enable us delayRCC_APB1PeriphClockCmd( RCC_APB1Periph_TIM3, ENABLE );TIM_TimeBaseInitTypeDef myTim3 ={0};myTim3.TIM_Prescaler = SystemCoreClock / 1000000 - 1;myTim3.TIM_Period = 0xFFFF;TIM_TimeBaseInit( TIM3, &myTim3);TIM_Cmd(TIM3, ENABLE);TIM_SetCounter(TIM3,0);Code:void Delay_Us(uint32_t n){TIM3->CNT = 0;while(TIM3->CNT <n);}and modify the Delay_Ms in debug.c to use vTaskDelaythen I add the USBLIB and UART files from the Simulate CDC example from ch32v20x\EVT\EXAM\USB\USBD\SimulateCDC github repo to the projectthen I add these codes to the task2_task functionCode:RCC_Configuration( );TIM2_Init( );UART2_Init( 1, DEF_UARTx_BAUDRATE, DEF_UARTx_STOPBIT, DEF_UARTx_PARITY );Set_USBConfig();USB_Init();USB_Interrupts_Config();while(1){UART2_DataRx_Deal( );UART2_DataTx_Deal( );vTaskDelay(1);}then I add void TIM2_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast"))); to the ch32v20x_it.c fileand add these codes thereCode:/********************************************************************** @fn TIM2_IRQHandler** @brief This function handles TIM2 exception.** @return none*/void TIM2_IRQHandler( void ){GET_INT_SP();portDISABLE_INTERRUPTS();/* uart timeout counts */Uart.Rx_TimeOut++;Uart.USB_Up_TimeOut++;/* clear status */TIM2->INTFR = (uint16_t)~TIM_IT_Update;portENABLE_INTERRUPTS();FREE_INT_SP();}It does not work, I will get constant crashes and I noticed something maybe would change SysTick->CMP value, you can take a look at the modified code, any Ideas on what am I doing wrong?

author: peter-h
author_rank: Super Contributor
timestamp: January 01, 2025, 09:43:23 am
content: IME, FreeRTOS is very straightforward to get going and to use.Make sure this is solid first. There is an issue with Systick. From my project notes:INTERRUPTSInterrupt priority is normally set with HAL_NVIC_SetPriority().Note that interrupts are not disabled with portENTER_CRITICAL(). That just stops RTOS task switching. If you really want a traditional DI/EI, use CPSID / CPSIE assembler.SysTick runs at 1kHz and drives the RTOS.The Systick interrupt is set up in the RTOS code in vPortSetupTimerInterrupt(). Frequency and priority are set by configTICK_RATE_HZ (1kHz), and configLIBRARY_LOWEST_INTERRUPT_PRIORITY.The handler for the Systick interrupt is xPortSysTickHandler().TIM6 runs at 1kHz and drives the 1kHz tick which is used for the various timers outside of the RTOS.The reason why SysTick could not be used for both above is due to an interrupt priority issue and this solution was recommended by FreeRTOS. See herehttps://www.digikey.com/en/maker/projects/getting-started-with-stm32-introduction-to-freertos/ad275395687e4d85935351e16ec575b1and herehttps://community.st.com/s/question/0D50X0000A4nQxpSQE/when-freertos-is-used-it-is-strongly-recommended-to-use-a-hal-time-base-source-other-than-the-systick-In general, because tasks are scheduled from the SysTick interrupt at a low priority which may call HAL functions that use delays and timeouts which need to come from a higher priority timer interrupt.The RTOS sets SysTick priority to the lowest (15).The priority of TIM6 interrupt is set when calling InitTIM6Tick() (in main.c) and currently is set to 1. This is a high priority.USB is entirely interrupt driven, with foreground code doing initialisation and VCP debugs.USB CDC (VCP) is immensely complicated. Do a search here; I posted various threads on USB in the past. Also ST USB code is buggy.

author: ali_asadzadeh
author_rank: Super Contributor
timestamp: January 02, 2025, 11:40:49 am
content: peter-h thanks for the feedback, I'm trying to port FreeRTOS to use another timer and see if it can coexist with the CDC example.

author: peter-h
author_rank: Super Contributor
timestamp: January 02, 2025, 12:31:34 pm
content: I suggest creating some RTOS tasks and make sure they run fine, use timers, etc, before diving into USB which is a massive chunk of code and so complicated that almost nobody understands it, and it is very hard to debug without a deep understanding of USB.

author: ali_asadzadeh
author_rank: Super Contributor
timestamp: January 04, 2025, 09:13:19 am
content: I managed to port it, and I found the problem, we should convert every __attribute__((interrupt("WCH-Interrupt-fast"))) to __attribute__((interrupt())) so It would work,why is that?Also the next step is to remove the Actual physical UART bulshit and make it send and receive data in sram buffers, any Hints or ideas?

author: brucehoult
author_rank: Super Contributor
timestamp: January 04, 2025, 10:52:49 am
content: Quote from: ali_asadzadeh on January 04, 2025, 09:13:19 amBecause you didn't use WCH's modified compiler which adds their custom interrupt attribute.It's possible to use it with a standard compiler with a bit more work, discussed in other posts here.

author: ali_asadzadeh
author_rank: Super Contributor
timestamp: January 04, 2025, 12:49:35 pm
content: thanks brucehoult, I'm using MounRiver, where can I find the thread and what should I do?Also I could manage to send and received data in sram buffers, using Termite terminal to test it, But when I use brays terminal, it does not work and the terminal program would crash, I have investigated it and noticed that the brays terminal would send data 1 bye in a single endpoint, and when I try to input single characters into the termite, the Receive endpoint would not get the second character!!! I'm totally confused and out of Ideas,Here are the modified parts of the codeI defined these in the main.cCode:#define BUFFER_SIZE 64uint8_t UART_Rx_Buf[ 1024 ]; /* Serial port 2 receive data buffer */uint8_t UART_Tx_Buf[ 1024 ]; /* Serial port 2 transmit data buffer */uint16_t txBufferIndex = 0;uint16_t rxBufferIndex = 0;extern uint8_t USBD_Endp3_Busy;I modified the usb_endp.c for EP2_OUT_Callback like thisCode:void EP2_OUT_Callback (void){uint32_t len;len = GetEPRxCount(EP2_OUT & 0x7F);PMAToUserBufferCopy(UART_Rx_Buf + rxBufferIndex, GetEPRxAddr(EP2_OUT & 0x7F), len);rxBufferIndex += len;SetEPRxValid(ENDP2);}this is the send function which is not working for more than 64 bytes for now and I do not know what has caused itCode:void send_data(uint8_t *buffer, uint16_t length){while (length > 0) {if (!USBD_Endp3_Busy) {uint16_t len = (length > BUFFER_SIZE) ? BUFFER_SIZE : length;USBD_ENDPx_DataUp(ENDP3, buffer, len);buffer += len;length -= len;}}}and this is the task2 body that would receive and answer some strings for testingCode:void task2_task(void *pvParameters){RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA, ENABLE );RCC_APB1PeriphClockCmd( RCC_APB1Periph_USART2, ENABLE );RCC_APB1PeriphClockCmd( RCC_APB1Periph_TIM2, ENABLE );RCC_AHBPeriphClockCmd( RCC_AHBPeriph_DMA1, ENABLE );Set_USBConfig();USB_Init();USB_Interrupts_Config();// Wait for USB to be configuredwhile (bDeviceState != CONFIGURED) {vTaskDelay(10);}while(1){// Send data if availableif (txBufferIndex > 0) {send_data(UART_Tx_Buf, txBufferIndex);txBufferIndex = 0;}// Receive data//if (rxBufferIndex > 0) {if(UART_Rx_Buf =='N' && UART_Rx_Buf== 'A' && UART_Rx_Buf=='M'){// Process received data// Clear the buffer after processingmemset(UART_Rx_Buf, 0, sizeof(UART_Rx_Buf));rxBufferIndex = 0;// Copy received data to transmit buffermemcpy(UART_Tx_Buf, "my name is ASiDeigner hello how are you? what do you do with this code and what's the plan? are you sure it's working? have you seen seasnon 2", 60);txBufferIndex = 60;}else if(rxBufferIndex>10){// Clear the buffer after processingmemset(UART_Rx_Buf, 0, sizeof(UART_Rx_Buf));rxBufferIndex = 0;memcpy(UART_Rx_Buf, "Error", 5);txBufferIndex = 5;}vTaskDelay(1); // Delay to avoid flooding the terminal}}

author: brucehoult
author_rank: Super Contributor
timestamp: January 04, 2025, 12:52:06 pm
content: Quote from: ali_asadzadeh on January 04, 2025, 12:49:35 pmOh! In that case__attribute__((interrupt("WCH-Interrupt-fast")))should compile ok.

author: ali_asadzadeh
author_rank: Super Contributor
timestamp: January 05, 2025, 11:07:32 am
content: QuoteIt compiles ok, But if it's used with freeRTOS, I would get constant crashes, do you have any clue why?

author: brucehoult
author_rank: Super Contributor
timestamp: January 05, 2025, 12:18:00 pm
content: Quote from: ali_asadzadeh on January 05, 2025, 11:07:32 amI don't know what freeRTOS does -- or your code. If you are returning from the interrupt as normally then there should be no problem. If you are trying to do some fancy task switching from an interrupt handler by e.g. switching the stack pointer before returning then it stands to reason that things would be mucked up because half of your registers are stored in internal shadow registers in the CPU, not on the stack.I would expect everything to work fine on the CH32V003 because it does the hardware register saving on the normal stack, but the 203 and above have an internal stack not accessible to the user.

author: SiliconWizard
author_rank: Super Contributor
timestamp: January 05, 2025, 11:21:41 pm
content: I have written a small "RTOS" and adapted it to the CH32V307. The "hardware stack" was enabled. I didn't notice any issue saving and restoring all registers on context switching and I do the context switching from within an interrupt. I'm curious what exact sequence FreeRTOS could use that would explain a problem here, or why I didn't encounter any.

author: ali_asadzadeh
author_rank: Super Contributor
timestamp: January 06, 2025, 11:51:24 am
content: I have attached the project till this point, it works if I only send more than 1 character and also use termite program, I did not done anything fancy in interrupts, you can take a look and if you have CH32V203 test it too.

author: westfw
author_rank: Super Contributor
timestamp: January 07, 2025, 10:02:51 am
content: QuoteIs your freeRTOS also compiled with the WCH compiler, or was it provided with pre-built library binaries?

author: ali_asadzadeh
author_rank: Super Contributor
timestamp: January 07, 2025, 10:18:25 am
content: QuoteYes it's shipped with wch compiler, I only changed it's timer

author: ali_asadzadeh
author_rank: Super Contributor
timestamp: January 09, 2025, 01:36:36 pm
content: Guys do you have any idea what might be wrong for 1 character? also I wonder if General GPIO's are 5v tolerant, I could not find any info in the datasheet.

author: TomS_
author_rank: Frequent Contributor
timestamp: January 11, 2025, 09:33:56 am
content: In my experience with FreeRTOS, crashes are due to improper handling of interrupts.The tick interrupt must be the lowest priority of all interrupts, and you must not call any FreeRTOS APIs from interrupts above the maximum system interrupt priority.You could also try enabling stack monitoring to ensure your aren't overflowing any task stack and blowing away something critical, like an adjacent tasks TCB.As long as interrupts are handled correctly, and the stack is not being corrupted, there's generally no reason for FreeRTOS to crash.