如何快速更改GPIO输入输出方向

采用宏定义#defind更改

#define SDA_IN() {GPIOC->CRH&=0XFFFF0FFF;GPIOC->CRH|=8<<12;}
#define SDA_OUT() {GPIOC->CRH&=0XFFFF0FFF;GPIOC->CRH|=3<<12;}

   	   		   
//IO方向设置   先用与(&)对11脚的四个位清零,再用或(|)置1,
#define SDA_IN()  {GPIOC->CRH&=0XFFFF0FFF;GPIOC->CRH|=8<<12;}//改变Pin11对应位改成1000,上或下拉输入,具体看该引脚初始化的状态是上拉还是下拉
#define SDA_OUT() {GPIOC->CRH&=0XFFFF0FFF;GPIOC->CRH|=3<<12;}//改变Pin11对应位改成0011 ,推挽输出
//							先用与(&)对11脚的四个位清零,再用或(1)置1,
//IO操作函数	 
#define IIC_SCL    PCout(12) //SCL
#define IIC_SDA    PCout(11) //SDA	 
#define READ_SDA   PCin(11)  //输入SDA 

使用的时候就像调用函数一样
SDA_OUT(); //SDA设置为输出
SDA_IN();//SDA设置为输入
如:

//IIC发送一个字节
//返回从机有无应答
//1,有应答
//0,无应答			  
void IIC_Send_Byte(u8 txd)
{                        
    u8 t;   
	  SDA_OUT();  //SDA设置为输出 
    IIC_SCL=0;//拉低时钟开始数据传输
    for(t=0;t<8;t++)
    {              
        IIC_SDA=(txd&0x80)>>7;
        txd<<=1; 	  
				delay_us(2);   //对TEA5767这三个延时都是必须的
				IIC_SCL=1;
				delay_us(2); 
				IIC_SCL=0;	
				delay_us(2);
    }	 
} 	    
//读1个字节,ack=1时,发送ACK,ack=0,发送nACK   
u8 IIC_Read_Byte(unsigned char ack)
{
	unsigned char i,receive=0;
	SDA_IN();//SDA设置为输入
    for(i=0;i<8;i++ )
	{
        IIC_SCL=0; 
        delay_us(2);
		    IIC_SCL=1;//IIC_SCL为高电平期间来读信号
        receive<<=1;
        if(READ_SDA)receive++;   
	    	delay_us(1); 
    }					 
    if (!ack)
        IIC_NAck();//发送nACK
    else
        IIC_Ack(); //发送ACK   
    return receive;
}

分析原理:
一组I/O口有16个引脚,控制I/O口输入、输出及输出速度的两个寄存器:
GPIOx_CRL(x=A…E),控制pin0—pin7
GPIOx_CRH(x=A…E),控制pin8—pin15
在这里插入图片描述
在这里插入图片描述
一个引脚由四个位(CNF1、CNF0,MODE1、MODE0)控制,而CRL,CRH都是32位寄存器,所有位都用上,刚好每个寄存器控制8个引脚。控制某个引脚的四个位的配置可看下图
端口位配置表:
在这里插入图片描述

如何来根据此表,用代码来配置寄存器?
先看懂这两张表:
1)先确定引脚的输入、输出。
输出:
【MODE1、MODE0】这两位为二进制的其他组合形式【0、1】,【1,0】【1、1】
输入:
【MODE1、MODE0】两位为【0、0】
2)再确定具体哪种输入、输出
在输出模式下再看CNF1和CNF0
在这里插入图片描述
在输入模式下如果CNF1和CNF0
在这里插入图片描述
如果想改变输出速度就更改MOD【1:0】
在这里插入图片描述

有没有发现,当为输入模式时,CNF1和CNF0为1,0,代表上拉和下拉,那具体的上拉输入还是下拉呢,看右边部分PxODR寄存器,通过控制对应引脚的输出0还是1来确定在这里插入图片描述
看代码:

void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct)
{
  uint32_t currentmode = 0x00, currentpin = 0x00, pinpos = 0x00, pos = 0x00;
  uint32_t tmpreg = 0x00, pinmask = 0x00;
  /* Check the parameters */
  assert_param(IS_GPIO_ALL_PERIPH(GPIOx));
  assert_param(IS_GPIO_MODE(GPIO_InitStruct->GPIO_Mode));
  assert_param(IS_GPIO_PIN(GPIO_InitStruct->GPIO_Pin));  
  
/*---------------------------- GPIO Mode Configuration -----------------------*/
  currentmode = ((uint32_t)GPIO_InitStruct->GPIO_Mode) & ((uint32_t)0x0F);
  if ((((uint32_t)GPIO_InitStruct->GPIO_Mode) & ((uint32_t)0x10)) != 0x00)
  { 
    /* Check the parameters */
    assert_param(IS_GPIO_SPEED(GPIO_InitStruct->GPIO_Speed));
    /* Output mode */
    currentmode |= (uint32_t)GPIO_InitStruct->GPIO_Speed;
  }
/*---------------------------- GPIO CRL Configuration ------------------------*/
  /* Configure the eight low port pins */
  if (((uint32_t)GPIO_InitStruct->GPIO_Pin & ((uint32_t)0x00FF)) != 0x00)
  {
    tmpreg = GPIOx->CRL;
    for (pinpos = 0x00; pinpos < 0x08; pinpos++)
    {
      pos = ((uint32_t)0x01) << pinpos;
      /* Get the port pins position */
      currentpin = (GPIO_InitStruct->GPIO_Pin) & pos;
      if (currentpin == pos)
      {
        pos = pinpos << 2;
        /* Clear the corresponding low control register bits */
        pinmask = ((uint32_t)0x0F) << pos;
        tmpreg &= ~pinmask;
        /* Write the mode configuration in the corresponding bits */
        tmpreg |= (currentmode << pos);
        /* Reset the corresponding ODR bit */
        if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPD)
        {
          GPIOx->BRR = (((uint32_t)0x01) << pinpos);
        }
        else
        {
          /* Set the corresponding ODR bit */
          if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPU)
          {
            GPIOx->BSRR = (((uint32_t)0x01) << pinpos);
          }
        }
      }
    }
    GPIOx->CRL = tmpreg;
  }
