寄存器点亮LED

GPIO的工作模式

  • 输入模式(模拟/浮空/上拉/下拉)

    输入模式中,施密特触发器打开,输出被禁止,可以通过输入数据寄存器GPIOx_IDR读取I/O状态。

    • 上拉/下拉:默认的电平由上拉或者下拉决定
    • 浮空:输入的电平不确定,完全由外部的输入决定,一般接按键的时候用的是这个模式
    • 模拟:用于ADC采集
  • 输出模式

    输出数据寄存器GPIOx_ODR可控制I/O输出高低电平

    • 开漏:只有N-MOS管工作,输出寄存器可控制I/O输出高阻态或低电平,输出速度可配置,有2MHZ,10MHZ,50MHZ的选项,此处输出速度即I/O支持的高低电平状态最高切换频率,支持的频率越高,功耗越大,如果功耗要求不严格,把速度设置成最大即可

      输出模式下施密特触发器打开,输入可用,通过输入数据寄存器GPIOx_IDR可读取I/O的实际状态

  • 复用功能(推挽/开漏)

    输出使能,输出速度可配置,可工作在开漏及推挽模式,但是输出信号源于其他外设,输出数据寄存器GPIOx_ODR无效,输入可用,通过输入数据寄存器可获取I/O实际状态,但一般直接用外设的寄存器来获取该数据信号

    image-20230305121926621

image-20230305121938559

如果但从这些枚举值的十六进制来看,很难发现规律,转化成二进制之后,就比较容易发现规律。 bit4 用来区分端口是输入还是输出,0 表示输入,1 表示输出,bit2 和 bit3 对应寄存器的 CNFY[1:0] 位,是我们真正要写入到 CRL 和 CRH 这两个端口控制寄存器中的值。bit0 和 bit1 对应寄存器的 MODEY[1:0] 位,这里我们暂不初始化,在 GPIO_Init() 初始化函数中用来跟 GPIOSpeed 的值相加 即可实现速率的配置。有关具体的代码分析见 GPIO_Init() 库函数。其中在下拉输入和上拉输入 中我们设置 bit5 和 bit6 的值为 01 和 10 来以示区别。

开启外设时钟

设置完 GPIO 的引脚,控制电平输出,以为现在总算可以点亮 LED 了吧,其实还差最后一步。由 于 STM32 的外设很多,为了降低功耗,每个外设都对应着一个时钟,在芯片刚上电的时候这些 时钟都是被关闭的,如果想要外设工作,必须把相应的时钟打开

所有的GPIO都挂载到APB2总线上,具体的时钟由APB2外设时钟使能寄存器(RCC_APB2ENR)来控制

image-20230304162809974

控制GPIO输出

对 GPIOx 的 BSRR 或 BRR 寄存器赋值,从而设置引脚为高 电平或低电平,操作 BSRR 或者 BRR 可以实现单独的操作某一位,有关这两个的寄存器说明见 图 BSRR 寄存器说明 和图 BRR 寄存器说明 。其中 GPIOx 是一个指针变量,通过函数的输入参数 我们可以修改它的值,如给它赋予 GPIOA、GPIOB、GPIOH 等结构体指针值,这个函数就可以控 制相应的 GPIOA、GPIOB、GPIOH 等端口的输出。

image-20230305112912853

image-20230305113530610

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;
	
	/*				GPIO模式配置				*/
	//把输入参数GPIO_Mode的低四位暂存在currentmode
	currentmode = ((uint32_t)GPIO_InitStruct->GPIO_Mode) & ((uint32_t)0x0F);
	
	
	//bit4是 1 表示输出,bit4是 0 则是输入
	//判断bit4是 1 还是 0,即首判断是输入还是输出模式
	if((((uint32_t)GPIO_InitStruct->GPIO_Mode) & ((uint32_t)0x10)) != 0x00)
	{
		//输出模式则要设置输出速度
		currentmode |= (uint32_t)GPIO_InitStruct->GPIO_Speed;
	}
	
	/*			GPIO CRL寄存器配置CRL寄存器控制着低8位IO					*/
	//配置端口低8位,即 Pin0 - Pin7
	if(((uint32_t)GPIO_InitStruct->GPIO_Pin & (uint32_t)0x00FF) != 0x00)
	{
		//先备份CRL寄存器的值
		tmpreg = GPIOx->CRL;
		//循环,从Pin0开始配对,找出具体的Pin
		for(pinpos = 0x00;pinpos < 0x08;pinpos++)
		{
			//pos的值为1左移pinpos位
			pos = ((uint32_t)0x01) << pinpos;
			
			//令pos与输入参数GPIO_PIN作位与运算
			currentpin = (GPIO_InitStruct->GPIO_Pin) & pos;
			
			//若 currentpin = pos,则找到使用的引脚
			if(currentpin == pos)
			{
				//pinpos 的值左移两位(乘4),因为寄存器中4个位配置一个引脚
				pos = pinpos << 2;
				//把控制这个引脚的4个寄存器位清零,其他寄存器位不变
				pinmask = ((uint32_t)0x0F) << pos;
				tmpreg &= ~pinmask;
				
				//向寄存器写入将要配置的引脚的模式
				tmpreg |= (currentmode << pos);
				
				//判断是否为下拉输入模式
				if(GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPD)
				{
					//下拉输入模式,引脚默认置0,对BRR寄存器写1对引脚置0
					GPIOx->BRR = (((uint32_t)0x01) << pinpos);
				}
				else
				{
					//判断是否位上拉输入模式
					if(GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPU)
					{
						//上拉输入模式,引脚默认值为1,对BSRR寄存器写1对引脚置1
						GPIOx->BSRR = (((uint32_t)0x01) << pinpos);
					}
				}
			}
		}
		//把前面处理后的暂存值写入到CRL寄存器之中
		GPIOx->CRL = tmpreg;
	}

image-20230305215356822

  1. 先取得 GPIO_Mode 的值,判断 bit4 是 1 还是 0 来判断是输出还是输入。如果是输出则设置 输出速率,即加上 GPIO_Speed 的值,输入没有速率之说,不用设置。
  2. 配置 CRL 寄存器。通过 GPIO_Pin 的值计算出具体需要初始化哪个引脚,算出后,然后把 需要配置的值写入到 CRL 寄存器中,具体分析见代码注释。这里有一个比较有趣的是上/下 拉输入并不是直接通过配置某一个寄存器来实现的,而是通过写 BSRR 或者 BRR 寄存器来 实现。这让很多只看手册没看固件库底层源码的人摸不着头脑,因为手册的寄存器说明中 没有明确的指出如何配置上拉/下拉,具体见图上拉 _ 下拉寄存器说明 。
  3. 配置 CRH 寄存器过程同 CRL

而是通过写 BSRR 或者 BRR 寄存器来 实现。这让很多只看手册没看固件库底层源码的人摸不着头脑,因为手册的寄存器说明中 没有明确的指出如何配置上拉/下拉,具体见图上拉 _ 下拉寄存器说明 。
3. 配置 CRH 寄存器过程同 CRL

image-20230305215715145

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值