IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> 御神楽的学习记录之HAL库移植μCOSIII -> 正文阅读

[嵌入式]御神楽的学习记录之HAL库移植μCOSIII


前言

实时操作系统(RTOS) 是指当外界事件或数据产生时,能够接受并以足够快的速度予以处理,其处理的结果又能在规定的时间之内来控制生产过程或对处理系统做出快速响应,并控制所有实时任务协调一致运行的操作系统。提供及时响应和高可靠性是其主要特点。
实时操作系统是保证在一定时间限制内完成特定功能的操作系统。实时操作系统有硬实时软实时之分,硬实时要求在规定的时间内必须完成操作,这是在操作系统设计时保证的;软实时则只要按照任务的优先级,尽可能快地完成操作即可。我们通常使用的操作系统在经过一定改变之后就可以变成实时操作系统。


一、μCOS简介

1.简介

uC/OS-III(Micro C OS Three 微型的C 语言编写的操作系统第3版) 是一个可升级的可固化的,基于优先级的实时内核。它对任务的个数无限制。uC/OS-III 是一个第3 代的系统内核,支持现代的实时内核所期待的大部分功能。例如资源管理,同步,任务间的通信等等。
在这里插入图片描述

2.特点

1)优先级
μCOS-III是可以抢占的多任务内核,始终运行进入就绪态的最重要的任务。μC/OS-III支持无限数量的任务,并允许在运行时,监测堆栈增长的任务。它还支持无限数量的优先级。然而,通常情况下,对于大多数应用,32至256个不同的优先级是足够的。
2)多任务
uCOS-III允许有任意数量的任务,信号量,互斥信号量,事件标志,消息队列,定时器和内存分区(仅受限于处理器可用的RAM大小)。
3)中断极快
μC/OS-III提供接近零的中断停用时间。μC/OS-III有一些内部数据结构和变量,需要获得原子访问权(不能够被打断的)。这些关键区域的保护由锁调度,而不是由禁用中断实现。中断被禁用的时钟周期几乎为零,确保了实时操作系统将能够响应一些最快的中断源。

二、STM32F103C8T6移植uCOS

1.uCOS下载

STM32F107uCOS样例下载
下载完成后文件如下:
在这里插入图片描述

2.文件导入

1)项目创建
使用STM32CUBEMX创建空项目
2)项目管理
1、在新建项目中新建文件夹UCOSIII
在这里插入图片描述
2、将官网样例文件中的uC-CPUuC-LIBuCOS-III移动到新建的文件夹UCOSIII中,并新建文件夹uCOS-BSPuCOS-CONFIG文件
在这里插入图片描述
3)文件添加
在这里插入图片描述
新建Groups:uCOS-BSPuCOS-CPUuCOS-LIBuCOS-CoreuCOS-PortuCOS-Config
1、uCOS-BSP中添加文件
在这里插入图片描述
完成后如下:
在这里插入图片描述
2、uCOS-CPU中添加文件
在这里插入图片描述
在这里插入图片描述
完成后如下:
在这里插入图片描述
3、uCOS-LIB中添加文件
在这里插入图片描述
在这里插入图片描述
完成后如下:
在这里插入图片描述

4、uCOS-Core中添加文件
在这里插入图片描述
共计20个文件
5、uCOS-Port中添加文件
在这里插入图片描述
完成后如下:
在这里插入图片描述
6、uCOS-Config中添加文件
在这里插入图片描述
7、keil总文件目录
在这里插入图片描述
在这里插入图片描述
4)头文件路径添加
在这里插入图片描述

3.文件修改

1、启动文件
在这里插入图片描述

DCD     PendSV_Handler             ; PendSV Handler
DCD     SysTick_Handler            ; SysTick Handler

改为
在这里插入图片描述

PendSV_Handler  PROC
                EXPORT  PendSV_Handler             [WEAK]
                B       .
                ENDP
SysTick_Handler PROC
                EXPORT  SysTick_Handler            [WEAK]
                B       .
                ENDP

改为
在这里插入图片描述
2、app_cfg.h

#define APP_CFG_SERIAL_EN DEF_ENABLED
改为
#define APP_CFG_SERIAL_EN DEF_DISABLED

#define APP_TRACE BSP_Ser_Printf
改为
#define APP_TRACE (void)

3、includes.h

第一处修改: 添加相关头文件
#include <bsp.h>
修改为
#include <bsp.h>
#include “gpio.h”
#include “app_cfg.h”
#include "app.h"

第二处修改: 添加HAL 库
#include <stm32f10x_lib.h>
修改为
#include "stm32f1xx_hal.h"

4、 bsp.c和bsp.h

//bsp.c
#include "includes.h"