/*---------------------------- GPIO CRH Configuration ------------------------*/
  /* Configure the eight high port pins */
  if (GPIO_InitStruct->GPIO_Pin > 0x00FF)
  {
    tmpreg = GPIOx->CRH;
    for (pinpos = 0x00; pinpos < 0x08; pinpos++)
    {
      pos = (((uint32_t)0x01) << (pinpos + 0x08));
      /* Get the port pins position */
      currentpin = ((GPIO_InitStruct->GPIO_Pin) & pos);
      if (currentpin == pos)
      {
        pos = pinpos << 2;
        /* Clear the corresponding high control register bits */
        pinmask = ((uint32_t)0x0F) << pos;
        tmpreg &= ~pinmask;
        /* Write the mode configuration in the corresponding bits */
        tmpreg |= (currentmode << pos);
        /* Reset the corresponding ODR bit */
        if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPD)
        {
          GPIOx->BRR = (((uint32_t)0x01) << (pinpos + 0x08));
        }
        /* Set the corresponding ODR bit */
        if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPU)
        {
          GPIOx->BSRR = (((uint32_t)0x01) << (pinpos + 0x08));
        }
      }
    }
    GPIOx->CRH = tmpreg;
  }
}

其中判断是上拉还是下拉的代码为:
在这里插入图片描述
在这里插入图片描述
看此段代码前需知道以下信息:
一个枚举,用typedef取别名为GPIOMode_TypeDef
在这里插入图片描述
一个结构体,用typedef取别名为GPIO_InitTypeDef
在这里插入图片描述
使用该函数时:
先用别名定义结构体变量GPIO_InitStructure,把引脚,速度和模式确定好,再传给GPIO_Init函数的第二个参数,因为其是指向该结构体的指针,所以记得前面加&,取地址给该指针,然后函数内部通过指针作初始化处理。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

随便说下第一个参数GPIOX是什么:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
GPIOX是基地址+偏移地址得到的地址,然后通过指向GPIO_TypeDef结构体类型的指针强制转换。得到宏定义标识符GPIOX。GPIOX就是指向该类结构体的指针了,可以通过该指针读写结构体成员,所以有:
在这里插入图片描述
再来看看箭头指向的这两个寄存器BSRR和BRR
在这里插入图片描述
BSRR寄存器的低16位和高16位都是控制某个引脚电平状态的,对任何位写0不产生影响,对下面一排(低16位)的引脚位写1置1,对上面一排(高16位)的引脚位写1置0。
如果只用这一个寄存器来置位,如PB1
GPIOB->BSRR = (uint16_t)0x0002;//PB1置1
GPIOB->BSRR = (uint32_t)0x0002 << 16u;//PB1置0,先转换位32位的,然后将数据移动到高位,这样GPIO_PIN_1 代表的数据((uint16_t)0x0002)可以不用修改。否则是这样的GPIOB->BSRR =0x00020000;
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

BRR寄存器高16位保留,低16位(下面那排)对应引脚写1置0,同样对任何位写0不产生影响。

结合BRR和BSRR两寄存器置位更加方便简洁,如PB1
GPIOx->BSRR = 0x0002;//PB1置1
GPIOx->BRR = 0x0002;//PB1置0
或者
GPIOx->BSRR = GPIO_Pin_1;//PB1置1
GPIOx->BRR = GPIO_Pin_1;///PB1置0
如这个GPIO_WriteBit函数
在这里插入图片描述
现在从上面的发散思维回来,看看更改GPIO输入输出方向的代码就知道其原理了

IO方向设置 先用与(&)对11脚的四个位清零,再用或(|)置1
**#define SDA_IN() {GPIOC->CRH&=0XFFFF0FFF;GPIOC->CRH|=8<<12;**改变Pin11对应位改成1000,上或下拉输入
在这里插入图片描述

在这里插入图片描述

**#define SDA_OUT() {GPIOC->CRH&=0XFFFF0FFF;GPIOC->CRH|=3<<12;**改变Pin11对应位改成0011 ,推挽输出

在这里插入图片描述
在这里插入图片描述

©️2020 CSDN 皮肤主题: 数字20 设计师:CSDN官方博客 返回首页