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 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> 【北邮果园微处理器设计】gpio.c 应用解读(附gpio.c源码) -> 正文阅读

[嵌入式]【北邮果园微处理器设计】gpio.c 应用解读(附gpio.c源码)

该工具类为北邮-国院-电信工程及管理专业-通信方向-大三课程-微处理器设计(Microprocessor System Design)- Lab 2中英方给的Driver类 gpio.c

由于上课课件中并未对该Driver进行解读,因此在写Lab2的时候就会造成很多的困扰。因此在写完Lab 2之后写了这篇博客希望对还没有做Lab 2的同学及学弟学妹带来帮助。

在本博客中的解读皆基于对该工具类中的方法基于原英文注释的翻译及博主使用体验编写。博客末尾已附上该gpio.c的源码。若有解读错误,欢迎指正。


直接看例子会更快哦!

如果设置了callback但在点击对应PIN还没有反应的,下拉到 设置回调函数 | set_callback 看解决方法。


设置PIN为Input或Output | gpio_set_mode

按照课程PPT的内容,我们在设置PIN

  • 为输入时仅考虑设置其为Pull Up模式或者 Pull Down模式。不考虑其他情况

GPIO输入Pull Up与Pull Down模式解读

该部分为直接引用博主Yngz_Miao的博客【STM32】GPIO工作原理(八种工作方式超详细分析,附电路图)

具体功能果园学子仍以PPT中为准。

Pull Up

img

上拉输入模式下,I/O端口的电平信号直接进入输入数据寄存器。

  • I/O端口悬空(在无信号输入)的情况下,输入端的电平可以保持在高电平
  • 在I/O端口输入为低电平的时候,输入端的电平也还是低电平

Pull Down

img

下拉输入模式下,I/O端口的电平信号直接进入输入数据寄存器。

  • 在I/O端口悬空(在无信号输入)的情况下,输入端的电平为低电平
  • 在I/O端口输入为高电平的时候,输入端的电平为高电平

设置PIN为input端口

调用方法:

void gpio_set_mode(Pin pin, PinMode mode){}

传入参数解释:

  • Pin pin:被设置的PIN

  • PinMode:所要设置的模式。该项可传入选择有

    // The modes configure the GPIO pin as follows:
    //  - Reset:    resets to a default state.
    //  - Input:    sets as a high impedance input.
    //  - Output:   sets as a push-pull output.
    //  - PullUp:   enables the internal pull-up resistor
    //              with the pin configured as an input, and
    //              sets the output to logic high (through
    //              the pull-up resistor).
    //  - PullDown: enables the internal pull-down resistor
    //              with the pin configured as an input, and
    //              sets the output to logic low (through the
    //              pull-down).
    

例子:

  1. 设置PB_0为上拉(Pull Up)输入模式

    gpio_set_mode(PB_0, PullUp);
    
  2. 设置PA_0为下拉(Pull Down)输入模式

    gpio_set_mode(PA_0, PullDown);
    

设置PIN为output端口

调用方法与设置input一致。直接看例子

例子: 设置PA_1为输出端口

gpio_set_mode(PA_1, Output);

读取PIN

读取单个PIN | gpio_get

调用方法:

int gpio_get(Pin pin) {}

传入参数解释:

  • Pin pin:要读取PIN

返回值:为整数类型,而不是二进制数。

例子: 读取PB_0,假设此时PB_0为高电平。

int status = gpio_get_range(PB_0);
//status = 1;
  • 若高电平时,返回1
  • 若低电平时,返回0

读取一个范围内的PIN | gpio_get_range

调用方法:

unsigned int gpio_get_range(Pin pin_base, int count) {{}

传入参数解释:

  • Pin pin_base:从哪一位PIN开始读
  • int count:一共要读取多少位。读一位则设为1

返回值:为整数类型,而不是二进制数。

例子:读取PB_1PB_2PB_3,假设此时PB_1PB_2PB_3分为为110

LINE = gpio_get_range(PB_1, 3);
//LINE = 6

读取时的坑点

两个读取函数使用时,并不是说你将所要读取的PIN设了,它就能马上读到。而是还要再按另一个其他的PIN才能将原来的读进去。

例如:现在我用a = gpio_get_range(PB_1, 3)读取3个PINs。进入Debug模式,假设运行后三个PINs为000,那么在我设置这三个PINs为001时(将PB_1设为HIGH),a仍然为(十进制的)0。在我(随意按一个没有set_mode的键)按PB_10后,a才变为(十进制的)1

设置PIN的值

每一个PIN只有高电平和低电平两个值。

  • HIGH:1
  • LOW:0

设置单个PIN | gpio_set

调用方法:

void gpio_set(Pin pin, int value) {}

传入参数解释:

  • Pin pin:要设置PIN
  • int value:对应要设置的值。注意这个值的类型为int类型的,这里只能输入010对应为LOW。(其他的值我也没测试)

例子:设置PA_1为高电平

gpio_set(PA_1, 1);

设置一个范围内的PIN | gpio_set_range

调用方法:

void gpio_set_range(Pin pin_base, int count, int value) {}

传入参数解释:

  • Pin pin:要从哪一个PIN开始设置
  • int count:要设置多少个PIN
  • int value:对应要设置的值。注意这个值的类型为int类型的也就是说,如果我们要设置3 PINs,当传入参数value的值为4时,这三个PINs会设置为100(左边为MSB)
    • 不允许设置的值超过所设置PIN的数量能表示的值,例如我要设置3个PINs,参数value传入十进制的8,这是会报错的。

例子: 将十进制的7用三个PB_1PB_2PB_3表示

gpio_set_range(PB_1, 3, 7);

例子: 利用PA_0 ~ PA_7这8个PIN来表示0~8的one-hot code。即,当输入十进制(int)的7时,PA_7为HIGH,其他都为LOW

#include <math.h>

gpio_set_range(PA_0, 8, pow(2, 7));//pow(2,x) x为要进行独热编码的十进制的值

设置PIN为原来的相反值 | gpio_toggle

调用方法:

void gpio_toggle(Pin pin) {}

传入参数解释:

  • Pin pin:要设置的PIN

例子:PB_0的值倒置。假设原PB_0 = LOW

gpio_toggle(PB_0);
//执行完后 PB_0 变为 HIGH

设置上升沿(儿)/下降沿(儿)触发 | gpio_set_trigger

调用方法:

void gpio_set_trigger(Pin pin, TriggerMode trig) {}

传入参数解释:

  • Pin pin:所要设置的Pin
  • TriggerMode trig:对应触发的模式。可选RisingFalling

例子: 设置PB_0为上升沿(儿)触发

gpio_set_trigger(PB_0, Rising);

设置回调函数 | gpio_set_callback

设置某个PIN触发后会调用什么函数来响应。(按下按钮触发函数就是设置这个!

调用方法:

void gpio_set_callback(Pin pin, void (*callback)(int status)){}

传入参数解释:

  • Pin pin:所要设置的Pin
  • void (*callback)(int status):所要触发函数的指针(其实就是函数名)。注意这个函数的传入参数一定要是int status,参数一定有且只有这个,函数内可不使用这个函数。

例子: 设定PB_0为Push Button,按下后调用void PB_ISR(int status){}函数

gpio_set_callback(PB_0, PB_ISR);

设置上了面代码,在Debug模式中点击PB_0仍没有触发PB_ISR解决方法: 确保将external interrupts 的 source的值是B。具体步骤为:

  1. 进入Debug模式

  2. 顶部工具栏 Peripherals -> External Interrupts。(点开Peripherals 没有内容就是没有进入debug模式!)

    image-20220515001126999
  3. Source设置为B

    image-20220515001206548

例子:设定PB_0为上升沿儿触发的Push Button,按下后调用void PB_ISR(int status){}函数

gpio_set_trigger(PB_0, Rising);
gpio_set_callback(PB_0, PB_ISR);

工具类源码 gpio.c

#include "platform.h"
#include "gpio.h"

uint32_t IRQ_status;
uint32_t IRQ_port_num;
uint32_t IRQ_pin_index;
uint32_t EXTI_port_set;
uint32_t prioritygroup;
uint32_t priority;

static void (*GPIO_callback)(int status);

void gpio_toggle(Pin pin) {
	// Toggles a GPIO pin.
	// In the absence of a pin toggle register, can be easily
	// implemented with:
	// gpio_set(pin, !gpio_get(pin));
	
	gpio_set(pin, !gpio_get(pin));
}

void gpio_set(Pin pin, int value) {
	// Sets the selected pin to the specified value.
	
	GPIO_TypeDef* p = GET_PORT(pin);
	uint32_t pin_index = GET_PIN_INDEX(pin);
	
	MODIFY_REG(p->ODR,1UL<<pin_index,value<<pin_index);
}

int gpio_get(Pin pin) {
	// Gets the current value of the specified pin.
	GPIO_TypeDef* p = GET_PORT(pin);
	uint32_t pin_index = GET_PIN_INDEX(pin);
	
	return (READ_BIT(p->IDR,(1<<pin_index)))>>pin_index;
	
}

void gpio_set_range(Pin pin_base, int count, int value) {
	// Sets a range of pins to the specified value.
	
	// This can be used to write to an entire port, or just
	// a subset of the (consecutive) pins on a port.
	
	// The output pin_base should be set to the LSB of
	// value. Pin (pin_base + 1) should be LSB + 1, etc.
	
	// The mask for the value parameter should be:
	// ((1 << count) - 1).
	
    GPIO_TypeDef* p = GET_PORT(pin_base);
	uint32_t pin_index = GET_PIN_INDEX(pin_base);
	
	MODIFY_REG(p->ODR,((1UL<<count)-1)<<pin_index,value<<pin_index);
}

unsigned int gpio_get_range(Pin pin_base, int count) {
	// Gets a range of pins.
	
	// This can be used to read an entire port, or just
	// a subset of the (consecutive) pins on a port.
	
	// The LSB of the return value should be the state of
	// pin_base. (LSB + 1) of the return value should be
	// the state of (pin_base + 1) etc.
	// e.g. if the state of pin_base is P1_0 (port 1, pin 0)
	// and count is 4 this, function should return:
	// (P1_3 << 3) | (P1_2 << 2) | (P1_1 << 1) | P1_0
	
	// The mask for the value parameter should be:
	// ((1 << count) - 1).
	GPIO_TypeDef* p = GET_PORT(pin_base);
	uint32_t pin_index = GET_PIN_INDEX(pin_base);
	
	return READ_BIT(p->IDR,(((1 << count) - 1)<<pin_index))>>pin_index;
}

void gpio_set_mode(Pin pin, PinMode mode) {
	// Sets the output mode of a pin.
	
	// If the clock for the pin's port needs to be enabled, it
	// should be done here.
	
	// The modes configure the GPIO pin as follows:
	//  - Reset:    resets to a default state.
	//  - Input:    sets as a high impedance input.
	//  - Output:   sets as a push-pull output.
	//  - PullUp:   enables the internal pull-up resistor
	//              with the pin configured as an input, and
	//              sets the output to logic high (through
	//              the pull-up resistor).
	//  - PullDown: enables the internal pull-down resistor
	//              with the pin configured as an input, and
	//              sets the output to logic low (through the
	//              pull-down).
	
	GPIO_TypeDef* p = GET_PORT(pin);
	uint32_t pin_index = GET_PIN_INDEX(pin);
    /*
	RCC->AHB1ENR|=1UL<<GET_PORT_INDEX(pin);//enable clock output
	// Enable clock for interrupts
	RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN;
	// Enable debug in low-power mode
	DBGMCU->CR |= DBGMCU_CR_DBG_SLEEP | DBGMCU_CR_DBG_STOP | DBGMCU_CR_DBG_STANDBY;
    */

	switch(mode) {
		case Reset: // input floating mode by default
            if (pin_index < 8) 
                MODIFY_REG(p->CRL, 0xFUL<<((pin_index)*4), 4UL<<((pin_index)*4)); //0100
            else
                MODIFY_REG(p->CRH, 0xFUL<<((pin_index-8)*4), 4UL<<((pin_index-8)*4)); 
			break;
		case Input: // same as reset
			 if (pin_index < 8) 
                MODIFY_REG(p->CRL, 0xFUL<<((pin_index)*4), 4UL<<((pin_index)*4)); //0100
            else
                MODIFY_REG(p->CRH, 0xFUL<<((pin_index-8)*4), 4UL<<((pin_index-8)*4));
			break;
		case Output: // output push-pull max 2 MHz
            if (pin_index < 8) 
                MODIFY_REG(p->CRL, 0xFUL<<((pin_index)*4), 2UL<<((pin_index)*4)); //0010
            else
                MODIFY_REG(p->CRH, 0xFUL<<((pin_index-8)*4), 2UL<<((pin_index-8)*4));
			break;
		case PullUp: // input pull-up 
            MODIFY_REG(p->ODR, 1UL<<((pin_index)*4), 1UL<<((pin_index)*4));
            if (pin_index < 8) 
                MODIFY_REG(p->CRL, 0xFUL<<((pin_index)*4), 8UL<<((pin_index)*4)); //1000
            else
                MODIFY_REG(p->CRH, 0xFUL<<((pin_index-8)*4), 8UL<<((pin_index-8)*4));
			break;
		case PullDown: // input pull-down
            MODIFY_REG(p->ODR, 1UL<<((pin_index)*4), 0UL<<((pin_index)*4));
            if (pin_index < 8) 
                MODIFY_REG(p->CRL, 0xFUL<<((pin_index)*4), 8UL<<((pin_index)*4)); //1000
            else
                MODIFY_REG(p->CRH, 0xFUL<<((pin_index-8)*4), 8UL<<((pin_index-8)*4));
			break;
	}
}

/*void gpio_set_trigger(Pin pin, TriggerMode trig) {
    __nop();
}*/

void gpio_set_trigger(Pin pin, TriggerMode trig) {
	// Sets the interrupt trigger for the specified pin.
	
	// The modes are as follows:
	//  - None:    Disable the trigger.
	//  - Rising:  Trigger on transition from logic low to
	//             high.
	//  - Falling: Trigger on transition from logic high to
	//             low.
	
	uint32_t pin_index = GET_PIN_INDEX(pin);
	
	switch(trig){
		case None:
			EXTI->IMR &= ~(1<<pin_index);
		break;
		case Rising:
			EXTI->IMR |= (1<<pin_index);
			EXTI->RTSR|= (1<<pin_index);
		break;
		case Falling:
			EXTI->IMR |= (1<<pin_index);
			EXTI->FTSR|= (1<<pin_index);
			EXTI->PR 	&= (0<<pin_index);
		break;
	}
}

/*
void gpio_set_callback(Pin pin, void (*callback)(int status)) {
    __nop();
}
*/

void gpio_set_callback(Pin pin, void (*callback)(int status)) {
	// Set up and enable the interrupt on the passed pin's
	// port.
	
	// The callback function should be stored in an internal
	// static function pointer.
	
	// When the port ISR is fired, the callback function
	// should be executed. The status parameter should equal
	// a mask of which pin triggered the interrupt, for
	// example if a callback is set on pin P1_2 (port 1, pin
	// 2) and the interrupt is triggered on port 1, the
	// function callback should be called with argument
	// status equalling 0b00000100.
	// This allows the user to determine the interrupt source
	// with (status & GET_PIN_INDEX(P1_2)).
	__enable_irq();
  IRQ_status = 0;
	IRQ_port_num = GET_PORT_INDEX(pin);
	IRQ_pin_index = GET_PIN_INDEX(pin);
	EXTI_port_set = IRQ_port_num<<(IRQ_pin_index % 4) * 4;
	
	GPIO_callback = callback;
	//Connect the pin to external interrupt line
	switch(IRQ_pin_index){
		case 0:
			//SYSCFG->EXTICR[0]|= EXTI_port_set;
			prioritygroup = NVIC_GetPriorityGrouping(); // will return 5
			priority = NVIC_EncodePriority(prioritygroup, 1, 1 ); // Pri=1 , SubPri=1
			NVIC_SetPriority(EXTI0_IRQn, priority);
		  NVIC_EnableIRQ(EXTI0_IRQn);		
		break;

		case 1:
			//SYSCFG->EXTICR[0]|= EXTI_port_set;
			prioritygroup = NVIC_GetPriorityGrouping(); // will return 5
			priority = NVIC_EncodePriority(prioritygroup, 1, 1 ); // Pri=1 , SubPri=1
			NVIC_SetPriority(EXTI1_IRQn, priority);
		  NVIC_EnableIRQ(EXTI1_IRQn);
		break;
		
    case 2:
			//SYSCFG->EXTICR[0]|= EXTI_port_set;
			prioritygroup = NVIC_GetPriorityGrouping(); // will return 5
			priority = NVIC_EncodePriority(prioritygroup, 1, 1 ); // Pri=1 , SubPri=1
			NVIC_SetPriority(EXTI2_IRQn, priority);
		  NVIC_EnableIRQ(EXTI2_IRQn);
		break;
		
		case 3:
			//SYSCFG->EXTICR[0]|= EXTI_port_set;
			prioritygroup = NVIC_GetPriorityGrouping(); // will return 5
			priority = NVIC_EncodePriority(prioritygroup, 1, 1 ); // Pri=1 , SubPri=1
			NVIC_SetPriority(EXTI3_IRQn, priority);
		  NVIC_EnableIRQ(EXTI3_IRQn);
		break;
		
		case 4:
			//SYSCFG->EXTICR[1]|= EXTI_port_set;
			prioritygroup = NVIC_GetPriorityGrouping(); // will return 5
			priority = NVIC_EncodePriority(prioritygroup, 1, 1 ); // Pri=1 , SubPri=1
			NVIC_SetPriority(EXTI4_IRQn, priority);
		  NVIC_EnableIRQ(EXTI4_IRQn);
		break;
		
		case 5:
    case 6:
		case 7:
//			SYSCFG->EXTICR[1]|= EXTI_port_set;
			prioritygroup = NVIC_GetPriorityGrouping(); // will return 5
			priority = NVIC_EncodePriority(prioritygroup, 1, 1 ); // Pri=1 , SubPri=1
			NVIC_SetPriority(EXTI9_5_IRQn, priority);
		  NVIC_EnableIRQ(EXTI9_5_IRQn);
		break;
		
		case 8:
		case 9:
	//		SYSCFG->EXTICR[2]|= EXTI_port_set;
			prioritygroup = NVIC_GetPriorityGrouping(); // will return 5
			priority = NVIC_EncodePriority(prioritygroup, 1, 1 ); // Pri=1 , SubPri=1
			NVIC_SetPriority(EXTI9_5_IRQn,3);
		  NVIC_EnableIRQ(EXTI9_5_IRQn);
		break;
			
		case 10:
		case 11:
		//	SYSCFG->EXTICR[2]|= EXTI_port_set;
			prioritygroup = NVIC_GetPriorityGrouping(); // will return 5
			priority = NVIC_EncodePriority(prioritygroup, 1, 1 ); // Pri=1 , SubPri=1
			NVIC_SetPriority(EXTI15_10_IRQn,3);
		  NVIC_EnableIRQ(EXTI15_10_IRQn);
		break;
		
		case 12:
		case 13:
    case 14:
		case 15:
			//SYSCFG->EXTICR[3]|= EXTI_port_set;
			prioritygroup = NVIC_GetPriorityGrouping(); // will return 5
			priority = NVIC_EncodePriority(prioritygroup, 1, 1 ); // Pri=1 , SubPri=1
			NVIC_SetPriority(EXTI15_10_IRQn,3);
		  NVIC_EnableIRQ(EXTI15_10_IRQn);
		break;
	}
}


//Note: only four interrupt lines are implemented i.e. only use pin 0-4

void EXTI0_IRQHandler(void){
	
	GPIO_TypeDef* p = ((GPIO_TypeDef*)(GPIOA_BASE + 0x0400 * IRQ_port_num));
	IRQ_status = EXTI->PR>>IRQ_pin_index & 1;		
	NVIC_ClearPendingIRQ(EXTI0_IRQn);
	EXTI->PR|=(1<<IRQ_pin_index);
	
	if(p->IDR&(1<<IRQ_pin_index)){
		GPIO_callback(IRQ_pin_index);
	}
}

void EXTI1_IRQHandler(void){
	
	GPIO_TypeDef* p = ((GPIO_TypeDef*)(GPIOA_BASE + 0x0400 * IRQ_port_num));
	IRQ_status = EXTI->PR>>IRQ_pin_index & 1;	
	NVIC_ClearPendingIRQ(EXTI1_IRQn);
	EXTI->PR|=(1<<IRQ_pin_index);
	
	if(p->IDR&(1<<IRQ_pin_index)){
		GPIO_callback(IRQ_pin_index);
	}
	
}

void EXTI2_IRQHandler(void){
	
	GPIO_TypeDef* p = ((GPIO_TypeDef*)(GPIOA_BASE + 0x0400 * IRQ_port_num));
	IRQ_status = EXTI->PR>>IRQ_pin_index & 1;	
	NVIC_ClearPendingIRQ(EXTI2_IRQn);
	EXTI->PR|=(1<<IRQ_pin_index);
	
	if(p->IDR&(1<<IRQ_pin_index)){
		GPIO_callback(IRQ_pin_index);
	}
	
}

void EXTI3_IRQHandler(void){
	
	GPIO_TypeDef* p = ((GPIO_TypeDef*)(GPIOA_BASE + 0x0400 * IRQ_port_num));
	IRQ_status = EXTI->PR>>IRQ_pin_index & 1;	
	NVIC_ClearPendingIRQ(EXTI3_IRQn);
	EXTI->PR|=(1<<IRQ_pin_index);
	
	if(p->IDR&(1<<IRQ_pin_index)){
		GPIO_callback(IRQ_pin_index);
	}
	
}

void EXTI4_IRQHandler(void){
	
	GPIO_TypeDef* p = ((GPIO_TypeDef*)(GPIOA_BASE + 0x0400 * IRQ_port_num));
	IRQ_status = EXTI->PR>>IRQ_pin_index & 1;
	NVIC_ClearPendingIRQ(EXTI4_IRQn);
	EXTI->PR|=(1<<IRQ_pin_index);
	
	if(p->IDR&(1<<IRQ_pin_index)){
		GPIO_callback(IRQ_pin_index);
	}
}

void EXTI9_5_IRQHandler(void){
	
	GPIO_TypeDef* p = ((GPIO_TypeDef*)(GPIOA_BASE + 0x0400 * IRQ_port_num));
	IRQ_status = (EXTI->PR>>IRQ_pin_index) & 1;
	NVIC_ClearPendingIRQ(EXTI9_5_IRQn);
	EXTI->PR|=(1<<IRQ_pin_index);
	
	if(p->IDR&(1<<IRQ_pin_index)){
		GPIO_callback(IRQ_pin_index);
	}
}


void EXTI15_10_IRQHandler(void){
	
	GPIO_TypeDef* p = ((GPIO_TypeDef*)(GPIOA_BASE + 0x0400 * IRQ_port_num));
	IRQ_status = (EXTI->PR>>IRQ_pin_index) & 1;
	NVIC_ClearPendingIRQ(EXTI15_10_IRQn);
	EXTI->PR|=(1<<IRQ_pin_index);
	
	if(p->IDR&(1<<IRQ_pin_index)){
		GPIO_callback(IRQ_pin_index);
	}
	
}
// *******************************ARM University Program Copyright ? ARM Ltd 2016*************************************

参考文章

Yngz_Miao,【STM32】GPIO工作原理(八种工作方式超详细分析,附电路图),https://blog.csdn.net/qq_38410730/article/details/79858906

  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2022-05-18 17:49:03  更:2022-05-18 17:50:35 
 
开发: 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 0:57:41-

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