#define  DWT_CR      *(CPU_REG32 *)0xE0001000
#define  DWT_CYCCNT  *(CPU_REG32 *)0xE0001004
#define  DEM_CR      *(CPU_REG32 *)0xE000EDFC
#define  DBGMCU_CR   *(CPU_REG32 *)0xE0042004

#define  DEM_CR_TRCENA                   (1 << 24)
#define  DWT_CR_CYCCNTENA                (1 <<  0)

CPU_INT32U  BSP_CPU_ClkFreq (void)
{
    return HAL_RCC_GetHCLKFreq();
}

void BSP_Tick_Init(void)
{
	CPU_INT32U cpu_clk_freq;
	CPU_INT32U cnts;
	cpu_clk_freq = BSP_CPU_ClkFreq();
	
	#if(OS_VERSION>=3000u)
		cnts = cpu_clk_freq/(CPU_INT32U)OSCfg_TickRate_Hz;
	#else
		cnts = cpu_clk_freq/(CPU_INT32U)OS_TICKS_PER_SEC;
	#endif
	OS_CPU_SysTickInit(cnts);
}



void BSP_Init(void)
{
	BSP_Tick_Init();

}


#if (CPU_CFG_TS_TMR_EN == DEF_ENABLED)
void  CPU_TS_TmrInit (void)
{
    CPU_INT32U  cpu_clk_freq_hz;


    DEM_CR         |= (CPU_INT32U)DEM_CR_TRCENA;                /* Enable Cortex-M3's DWT CYCCNT reg.                   */
    DWT_CYCCNT      = (CPU_INT32U)0u;
    DWT_CR         |= (CPU_INT32U)DWT_CR_CYCCNTENA;

    cpu_clk_freq_hz = BSP_CPU_ClkFreq();
    CPU_TS_TmrFreqSet(cpu_clk_freq_hz);
}
#endif


#if (CPU_CFG_TS_TMR_EN == DEF_ENABLED)
CPU_TS_TMR  CPU_TS_TmrRd (void)
{
    return ((CPU_TS_TMR)DWT_CYCCNT);
}
#endif


#if (CPU_CFG_TS_32_EN == DEF_ENABLED)
CPU_INT64U  CPU_TS32_to_uSec (CPU_TS32  ts_cnts)
{
	CPU_INT64U  ts_us;
  CPU_INT64U  fclk_freq;

 
  fclk_freq = BSP_CPU_ClkFreq();
  ts_us     = ts_cnts / (fclk_freq / DEF_TIME_NBR_uS_PER_SEC);

  return (ts_us);
}
#endif
 
 
#if (CPU_CFG_TS_64_EN == DEF_ENABLED)
CPU_INT64U  CPU_TS64_to_uSec (CPU_TS64  ts_cnts)
{
	CPU_INT64U  ts_us;
	CPU_INT64U  fclk_freq;


  fclk_freq = BSP_CPU_ClkFreq();
  ts_us     = ts_cnts / (fclk_freq / DEF_TIME_NBR_uS_PER_SEC);
	
  return (ts_us);
}
#endif
###################################################################
//bsp.h
#ifndef  __BSP_H__
#define  __BSP_H__

#include "stm32f1xx_hal.h"

void BSP_Init(void);

#endif                                                 /* End of module include.   

5、lib_cfg.h
这个头文件中有一个宏定义:
#define LIB_MEM_CFG_HEAP_SIZE 27u * 1024u
表示把堆的空间设置为27KB,但是我使用的stm32f103c8t6的RAM总共才20K,所以这里需要将堆空间改小一点,我改成了10K
#define LIB_MEM_CFG_HEAP_SIZE 10u * 1024u
此处的修改若是对于RAM空间较大的单片机是没有必要的,但是对于小容量的单片机则是必须的。

三、简单多任务实现

1、main.c

/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "usart.h"
#include "gpio.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */

#include <includes.h>
#include <stdio.h>
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
//重写printf函数
int fputc(int ch,FILE *f)
 
{
    HAL_UART_Transmit(&huart1,(uint8_t *)&ch,1,0xFFFF);    
		//等待发送结束	
		while(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_TC)!=SET){
		}		

    return ch;
}


/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */

//任务控制块
static  OS_TCB   AppTaskStartTCB;
OS_TCB LEDPB6TaskTCB;//LEDPB6
OS_TCB LEDPB7TaskTCB;//LEDPB7
OS_TCB USART1TaskTCB;//串口1

//任务堆栈
static  CPU_STK  AppTaskStartStk[APP_TASK_START_STK_SIZE];

/* 私有函数原形 --------------------------------------------------------------*/
static  void  AppTaskCreate(void);
static  void  AppObjCreate(void);
static  void  AppTaskStart(void *p_arg);

//任务函数
void LEDPB6_TASK(void *p_arg);
void LEDPB7_TASK(void *p_arg);
void USART1_TASK(void *p_arg);

