PCA9555详细学习及代码
本文参考
https://blog.csdn.net/TheOneZn/article/details/122723644
https://blog.csdn.net/LH_SMD/article/details/121354625
PCA9555芯片介绍
PCA9555是一款16位通用输入/输出(GPIO)扩展器,具有中断和弱上拉电阻,适用于I2c总线/SMBus应用。它具有以下特点:
1.PCA9555由两个8位配置(输入或输出选择),输入端口,输出端口和极性反转(高电平有效或低电平有效运行)寄存器组成。
2.在加电时I /O被配置为输入,系统主控制器可以通过写入I /O配置位将I /O启动为输入或输出。
3.每一个输入或者输出的数据被保存在相应的输入或者输出寄存器内。
4.输入端口寄存器的极性可借助极性反转寄存器进行转换。
5.所有寄存器都可由系统主控器读取。
芯片引脚及功能描述:
PCA9555详细学习
1.INT引脚功能:当任何输入状态与其对应的状态不同时,PCA9555开漏中断(INT)引脚输出(只对被配置为输入模式的引脚产生反应),用于向单片机指示输入状态已更改。INT可以连接到微控制器的中断输入端,通过在这条线发送中断信号给单片机,如果端口上有传入数据,远程I/O可以通知微控制器,而不必通过通信I2C总线。因此PCA9555可以保持一个简单的从设备。
2.PCA9555芯片采用iic的方式进行通信,其地址如下:
其前四位为固定位,后三位A0,A1,A2为可编程位,用于设置地址。从地址的最后一位定义了要执行的操作(读或写)。高(1)表示读操作,低(0)表示写操作。
3.控制寄存器和命令字节
当地址位正确应答之后,主机应该发送一个控制字节,控制字节将会存PCA9555的控制寄存器中,其中B0,B1,B2三位,将会定义操作以及内部的寄存器(输入、输出、极性反转或配置),控制寄存器能被读以及写,控制字节只有写的时候才发送。一旦发送了一个命令字节,被寻址的寄存器将继续被读访问,直到已发送新的命令字节。command其实就相当于相应的控制寄存器地址,比如000就是访问input port0的寄存器
4.PCA9555芯片具有四种内部寄存器:
输入寄存器:输入端口寄存器(寄存器0和1)反映引脚的传入逻辑电平,而不管是否
引脚由配置寄存器定义为输入或输出。它只作用于读操作。写入这些寄存器没有作用。默认值X由外部应用的逻辑级别决定。在进行读操作之前,发送一个带有命令字节的写传输,以指示I2C设备接下来将访问输入端口寄存器。
输出寄存器:输出端口寄存器(寄存器2和3)显示定义为输出的引脚的传出逻辑电
配置寄存器。这个寄存器中的位值对定义为输入的引脚没有影响。反过来,从这里读寄存器反映控制输出选择的触发器中的值,而不是实际引脚值。
极性反转寄存器:极性反转寄存器(寄存器4和5)允许定义为输入的引脚极性反转
配置寄存器。向该寄存器里某一位写入1,表明该引脚在输入状态的时候输入极性会反转,写入0则不起作用。
配置寄存器:配置寄存器(寄存器6和7)配置I/O引脚的方向。如果这个寄存器中的位是设置为1,对应的端口引脚作为高阻抗输出驱动器的输入启用。如果在这一点寄存器清除为0,相应的端口引脚作为输出启用。
5.通过上面的介绍,PCA9555的功能应该有所了解了,下面来看下然后使用这个芯片:
例1:主机发送从设备地址,从设备地址匹配,从设备发送ack,主机接收应答后发生命令字节,比如发送0x02,等待从机应答后,即可向输出寄存器写入数据改变拓展IO引脚输出。如果命令直接写入的配置寄存器的地址,则可配置IO口的输入输出状态。(在写入输出状态前因先将IO口通过配置寄存器设置为输出状态)
例2:主机发送从设备地址(主要此时起始信号为读写位为写状态,因为等下得得写入命令字节),从设备地址匹配,从设备发送ack,主机发送命令字节,接收应答后,重新发送设备地址(此时起始信号为读写位为读取状态),等待应答主机即可读取从机发送过来得数据。
PCA9555代码实例
头文件:
#ifndef USER_APP_PCA9555_H_
#define USER_APP_PCA9555_H_
#include "stm32f4xx_ll_gpio.h"
/***************************************************IIC 驱动部门**********************************************************/
#define IIC_SCL_GPIO_PORT GPIOB
#define IIC_SCL_GPIO_PIN LL_GPIO_PIN_7
#define IIC_SDA_GPIO_PORT GPIOB
#define IIC_SDA_GPIO_PIN LL_GPIO_PIN_8
#define IIC_SCL_SET_GPIO_OUTPUT_STATUS(status) if(status == 1) LL_GPIO_SetOutputPin(IIC_SCL_GPIO_PORT, IIC_SCL_GPIO_PIN);\
else if(status == 0) LL_GPIO_ResetOutputPin(IIC_SCL_GPIO_PORT,IIC_SCL_GPIO_PIN);
#define IIC_SDA_SET_GPIO_OUTPUT_STATUS(status) if(status == 1) LL_GPIO_SetOutputPin(IIC_SDA_GPIO_PORT, IIC_SDA_GPIO_PIN);\
else if(status == 0) LL_GPIO_ResetOutputPin(IIC_SDA_GPIO_PORT, IIC_SDA_GPIO_PIN);
#define IIC_SDA_GET_GPIO_INPUT_STATUS LL_GPIO_IsInputPinSet(IIC_SDA_GPIO_PORT, IIC_SDA_GPIO_PIN)
void IIC_gpio_init(void);
void IIC_start(void);
void IIC_stop(void);
uint8_t IIC_wait_ack(void);
void IIC_ack(void);
void IIC_nack(void);
void IIC_send_byte(uint8_t txd);
void IIC_send_byte(uint8_t txd);
uint8_t IIC_read_byte(unsigned char ack);
/***********************************************************IIC 驱动 END**********************************************************************/
/************************************************************PCA9555 驱动***********************************************************/
#define SUCCESS 0
#define ERROR 1
#define SLAVE_ADDR0 0x40
#define HOST_WRITE_COMMAND 0x00
#define HOST_READ_COMMAND 0x01
#define INPUT_PORT_REGISTER0 0x00 /* 输入端口寄存器0,负责IO00-IO07 */
#define INPUT_PORT_REGISTER1 0x01 /* 输入端口寄存器1,负责IO10-IO17 */
#define OUTPUT_PORT_REGISTER0 0x02 /* 输入端口寄存器0,负责IO00-IO07 */
#define OUTPUT_PORT_REGISTER1 0x03 /* 输入端口寄存器1,负责IO10-IO17 */
#define POLARITY_INVERSION_PORT_REGISTER0 0x04 /* 极性反转端口寄存器0,负责IO00-IO07 */
#define POLARITY_INVERSION_PORT_REGISTER1 0x05 /* 极性反转端口寄存器1,负责IO10-IO17 */
#define CONFIG_PORT_REGISTER0 0x06 /* 输入端口寄存器0,负责IO00-IO07 */
#define CONFIG_PORT_REGISTER1 0x07 /* 输入端口寄存器1,负责IO10-IO17 */
#define GPIO_PORT0 0
#define GPIO_PORT1 1
#define GPIO_0 0x01
#define GPIO_1 0x02
#define GPIO_2 0x04
#define GPIO_3 0x08
#define GPIO_4 0x10
#define GPIO_5 0x20
#define GPIO_6 0x40
#define GPIO_7 0x80
#define PCA9555_WAIT_IS_RETURN_SUCCESS(flag, tips) if(flag != SUCCESS)\
{ \
printf("%s", tips); \
return ERROR;\
}
void pca9555_init(void);
void pca9555_read_wtite_test(void);
void pca9555_set_output_mode_all(uint8_t slave_num, uint8_t gpio_port);
void pca9555_set_output_mode(uint8_t slave_num, uint8_t gpio_port, uint8_t gpio_num);
void pca9555_set_input_mode_all(uint8_t slave_num, uint8_t gpio_port);
void pca9555_set_gpio_output_status(uint8_t slave_num, uint8_t gpio_port, uint8_t gpio_num, uint8_t status);
void pca9555_set_input_mode(uint8_t slave_num, uint8_t gpio_port, uint8_t gpio_num);
void pca9555_set_input_mode_all(uint8_t slave_num, uint8_t gpio_port);
void pca9555_set_output_mode_all(uint8_t slave_num, uint8_t gpio_port);
uint8_t pca9555_get_gpio_status(uint8_t slave_num, uint8_t gpio_port, uint8_t gpio_num);
uint8_t pca9555_read_byte(uint8_t slave_num, uint8_t addr, uint8_t read_register_data, uint8_t *read_data);
uint8_t pca9555_write_byte(uint8_t addr, uint8_t command, uint8_t write_register_data);
#endif /* USER_APP_PCA9555_H_ */
源文件:
#include "hardware/pca9555.h"
#include "main.h"
#include "hardware/timer.h"
#include <stdio.h>
void IIC_gpio_init(void)
{
LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOC); /* 使能GPIO时钟 */
LL_GPIO_SetPinMode(IIC_SCL_GPIO_PORT, IIC_SCL_GPIO_PIN, LL_GPIO_MODE_OUTPUT);//设置为推挽输出,初始化高电平
LL_GPIO_SetPinOutputType(IIC_SCL_GPIO_PORT, IIC_SCL_GPIO_PIN,LL_GPIO_OUTPUT_PUSHPULL);//设置为推挽输出
LL_GPIO_SetOutputPin(IIC_SCL_GPIO_PORT, IIC_SCL_GPIO_PIN);
LL_GPIO_SetPinMode(IIC_SDA_GPIO_PORT, IIC_SDA_GPIO_PIN, LL_GPIO_MODE_OUTPUT);//设置为推挽输出,初始化高电平
LL_GPIO_SetPinOutputType(IIC_SDA_GPIO_PORT, IIC_SDA_GPIO_PIN,LL_GPIO_OUTPUT_PUSHPULL);
LL_GPIO_SetOutputPin(IIC_SDA_GPIO_PORT, IIC_SDA_GPIO_PIN);
}
static void IIC_SDA_set_output(void)
{
LL_GPIO_SetPinMode(IIC_SDA_GPIO_PORT, IIC_SDA_GPIO_PIN, LL_GPIO_MODE_OUTPUT);
LL_GPIO_SetPinOutputType(IIC_SDA_GPIO_PORT, IIC_SDA_GPIO_PIN, LL_GPIO_OUTPUT_PUSHPULL);
LL_GPIO_SetOutputPin(IIC_SDA_GPIO_PORT, IIC_SDA_GPIO_PIN);
}
static void IIC_SDA_set_input(void)
{
LL_GPIO_SetPinMode(IIC_SDA_GPIO_PORT, IIC_SDA_GPIO_PIN, LL_GPIO_MODE_INPUT);
LL_GPIO_SetPinPull(IIC_SDA_GPIO_PORT, IIC_SDA_GPIO_PIN, LL_GPIO_PULL_UP);
}
//产生IIC起始信号
void IIC_start(void)
{
IIC_SDA_set_output(); //sda线输出
IIC_SDA_SET_GPIO_OUTPUT_STATUS(1);
IIC_SCL_SET_GPIO_OUTPUT_STATUS(1);
//sl_udelay_wait(6);
delay_us(6);
IIC_SDA_SET_GPIO_OUTPUT_STATUS(0);//START:when CLK is high,DATA change form high to low
//sl_udelay_wait(6);
delay_us(6);
IIC_SCL_SET_GPIO_OUTPUT_STATUS(0);//钳住I2C总线,准备发送或接收数据
}
//产生IIC停止信号
void IIC_stop(void)
{
IIC_SDA_set_output();//sda线输出
IIC_SCL_SET_GPIO_OUTPUT_STATUS(0);
IIC_SDA_SET_GPIO_OUTPUT_STATUS(0);//STOP:when CLK is high DATA change form low to high
//sl_udelay_wait(6);
delay_us(6);
IIC_SCL_SET_GPIO_OUTPUT_STATUS(1);
IIC_SDA_SET_GPIO_OUTPUT_STATUS(1);//发送I2C总线结束信号
//sl_udelay_wait(6);
delay_us(6);
}
//等待应答信号到来
//返回值:1,接收应答失败
// 0,接收应答成功
uint8_t IIC_wait_ack(void)
{
uint8_t ucErrTime=0;
IIC_SDA_set_input(); //SDA设置为输入
IIC_SDA_SET_GPIO_OUTPUT_STATUS(1);
//sl_udelay_wait(1);
delay_us(1);
IIC_SCL_SET_GPIO_OUTPUT_STATUS(1);
//sl_udelay_wait(1);
delay_us(1);
while(IIC_SDA_GET_GPIO_INPUT_STATUS)
{
ucErrTime++;
//sl_udelay_wait(1);
delay_us(1);
if(ucErrTime>250)
{
IIC_stop();
printf("return 1\r\n");
return 1;
}
}
IIC_SCL_SET_GPIO_OUTPUT_STATUS(0);//时钟输出0
return 0;
}
//产生ACK应答
void IIC_ack(void)
{
IIC_SCL_SET_GPIO_OUTPUT_STATUS(0);
IIC_SDA_set_output();
IIC_SDA_SET_GPIO_OUTPUT_STATUS(1);
//sl_udelay_wait(5);
delay_us(5);
IIC_SCL_SET_GPIO_OUTPUT_STATUS(1);
//sl_udelay_wait(5);
delay_us(5);
IIC_SCL_SET_GPIO_OUTPUT_STATUS(0);
}
//不产生ACK应答
void IIC_nack(void)
{
IIC_SCL_SET_GPIO_OUTPUT_STATUS(0);
IIC_SDA_set_output();
IIC_SDA_SET_GPIO_OUTPUT_STATUS(1);
//sl_udelay_wait(5);
delay_us(5);
IIC_SCL_SET_GPIO_OUTPUT_STATUS(1);
//sl_udelay_wait(5);
delay_us(5);
IIC_SCL_SET_GPIO_OUTPUT_STATUS(0);
}
//IIC发送一个字节(高位先发)
//返回从机有无应答
//1,有应答
//0,无应答
void IIC_send_byte(uint8_t txd)
{
uint8_t t;
IIC_SDA_set_output();
IIC_SCL_SET_GPIO_OUTPUT_STATUS(0);//拉低时钟开始数据传输
for(t=0;t<8;t++)
{
//printf("\r\n1 GPIO_PinInGet(IIC_SDA_GPIO_PORT, IIC_SDA_GPIO_PIN) = %d\r\n", GPIO_PinInGet(IIC_SDA_GPIO_PORT, IIC_SDA_GPIO_PIN));
IIC_SDA_SET_GPIO_OUTPUT_STATUS((txd&0x80)>>7);
txd<<=1;
delay_us(4);
IIC_SCL_SET_GPIO_OUTPUT_STATUS(1);
delay_us(4);
IIC_SCL_SET_GPIO_OUTPUT_STATUS(0);
delay_us(4);
}
}
//读1个字节,ack=1时,发送ACK,ack=0,发送nACK(高位先接收)
uint8_t IIC_read_byte(unsigned char ack)
{
unsigned char i,receive=0;
IIC_SDA_set_input();//SDA设置为输入
for(i=0;i<8;i++)
{
IIC_SCL_SET_GPIO_OUTPUT_STATUS(0);
delay_us(4);
IIC_SCL_SET_GPIO_OUTPUT_STATUS(1);
receive<<=1;
if(IIC_SDA_GET_GPIO_INPUT_STATUS)receive++;
//printf("\r\n2 GPIO_PinInGet(IIC_SDA_GPIO_PORT, IIC_SDA_GPIO_PIN) = %d\r\n", GPIO_PinInGet(IIC_SDA_GPIO_PORT, IIC_SDA_GPIO_PIN));
delay_us(4);
}
if (!ack)
IIC_nack();//发送nACK
else
IIC_ack(); //发送ACK
return receive;
}
void pca9555_init(void)
{
IIC_gpio_init();
}
//pca9555写寄存器值
uint8_t pca9555_write_byte(uint8_t addr, uint8_t command, uint8_t write_register_data)
{
uint8_t ret = 1;
IIC_start();
IIC_send_byte(addr); //写从机地址
ret = IIC_wait_ack();
PCA9555_WAIT_IS_RETURN_SUCCESS(ret, "[ERROR] wait slave ack error!\r\n");
IIC_send_byte(command); //写要写的寄存器地址
ret = IIC_wait_ack();
PCA9555_WAIT_IS_RETURN_SUCCESS(ret, "[ERROR] wait slave ack error!\r\n");
IIC_send_byte(write_register_data);//写入数据
ret = IIC_wait_ack();
PCA9555_WAIT_IS_RETURN_SUCCESS(ret, "[ERROR] wait slave ack error!\r\n");
IIC_stop();
delay_us(10000);
return SUCCESS;
}
/*
* pca9555读取寄存器值
*
* addr 读取地址
* read_register_data 要读取的寄存器
* read_data 读取数据存放地址
*
*
* 返回值:读取成功返回SUCCESS 失败返回ERROR
*
* */
uint8_t pca9555_read_byte(uint8_t slave_num, uint8_t addr, uint8_t read_register_data, uint8_t *read_data)
{
uint8_t ret = 0;
IIC_start();
IIC_send_byte(slave_num); // 写入从机地址
ret = IIC_wait_ack();
PCA9555_WAIT_IS_RETURN_SUCCESS(ret, "[ERROR] wait slave ack error!\r\n");
IIC_send_byte(read_register_data); //写入要读取的寄存器地址
ret = IIC_wait_ack();
PCA9555_WAIT_IS_RETURN_SUCCESS(ret, "[ERROR] wait slave ack error!\r\n");
IIC_start(); /* 开始接收数据 */
IIC_send_byte(addr); //发送从机地址并设置为读取
ret = IIC_wait_ack();
PCA9555_WAIT_IS_RETURN_SUCCESS(ret, "[ERROR] wait slave ack error!\r\n");
*read_data = IIC_read_byte(0);
IIC_stop();
return SUCCESS;
}
/*
* 设置指定GPIO的模式
*
* slave_num 需要操作的从机设备
* gpio_port gpio端口 端口0/1
* gpio_num 哪一个GPIO
*
* 返回值:void
* */
void pca9555_set_output_mode(uint8_t slave_num, uint8_t gpio_port, uint8_t gpio_num)
{
uint8_t register_original_data = 0;
if(gpio_port > 1 || gpio_num > 0x80) return;
if(gpio_port == 0)
{
pca9555_read_byte(slave_num, slave_num | HOST_READ_COMMAND, CONFIG_PORT_REGISTER0, ®ister_original_data);//读取原来的设置
pca9555_write_byte(slave_num | HOST_WRITE_COMMAND, CONFIG_PORT_REGISTER0, register_original_data & (~gpio_num));//在不影响原来设置的情况下修改配置寄存器的设置
}
else if(gpio_port == 1)
{
pca9555_read_byte(slave_num, slave_num | HOST_READ_COMMAND, CONFIG_PORT_REGISTER1, ®ister_original_data);
pca9555_write_byte( slave_num | HOST_WRITE_COMMAND, CONFIG_PORT_REGISTER1, register_original_data & (~gpio_num));
}
}
void pca9555_set_output_mode_all(uint8_t slave_num, uint8_t gpio_port)
{
uint8_t register_original_data = 0x00;
if(gpio_port > 1) return;
if(gpio_port == 0)
{
pca9555_write_byte(slave_num | HOST_WRITE_COMMAND, CONFIG_PORT_REGISTER0, register_original_data);
}
else if(gpio_port == 1)
{
pca9555_write_byte( slave_num | HOST_WRITE_COMMAND, CONFIG_PORT_REGISTER1, register_original_data);
}
}
/*
* 设置GPIO为输入模式
*
* slave_num 需要操作的从机设备
* gpio_port gpio端口 端口0/1
* gpio_num 哪一个GPIO
*
* 返回值:void
**/
void pca9555_set_input_mode(uint8_t slave_num, uint8_t gpio_port, uint8_t gpio_num)
{
uint8_t register_original_data = 0;
if(gpio_port > 1 || gpio_num > 0x80) return;
if(gpio_port == 0)
{
pca9555_read_byte(slave_num, slave_num | HOST_READ_COMMAND, CONFIG_PORT_REGISTER0, ®ister_original_data);
pca9555_write_byte(slave_num | HOST_WRITE_COMMAND, CONFIG_PORT_REGISTER0, register_original_data | gpio_num);
}
else if(gpio_port == 1)
{
pca9555_read_byte(slave_num, slave_num | HOST_READ_COMMAND, CONFIG_PORT_REGISTER1, ®ister_original_data);
pca9555_write_byte(slave_num | HOST_WRITE_COMMAND, CONFIG_PORT_REGISTER1, register_original_data | gpio_num);//参考的文章里给的代码这里使用的是&,但其实这里应该用|才对啊,不然另外7个全变0了
}
}
void pca9555_set_input_mode_all(uint8_t slave_num, uint8_t gpio_port)
{
uint8_t register_original_data = 0xff;
if(gpio_port > 1 ) return;
if(gpio_port == 0)
{
pca9555_write_byte(slave_num | HOST_WRITE_COMMAND, CONFIG_PORT_REGISTER0, register_original_data);
}
else if(gpio_port == 1)
{
pca9555_write_byte(slave_num | HOST_WRITE_COMMAND, CONFIG_PORT_REGISTER1, register_original_data);
}
}
/*
* 设置GPIO输出状态
*
* slave_num 需要操作的从机设备
* gpio_port gpio端口 端口0/1
* gpio_num 哪一个GPIO
* status 输出状态
*
* 返回值:void
**/
void pca9555_set_gpio_output_status(uint8_t slave_num, uint8_t gpio_port, uint8_t gpio_num, uint8_t status)
{
uint8_t register_original_data = 0;
if(gpio_port > 1 || gpio_num > 0x80) return;
if(gpio_port == 0)
{
pca9555_read_byte(slave_num, slave_num | HOST_READ_COMMAND, OUTPUT_PORT_REGISTER0, ®ister_original_data);
if(status == 1)
{
pca9555_write_byte(slave_num | HOST_WRITE_COMMAND, OUTPUT_PORT_REGISTER0, register_original_data | gpio_num);
}
else
{
pca9555_write_byte(slave_num | HOST_WRITE_COMMAND, OUTPUT_PORT_REGISTER0, register_original_data & (~gpio_num));
}
}
else if(gpio_port == 1)
{
pca9555_read_byte(slave_num, slave_num | HOST_READ_COMMAND, OUTPUT_PORT_REGISTER1, ®ister_original_data);
if(status == 1)
{
pca9555_write_byte(slave_num | HOST_WRITE_COMMAND, OUTPUT_PORT_REGISTER1, register_original_data | gpio_num);
}
else
{
pca9555_write_byte(slave_num | HOST_WRITE_COMMAND, OUTPUT_PORT_REGISTER1, register_original_data & (~gpio_num));
}
}
}
/*
* 获取GPIO状态
*
* slave_num 需要操作的从机设备
* gpio_port gpio端口 端口0/1
* gpio_num 哪一个GPIO
*
* 返回值:GPIO状态
**/
uint8_t pca9555_get_gpio_status(uint8_t slave_num, uint8_t gpio_port, uint8_t gpio_num)
{
uint8_t register_original_data = 0;
uint8_t gpio_status = 0;
if(gpio_port > 1 || gpio_num > 0x80)
{
printf("[ERROR] gpio_port > 1 || gpio_num > 0x80\r\n");
return 2;
}
if(gpio_port == 0)
{
pca9555_read_byte(slave_num, slave_num | HOST_READ_COMMAND, INPUT_PORT_REGISTER0, ®ister_original_data);
}
else if(gpio_port == 1)
{
pca9555_read_byte(slave_num, slave_num | HOST_READ_COMMAND, INPUT_PORT_REGISTER1, ®ister_original_data);
}
switch(gpio_num)
{
case 0x01: gpio_status = register_original_data & gpio_num;break;
case 0x02: gpio_status = (register_original_data & gpio_num) >> 1;break;
case 0x04: gpio_status = (register_original_data & gpio_num) >> 2;break;
case 0x08: gpio_status = (register_original_data & gpio_num) >> 3;break;
case 0x10: gpio_status = (register_original_data & gpio_num) >> 4;break;
case 0x20: gpio_status = (register_original_data & gpio_num) >> 5;break;
case 0x40: gpio_status = (register_original_data & gpio_num) >> 6;break;
case 0x80: gpio_status = (register_original_data & gpio_num) >> 7;break;
default: printf("[ERROR] gpio error!\r\n");
}
return gpio_status;
}
注:
PCA9555采用IIC通信,协议中涉及的延时函数务必保证精确。
上面代码可直接运行在STM32单片机中,只需要对以下两部点进行修改即可
①GPIO输入输出设置
②精确到us级别的延时函数