欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2022-03-13 17:33:06
...

采用宏定义#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
如何快速更改GPIO输入输出方向
如何快速更改GPIO输入输出方向
一个引脚由四个位(CNF1、CNF0,MODE1、MODE0)控制,而CRL,CRH都是32位寄存器,所有位都用上,刚好每个寄存器控制8个引脚。控制某个引脚的四个位的配置可看下图
端口位配置表:
如何快速更改GPIO输入输出方向

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

有没有发现,当为输入模式时,CNF1和CNF0为1,0,代表上拉和下拉,那具体的上拉输入还是下拉呢,看右边部分PxODR寄存器,通过控制对应引脚的输出0还是1来确定如何快速更改GPIO输入输出方向
看代码:

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;
  }
}

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

随便说下第一个参数GPIOX是什么:
如何快速更改GPIO输入输出方向
如何快速更改GPIO输入输出方向
如何快速更改GPIO输入输出方向
GPIOX是基地址+偏移地址得到的地址,然后通过指向GPIO_TypeDef结构体类型的指针强制转换。得到宏定义标识符GPIOX。GPIOX就是指向该类结构体的指针了,可以通过该指针读写结构体成员,所以有:
如何快速更改GPIO输入输出方向
再来看看箭头指向的这两个寄存器BSRR和BRR
如何快速更改GPIO输入输出方向
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;
如何快速更改GPIO输入输出方向

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

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输入输出方向
现在从上面的发散思维回来,看看更改GPIO输入输出方向的代码就知道其原理了

IO方向设置 先用与(&)对11脚的四个位清零,再用或(|)置1
**#define SDA_IN() {GPIOC->CRH&=0XFFFF0FFF;GPIOC->CRH|=8<<12;**改变Pin11对应位改成1000,上或下拉输入
如何快速更改GPIO输入输出方向

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

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

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

相关标签: 嵌入式 指针