//任务优先级
#define LEDPB6_TASK_PRIO 3  //PB6优先级
#define LEDPB7_TASK_PRIO 3  //PB7优先级
#define USART1_TASK_PRIO 3 //USART1优先级

//任务栈大小
#define LEDPB6_STK_SIZE 128 //PB6栈大小
#define LEDPB7_STK_SIZE 128 //PB7栈大小
#define USART1_STK_SIZE 128 //串口1栈大小


CPU_STK LEDPB6_TASK_STK[LEDPB6_STK_SIZE];//PB6任务栈
CPU_STK LEDPB7_TASK_STK[LEDPB7_STK_SIZE];//PB7任务栈
CPU_STK USART1_TASK_STK[USART1_STK_SIZE];//串口任务栈

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */
	
	OS_ERR  err;

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */
	
	OSInit(&err);  

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USART1_UART_Init();
  
  /* USER CODE BEGIN 2 */

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  /* 创建任务 */
	OSTaskCreate((OS_TCB     *)&AppTaskStartTCB,                /* Create the start task                                */
				 (CPU_CHAR   *)"App Task Start",
				 (OS_TASK_PTR ) AppTaskStart,
				 (void       *) 0,
				 (OS_PRIO     ) APP_TASK_START_PRIO,
				 (CPU_STK    *)&AppTaskStartStk[0],
				 (CPU_STK_SIZE) APP_TASK_START_STK_SIZE / 10,
				 (CPU_STK_SIZE) APP_TASK_START_STK_SIZE,
				 (OS_MSG_QTY  ) 0,
				 (OS_TICK     ) 0,
				 (void       *) 0,
				 (OS_OPT      )(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR),
				 (OS_ERR     *)&err);
				 
	OSStart(&err);            /* Start multitasking (i.e. give control to uC/OS-III). */

//  while (1)
//  {
//    /* USER CODE END WHILE */
    /* USER CODE BEGIN 3 */
//  }
  /* USER CODE END 3 */
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI_DIV2;
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1) != HAL_OK)
  {
    Error_Handler();
  }
}

/* USER CODE BEGIN 4 */

/**
  * 函数功能: 启动任务函数体。
  * 输入参数: p_arg 是在创建该任务时传递的形参
  * 返 回 值: 无
  * 说    明:无
  */
static  void  AppTaskStart (void *p_arg)
{
  OS_ERR      err;

  (void)p_arg;

  BSP_Init();                                                 /* Initialize BSP functions                             */
  CPU_Init();

  Mem_Init();                                                 /* Initialize Memory Management Module                  */

#if OS_CFG_STAT_TASK_EN > 0u
  OSStatTaskCPUUsageInit(&err);                               /* Compute CPU capacity with no task running            */
#endif

  CPU_IntDisMeasMaxCurReset();

  AppTaskCreate();                                            /* Create Application Tasks                             */

  AppObjCreate();                                             /* Create Application Objects                           */

	OSTaskCreate((OS_TCB 	* )&LEDPB6TaskTCB,		
				 (CPU_CHAR	* )"PB6 task", 		
				 (OS_TASK_PTR )LEDPB6_TASK, 			
				 (void		* )0,					
				 (OS_PRIO	  )LEDPB6_TASK_PRIO,     
				 (CPU_STK   * )&LEDPB6_TASK_STK[0],	
				 (CPU_STK_SIZE)LEDPB6_STK_SIZE/10,	
				 (CPU_STK_SIZE)LEDPB6_STK_SIZE,		
				 (OS_MSG_QTY  )0,					
				 (OS_TICK	  )0,					
				 (void   	* )0,					
				 (OS_OPT      )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR,
				 (OS_ERR 	* )&err);	
				 
	OSTaskCreate((OS_TCB 	* )&LEDPB7TaskTCB,		
				 (CPU_CHAR	* )"PB7 task", 		
				 (OS_TASK_PTR )LEDPB7_TASK, 			
				 (void		* )0,					
				 (OS_PRIO	  )LEDPB7_TASK_PRIO,     
				 (CPU_STK   * )&LEDPB7_TASK_STK[0],	
				 (CPU_STK_SIZE)LEDPB7_STK_SIZE/10,	
				 (CPU_STK_SIZE)LEDPB7_STK_SIZE,		
				 (OS_MSG_QTY  )0,					
				 (OS_TICK	  )0,					
				 (void   	* )0,					
				 (OS_OPT      )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR,
				 (OS_ERR 	* )&err);
				 
				 
	OSTaskCreate((OS_TCB 	* )&USART1TaskTCB,		
				 (CPU_CHAR	* )"usart1 task", 		
				 (OS_TASK_PTR )USART1_TASK, 			
				 (void		* )0,					
				 (OS_PRIO	  )USART1_TASK_PRIO,     
				 (CPU_STK   * )&USART1_TASK_STK[0],	
				 (CPU_STK_SIZE)USART1_STK_SIZE/10,	
				 (CPU_STK_SIZE)USART1_STK_SIZE,		
				 (OS_MSG_QTY  )0,					
				 (OS_TICK	  )0,					
				 (void   	* )0,					
				 (OS_OPT      )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR,
				 (OS_ERR 	* )&err);				 
				 
	OS_TaskSuspend((OS_TCB*)&AppTaskStartTCB,&err);
 
}


