1
完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
一.在stm32f10x上的移植(vet6)
1.源文件的获取 2.源文件的拷贝和理解 1. FreeRTOS系统层源代码
port.c文件中,有3个freeRTOS的Exception handlers函数需要被设置为相应的中断入口 1.xPortPendSVHandler 2.xPortSysTickHandler 3.vPortSVCHandler 这三个函数是连接硬件设备的重要桥梁,需要在statrtcode将相应中断入口中重新定向至这三个函数 *****1和3是由内联汇编编写的函数,主要用于上下文切换,使用汇编能大大提高切换速率,减少切换过程中关闭中断的时间,增强系统的稳定性 4.freeRTOSConfig.h的编写或修改 添加Demo中的freeRTOSConfig.h后,编译会发现xTaskGetCurrentTaskHandle() 没有被定义,导致编译无法通过。这个函数的主要作用是获取当前任务的句柄,报错的地方出现在stream_buffer.c中。这时候转到定义,发现有一个条件宏 #if ( ( INCLUDE_xTaskGetCurrentTaskHandle == 1 ) || ( configUSE_MUTEXES == 1 ) ) TaskHandle_t xTaskGetCurrentTaskHandle( void ) { TaskHandle_t xReturn; /* A critical section is not required as this is not called from * an interrupt and the current TCB will always be the same for any * individual execution thread. */ xReturn = pxCurrentTCB; return xReturn; } #endif 原因是 INCLUDE_xTaskGetCurrentTaskHandle 和 configUSE_MUTEXES 都没有被定义导致函数没有定义,只要在freeRTOSConfig.h前加入相关宏定义即可编译通过 5.freeRTOS移植测试 做一个简单的bsp(板级支持包),初始化一个led或者串口 #include "stm32f10x.h" #include "FreeRTOS.h" #include "task.h" #include "bsp_led.h" static void LED_TestApp(void*ptr); //声明任务函数 static TaskHandle_t LED_Handle = NULL; //任务句柄 int main(void) { NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4); //任务分组 BSP_LED_Init(); //LED初始化 //创建一个led闪烁的任务 xTaskCreate((TaskFunction_t )LED_TestApp, (const char* )"LED_Test", (uint16_t )256 , (void* )NULL, (UBaseType_t )1, (TaskHandle_t* )&LED_Handle); vTaskStartScheduler(); //开启任务调度 while(1); } void LED_TestApp(void*ptr) //移植成功验证任务 { while(1) { BSP_LED_CTR(0,LED_Off); vTaskDelay(500/portTICK_RATE_MS); BSP_LED_CTR(0,LED_On); vTaskDelay(500/portTICK_RATE_MS); } } led按照预定时间闪烁,freeRTOS移植成功 二.几个常用的API函数 1.xTaskCreate() 函数原型: BaseType_t xTaskCreate( TaskFunction_t pxTaskCode, //任务函数指针 const char * const pcName, //任务名称 const configSTACK_DEPTH_TYPE usStackDepth, //任务堆栈大小 void * const pvParameters, //传入任务函数的参数 UBaseType_t uxPriority, //任务优先级 TaskHandle_t * const pxCreatedTask ) //任务句柄 该函数的目的就是建立一个任务
开启任务调度器,会默认创建优先级为0的prvIdleTask()任务(空闲任务) vTaskStartScheduler()中创建空闲任务的代码片段 xReturn = xTaskCreate( prvIdleTask, configIDLE_TASK_NAME, configMINIMAL_STACK_SIZE, ( void * ) NULL, portPRIVILEGE_BIT, &xIdleTaskHandle ); vTaskStartScheduler()中还关闭了中断(内联汇编),在portmacro.h中的vPortRaiseBASEPRI函数 freeRTOS中还有许多可以由用户定义的宏定义函数,vTaskStartScheduler()中就调用了以下的‘空’函数 portCONFIGURE_TIMER_FOR_RUN_TIME_STATS(); traceTASK_SWITCHED_IN(); FreeRTOS.h中的空宏定义 #ifndef portCONFIGURE_TIMER_FOR_RUN_TIME_STATS #define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() #endif 这些空的宏定义也是freeRTOS移植中非常重要的一环 3.vTaskDelete() 函数原型 void vTaskDelete( TaskHandle_t xTaskToDelete ); 有创建任务的函数就有删除任务的函数,直接向vTaskDelete()里头丢任务的句柄,就能完成删除任务的操作,若删除的是调用这个函数的任务本身,可以直接传入NULL即可完成删除自身的操作。常用于开始任务。 有些任务别瞎***删!! 4.vTaskDelay() 函数原型 void vTaskDelay( const TickType_t xTicksToDelay ); 延时函数,相比于普通的软件延时和裸机的定时器延时,vTaskDelay()有本质上的区别,它能使任务进入阻塞态,从而触发freeRTOS的任务调度器。可近似的简单理解为vTaskDelay()使任务阻塞一段时间,把cpu的使用权暂时交出去,阻塞的时间单位为系统心跳,系统心跳在freeRTOSConfig.h中定义 #define configTICK_RATE_HZ ( ( TickType_t ) 1000 ) 阻塞的时间可以通过除以portTICK_RATE_MS来变成ms,portTICK_RATE_MS在FreeRTOS.h中有定义 5.vTaskDelayUntil() 函数原型 BaseType_t xTaskDelayUntil( TickType_t * const pxPreviousWakeTime, const TickType_t xTimeIncrement ); 相比vTaskDelay(),vTaskDelayUntil()更能适用于周期性任务。vTaskDelay()能阻塞任务一段固定的时间,而vTaskDelayUntil()的计时时间长是从上一次从阻塞态回到就绪态时开始算起,所以能保证任务的周期性,屏蔽了任务运行本身所用的时长
立刻唤醒调度器,不必等到其他能堵塞任务的情况出现。 注意:taskYIELD()不能将cpu的使用权交给比该任务低的就绪态任务,taskYIELD()只是放弃该时间片,并没有使任务进入阻塞态! 三,任务优先级API函数 1.vTaskPrioritySet() 可以在调度器运行之后改变任务的优先级。 函数原型 void vTaskPrioritySet( TaskHandle_t xTask, UBaseType_t uxNewPriority );
查询任务优先级。 四,空闲任务 五,队列相关API函数 1.xQueueCreate() 队列创建函数 在硬件中断中禁止使用这个函数!! 原型 #define xQueueCreate( uxQueueLength, uxItemSize ) xQueueGenericCreate( ( uxQueueLength ), ( uxItemSize ), ( queueQUEUE_TYPE_BASE ) )
向队列的队尾发送数据 原型 #define xQueueSend( xQueue, pvItemToQueue, xTicksToWait ) xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), ( xTicksToWait ), queueSEND_TO_BACK )
读取队列数据 在硬件中断中禁止使用这个函数!! 原型 BaseType_t xQueueReceive( QueueHandle_t xQueue, void * const pvBuffer, TickType_t xTicksToWait )
1.vSemaphoreCreateBinary() & xSemaphoreCreateBinary() vSemaphoreCreateBinary()已经弃用 #if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) #define vSemaphoreCreateBinary( xSemaphore ) { ( xSemaphore ) = xQueueGenericCreate( ( UBaseType_t ) 1, semSEMAPHORE_QUEUE_ITEM_LENGTH, queueQUEUE_TYPE_BINARY_SEMAPHORE ); if( ( xSemaphore ) != NULL ) { ( void ) xSemaphoreGive( ( xSemaphore ) ); } } #endif vSemaphoreCreateBinary()采用宏定义的方式,中间使用了xSemaphoreGive()产生了一个二值信号 xSemaphoreCreateBinary() #if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) #define xSemaphoreCreateBinary() xQueueGenericCreate( ( UBaseType_t ) 1, semSEMAPHORE_QUEUE_ITEM_LENGTH, queueQUEUE_TYPE_BINARY_SEMAPHORE ) #endif 相比vSemaphoreCreateBinary()没有在创建时产生二值信号 vSemaphoreCreateBinary()直接返回xSemaphoreHandle_t,xSemaphoreCreateBinary()需要传入xSemaphoreHandle_t(不是地址,变量本身就是一个指向结构体的指针) 2.xSemaphoreGive() & xSemaphoreGiveFromISR() 发送一个信号量 xSemaphoreGive原型 #define xSemaphoreGive( xSemaphore ) xQueueGenericSend( ( QueueHandle_t ) ( xSemaphore ), NULL, semGIVE_BLOCK_TIME, queueSEND_TO_BACK ) xSemaphore参数 要发送的信号量 返回值 pdPASS pdFALSE xSemaphoreGiveFromISR()原型 #define xSemaphoreGiveFromISR( xSemaphore, pxHigherPriorityTaskWoken ) xQueueGiveFromISR( (
#define xSemaphoreGiveFromISR( xSemaphore, pxHigherPriorityTaskWoken ) xQueueGiveFromISR( ( QueueHandle_t ) ( xSemaphore ), ( pxHigherPriorityTaskWoken ) )
任务进入阻塞态,等待(释放)一个信号量 xSemaphoreTake()原型 #define xSemaphoreTake( xSemaphore, xBlockTime ) xQueueSemaphoreTake( ( xSemaphore ), ( xBlockTime ) )
1.任务莫名其妙被饿死 可能是任务堆栈溢出!给相关任务多分配一写内存即可。 六,示例 1.通过一个任务函数创建多个任务(使用任务参数) #include "stm32f10x.h" #include "bsp_usart.h" #include "FreeRTOSConfig.h" #include "FreeRTOS.h" #include "task.h" void Usart_Send_Task(void*ptr); TaskHandle_t Task1 = NULL; TaskHandle_t Task2 = NULL; static uint8_t Send1[] = "Task1rn"; static uint8_t Send2[] = "Task2rn"; int main(void) { NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4); BSP_Usart_Init(); xTaskCreate( Usart_Send_Task, "Task1", 16, Send1, 3, &Task1 ); xTaskCreate( Usart_Send_Task, "Task1", 16, Send2, 3, &Task2 ); vTaskStartScheduler(); while(1); } void Usart_Send_Task(void*ptr) { while(1) { printf("%s",(uint8_t*)ptr); vTaskDelay(200/portTICK_RATE_MS); } } 注意:任务参数必须定义为静态变量,通过指针传入任务函数。 2.更改任务优先级 #include "stm32f10x.h" #include "bsp_usart.h" #include "FreeRTOSConfig.h" #include "FreeRTOS.h" #include "task.h" void vApplicationIdleHook( void ); void Usart_Send_Task(void*ptr); void Usart_Rx_Task(void*ptr); static TaskHandle_t Task1 = NULL; static TaskHandle_t Task2 = NULL; static uint8_t Send1[] = "Task1rn"; int main(void) { NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4); BSP_Usart_Init(); xTaskCreate( Usart_Send_Task, "Task1", 32, Send1, 6, &Task1 ); xTaskCreate( Usart_Rx_Task, "Task2", 32, NULL, 6, &Task2 ); vTaskStartScheduler(); while(1); } void Usart_Rx_Task(void*ptr) { while(1) { uint8_t*dat; uint8_t Target; dat = Usart_Read(1); if(*dat != 0) { for(uint8_t temp=0;temp<*dat;temp++) { if(*(dat+temp+1) == 'n') { Target = 0; for(uint8_t n=0;n Target*=10; Target+=*(dat+n+1) - 0x30; } USART_Push(1,temp+2); printf("Input:%drn",Target); vTaskPrioritySet(Task1,Target); break; } } } vTaskDelay(200/portTICK_RATE_MS); } } void Usart_Send_Task(void*ptr) { while(1) { printf("%s",(uint8_t*)ptr); printf("优先级:%drn",uxTaskPriorityGet(NULL)); vTaskDelay(200/portTICK_RATE_MS); } } |
|
|
|
3.串口指令截取(’n’为指令结束符,基于自写的bsp_usart)
void Usart_Rx_Task(void*ptr) { while(1) { uint8_t*dat; uint8_t cmd; dat = Usart_Read(1); if(*dat != 0) { for(uint8_t temp=0;temp<*dat;temp++) { if(*(dat+temp+1) == 'n') { cmd = 0; for(uint8_t n=0;n cmd*=10; cmd+=*(dat+n+1) - 0x30; } USART_Push(1,temp+2); printf("Input:%drn",Target); break; } } } vTaskDelay(200/portTICK_RATE_MS); } } 4.队列发送结构体 #include "stm32f10x.h" #include #include "bsp_usart.h" #include "FreeRTOSConfig.h" #include "FreeRTOS.h" #include "queue.h" #include "task.h" void Usart_Send_Task(void*ptr); void Usart_Rx_Task(void*ptr); static TaskHandle_t Task1 = NULL; static TaskHandle_t Task2 = NULL; typedef struct { uint8_t TaskPriority; uint16_t TaskDelayTime; }xDat; static QueueHandle_t Usart_Cmd = NULL; static uint8_t Send1[] = "Task1"; int main(void) { NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4); BSP_Usart_Init(); Usart_Cmd = xQueueCreate(4,sizeof(xDat)); xTaskCreate( Usart_Send_Task, "Task1", 64, Send1, 6, &Task1 ); xTaskCreate( Usart_Rx_Task, "Task2", 32, NULL, 6, &Task2 ); if(Usart_Cmd == NULL) printf("QueueFlasern"); else vTaskStartScheduler(); while(1); } void Usart_Rx_Task(void*ptr) { while(1) { uint8_t*dat; xDat Usart_Rx; dat = Usart_Read(1); if(*dat != 0) { for(uint8_t temp=0;temp<*dat;temp++) { if(*(dat+temp+1) == 'n') { uint8_t n,m; Usart_Rx.TaskPriority = 0; Usart_Rx.TaskDelayTime = 0; for(n=0;*(dat+n+1)!=' ';n++) { Usart_Rx.TaskPriority *= 10; Usart_Rx.TaskPriority += *(dat+n+1) - 0x30; } for(m=0;*(dat+n+m+2)!='n';m++) { Usart_Rx.TaskDelayTime *= 10; Usart_Rx.TaskDelayTime += *(dat+n+m+2) - 0x30; } printf("%d %drn",Usart_Rx.TaskPriority,Usart_Rx.TaskDelayTime); if(xQueueSend(Usart_Cmd,&Usart_Rx,0) == errQUEUE_FULL) printf("QueueSendErrrn"); USART_Push(1,temp+1); break; } } } vTaskDelay(50/portTICK_RATE_MS); } } void Usart_Send_Task(void*ptr) { while(1) { static xDat In = {6,300}; xQueueReceive(Usart_Cmd,&In,0); vTaskPrioritySet(NULL,In.TaskPriority); printf("%srnDelay_Time:%drnPriority:%drn",(uint8_t*)ptr,In.TaskDelayTime,uxTaskPriorityGet(NULL)); vTaskDelay(In.TaskDelayTime/portTICK_RATE_MS); } } 5.ISR中发送信号量 任务函数 SemaphoreHandle_t Usart_Rx_Flag = NULL; QueueHandle_t Usart_Cmd = NULL; void Usart_Rx_Task(void*ptr) { while(1) { uint8_t*dat; xDat Cmd; xSemaphoreTake(Usart_Rx_Flag,portMAX_DELAY); dat = Usart_Read(1); if(*(dat+*dat) == 'n') { uint8_t temp,n; Cmd.TaskPriority = 0; Cmd.TaskDelayTime = 0; for(temp=0;*(dat+temp+1)!=' ';temp++) { Cmd.TaskPriority*=10; Cmd.TaskPriority+=*(dat+temp+1) - 0x30; } for(n=0;*(dat+temp+n+2)!='n';n++) { Cmd.TaskDelayTime*=10; Cmd.TaskDelayTime+=*(dat+temp+n+2) - 0x30; } USART_Clear(1); xQueueSend(Usart_Cmd,&Cmd,0); } } } void Usart_Send_Task(void*ptr) { while(1) { static xDat In = {6,300}; xQueueReceive(Usart_Cmd,&In,0); vTaskPrioritySet(NULL,In.TaskPriority); printf("%srnDelay_Time:%drnPriority:%drn",(uint8_t*)ptr,In.TaskDelayTime,uxTaskPriorityGet(NULL)); vTaskDelay(In.TaskDelayTime/portTICK_RATE_MS); } } 中断 extern SemaphoreHandle_t Usart_Rx_Flag; void USART1_IRQHandler(void) { BaseType_t temp = pdFALSE; if(USART_GetITStatus(USART1,USART_IT_RXNE) == SET) { Rx_SbufferInput(1,USART_ReceiveData(USART1)); xSemaphoreGive(Usart_Rx_Flag); xSemaphoreGiveFromISR(Usart_Rx_Flag,&temp); portEND_SWITCHING_ISR(&temp); USART_ClearITPendingBit(USART1,USART_IT_RXNE); } } 注意:在其他文件里定义的Usart_Rx_Flag不能是静态变量,否则无法通过exturn声明该变量! 6.在缓存式串口ISR方式二值信号量 * 在串口数据量过大和波特率过高时不建议使用二值信号量直接触发服务 * 函数 * 通常采用缓存式,中断产生信号量后,由信号量触发缓存函数。当达到某 * 个特殊条件(超时,换行…)时在触发服务函数处理数据 extern SemaphoreHandle_t Usart_Rx_Flag; void USART1_IRQHandler(void) { BaseType_t temp = pdFALSE; if(USART_GetITStatus(USART1,USART_IT_RXNE) == SET) { Rx_SbufferInput(1,USART_ReceiveData(USART1)); if(USART_Rx_Sbuffer[0][USART_Rx_Sbuffer[0][0]+1] == 'n') { xSemaphoreGiveFromISR(Usart_Rx_Flag,&temp); portEND_SWITCHING_ISR(&temp); } USART_ClearITPendingBit(USART1,USART_IT_RXNE); } } 7.freeRTOS项目基本模板 main.c 任务,信号量,队列和硬件的初始化 #include "stm32f10x.h" #include #include "self_type.h" #include "bsp_usart.h" #include "bsp_oled12864.h" #include "FreeRTOSConfig.h" #include "FreeRTOS.h" #include "queue.h" #include "task.h" #include "semphr.h" #define System_Init_Task_Stack 64 #define Usart_Send_Task_Stack 64 #define Usart_Rx_Task_Stack 64 void System_Init_Task(void*ptr); void Usart_Send_Task(void*ptr); void Usart_Rx_Task(void*ptr); TaskHandle_t Init_TaskHandel = NULL; TaskHandle_t Usart_Send_TaskHandle = NULL; TaskHandle_t Usart_Rx_TaskHandle = NULL; SemaphoreHandle_t Usart_Rx_Flag = NULL; QueueHandle_t Usart_Rx_Cmd = NULL; int main(void) { xTaskCreate( System_Init_Task, "SystemInit", System_Init_Task_Stack, NULL, 15, &Init_TaskHandel ); vTaskStartScheduler(); while(1); } void System_Init_Task(void*ptr) { //硬件初始化和中断分组 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4); BSP_Usart_Init(); //创建串口接收信号量和串口指令队列 Usart_Rx_Cmd = xQueueCreate(4,sizeof(Usart_Send_TaskDat)); Usart_Rx_Flag = xSemaphoreCreateBinary(); //创建任务 xTaskCreate( Usart_Send_Task, "UsartSend", Usart_Send_Task_Stack, "Usart_Send_Task", 4, &Usart_Send_TaskHandle ); xTaskCreate( Usart_Rx_Task, "UsartRx", Usart_Rx_Task_Stack, NULL, 10, &Usart_Rx_TaskHandle ); //检查是否初始化成功 if(Usart_Rx_Flag!=NULL && Usart_Rx_Cmd!=NULL && Usart_Send_TaskHandle!=NULL && Usart_Rx_TaskHandle!=NULL) { printf("System_Initrn"); vTaskDelete(NULL); }else { printf("SystemInit_Flasern"); while(1); } } void Usart_Rx_Task(void*ptr) { while(1) { uint8_t*dat; Usart_Send_TaskDat Cmd; xSemaphoreTake(Usart_Rx_Flag,portMAX_DELAY); dat = Usart_Read(1); uint8_t temp,n; Cmd.TaskDelayTime = 0; Cmd.TaskPriority = 0; for(temp=0;*(dat+temp+1)!=' ';temp++) { Cmd.TaskDelayTime*=10; Cmd.TaskDelayTime+=*(dat+temp+1)-0x30; } for(n=0;*(dat+temp+n+2)!='n';n++) { Cmd.TaskPriority*=10; Cmd.TaskPriority+=*(dat+temp+n+2)-0x30; } USART_Clear(1); xQueueSend(Usart_Rx_Cmd,&Cmd,0); } } void Usart_Send_Task(void*ptr) { while(1) { static Usart_Send_TaskDat In = {6,300}; xQueueReceive(Usart_Rx_Cmd,&In,0); vTaskPrioritySet(NULL,In.TaskPriority); printf("%srnDelay_Time:%drnPriority:%drn",(uint8_t*)ptr,In.TaskDelayTime,uxTaskPriorityGet(NULL)); vTaskDelay(In.TaskDelayTime/portTICK_RATE_MS); } } self_type.h 定义需要用的结构体类型 #ifndef _SELF_TYPE_H_ #define _SELF_TYPE_H_ #include "stm32f10x.h" //串口发送任务信息结构体 typedef struct { uint8_t TaskPriority; uint16_t TaskDelayTime; }Usart_Send_TaskDat; #endif isr.c 中断 #include "isr.h" #include "bsp_usart.h" #include "FreeRTOS.h" #include "queue.h" #include "semphr.h" /***************中断***********************/ extern SemaphoreHandle_t Usart_Rx_Flag; void USART1_IRQHandler(void) { BaseType_t temp = pdFALSE; if(USART_GetITStatus(USART1,USART_IT_RXNE) == SET) { Rx_SbufferInput(1,USART_ReceiveData(USART1)); if(*(Usart_Read(1) + *Usart_Read(1))=='n') { xSemaphoreGiveFromISR(Usart_Rx_Flag,&temp); portEND_SWITCHING_ISR(&temp); } USART_ClearITPendingBit(USART1,USART_IT_RXNE); } } void USART2_IRQHandler(void) { if(USART_GetITStatus(USART1,USART_IT_RXNE) == SET) { Rx_SbufferInput(2,USART_ReceiveData(USART2)); USART_ClearITPendingBit(USART2,USART_IT_RXNE); } } void USART3_IRQHandler(void) { if(USART_GetITStatus(USART3,USART_IT_RXNE) == SET) { Rx_SbufferInput(3,USART_ReceiveData(USART3)); USART_ClearITPendingBit(USART3,USART_IT_RXNE); } } void DMA1_Channel4_IRQHandler(void) { if(DMA_GetITStatus(DMA1_IT_TC4) == SET) { Tx_Flag_Clear(1); TargetDMA_Channel[0]->CCR &= (uint16_t)(~DMA_CCR1_EN); DMA_ClearITPendingBit(DMA1_IT_TC4); } } void DMA1_Channel7_IRQHandler(void) { if(DMA_GetITStatus(DMA1_IT_TC7) == SET) { Tx_Flag_Clear(2); TargetDMA_Channel[1]->CCR &= (uint16_t)(~DMA_CCR1_EN); DMA_ClearITPendingBit(DMA1_IT_TC7); } } void DMA1_Channel2_IRQHandler(void) { if(DMA_GetITStatus(DMA1_IT_TC2) == SET) { Tx_Flag_Clear(3); TargetDMA_Channel[2]->CCR &= (uint16_t)(~DMA_CCR1_EN); DMA_ClearITPendingBit(DMA1_IT_TC2); } } |
|
|
|
只有小组成员才能发言,加入小组>>
调试STM32H750的FMC总线读写PSRAM遇到的问题求解?
1771 浏览 1 评论
X-NUCLEO-IHM08M1板文档中输出电流为15Arms,15Arms是怎么得出来的呢?
1619 浏览 1 评论
1070 浏览 2 评论
STM32F030F4 HSI时钟温度测试过不去是怎么回事?
724 浏览 2 评论
ST25R3916能否对ISO15693的标签芯片进行分区域写密码?
1673 浏览 2 评论
1936浏览 9评论
STM32仿真器是选择ST-LINK还是选择J-LINK?各有什么优势啊?
729浏览 4评论
STM32F0_TIM2输出pwm2后OLED变暗或者系统重启是怎么回事?
569浏览 3评论
594浏览 3评论
stm32cubemx生成mdk-arm v4项目文件无法打开是什么原因导致的?
552浏览 3评论
小黑屋| 手机版| Archiver| 德赢Vwin官网 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-22 20:19 , Processed in 0.998511 second(s), Total 78, Slave 62 queries .
Powered by 德赢Vwin官网 网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
德赢Vwin官网 观察
版权所有 © 湖南华秋数字科技有限公司
德赢Vwin官网 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号