如何快速更改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 ,推挽输出
上一篇: Mock工具库Random学习