void  LEDPB6_TASK (void *p_arg)
{
  OS_ERR      err;

  (void)p_arg;

  BSP_Init();                                                 /* Initialize BSP functions                             */
  CPU_Init();

  Mem_Init();                                                 /* Initialize Memory Management Module                  */

#if OS_CFG_STAT_TASK_EN > 0u
  OSStatTaskCPUUsageInit(&err);                               /* Compute CPU capacity with no task running            */
#endif

  CPU_IntDisMeasMaxCurReset();

  AppTaskCreate();                                            /* Create Application Tasks                             */

  AppObjCreate();                                             /* Create Application Objects                           */

  while (DEF_TRUE)
  {

		OSTimeDly(1000, OS_OPT_TIME_DLY, &err);
		HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_SET);
		OSTimeDly(1000, OS_OPT_TIME_DLY, &err);
		HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_RESET);

  }
}


void  LEDPB7_TASK (void *p_arg)
{
  OS_ERR      err;

  (void)p_arg;

  BSP_Init();                                                 /* Initialize BSP functions                             */
  CPU_Init();

  Mem_Init();                                                 /* Initialize Memory Management Module                  */

#if OS_CFG_STAT_TASK_EN > 0u
  OSStatTaskCPUUsageInit(&err);                               /* Compute CPU capacity with no task running            */
#endif

  CPU_IntDisMeasMaxCurReset();

  AppTaskCreate();                                            /* Create Application Tasks                             */

  AppObjCreate();                                             /* Create Application Objects                           */

  while (DEF_TRUE)
  {

		OSTimeDly(1000, OS_OPT_TIME_DLY, &err);
		OSTimeDly(1000, OS_OPT_TIME_DLY, &err);
		OSTimeDly(1000, OS_OPT_TIME_DLY, &err);
		OSTimeDly(1000, OS_OPT_TIME_DLY, &err);
		OSTimeDly(1000, OS_OPT_TIME_DLY, &err);
		HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_SET);
		OSTimeDly(1000, OS_OPT_TIME_DLY, &err);
		HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_RESET);
  }
}



void  USART1_TASK (void *p_arg)
{
  OS_ERR      err;

  (void)p_arg;

  BSP_Init();                                                 /* Initialize BSP functions                             */
  CPU_Init();

  Mem_Init();                                                 /* Initialize Memory Management Module                  */

#if OS_CFG_STAT_TASK_EN > 0u
  OSStatTaskCPUUsageInit(&err);                               /* Compute CPU capacity with no task running            */
#endif

  CPU_IntDisMeasMaxCurReset();

  AppTaskCreate();                                            /* Create Application Tasks                             */

  AppObjCreate();                                             /* Create Application Objects                           */

  while (DEF_TRUE)
  {
		printf("hello uc/OS! 欢迎来到RTOS多任务环境!");
		OSTimeDly(1000, OS_OPT_TIME_DLY, &err);
		OSTimeDly(1000, OS_OPT_TIME_DLY, &err);
  }
}

/**
  * 函数功能: 创建应用任务
  * 输入参数: p_arg 是在创建该任务时传递的形参
  * 返 回 值: 无
  * 说    明:无
  */
static  void  AppTaskCreate (void)
{

	  
}

/**
  * 函数功能: uCOSIII内核对象创建
  * 输入参数: 无
  * 返 回 值: 无
  * 说    明:无
  */
static  void  AppObjCreate (void)
{
	
}

/* USER CODE END 4 */

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
  __disable_irq();
  while (1)
  {
  }
  /* USER CODE END Error_Handler_Debug */
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

2、运行结果

在这里插入图片描述

3、注意事项

1)keil编译报错内存不足
.\Objects\ucos-led.axf: Error: L6406E: No space in execution regions with .ANY selector matching os_cfg_app.o(.bss).
参考解决方法:
在这里插入图片描述
将红框数据改大即可
2)重写输出函数后需要勾选 Use MicroLIB
在这里插入图片描述


参考

https://blog.csdn.net/weixin_43116606/article/details/105532222

  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2021-12-01 17:52:21  更:2021-12-01 17:52:36 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 10:15:37-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码