STM32指南者萌新学习笔记(三)

一、GPIO各常用相关函数复习

1.void  GPIO_DeInit  (GPIO_TypeDef* GPIOx)

函数解释:gpio的反初始化函数,该函数的作用是把GPIO相关的寄存器配置成上电复位后的默认状态,在第一次初始化前或者不在使用某一接口后,可以调用该函数。

参数:GPIOx,GPIO的分组,如 GPIOA,GPIOB,GPIOC等的宏定义。

2.void  GPIO_Init  (GPIO_TypeDef* GPIOx,GPIO_InitTypeDef* GPIO_InitStruct)

函数解释:GPIO的初始化函数,该函数的作用是对io进行初始化。

参数:(1)GPIOx,GPIO的分组,如 GPIOA,GPIOB,GPIOC等的宏定义。

            (2)GPIO_InitStruct,GPIO的初始化相关结构体。该结构体里的成员变量决定了我们具体的初始化参数。以下进行说明:

              GPIO_Pin:指定具体的io脚,如GPIO_Pin_0,GPIO_Pin_1这样的宏定义。

              GPIO_Mode:指定GPIO的模式,有四种模式:

  GPIO_Mode_IN(输入), GPIO_Mode_OUT(输出), GPIO_Mode_AF(第二功能), GPIO_Mode_AN(模拟)

              GPIO_Speed:指定IO最快翻转速度,也就是当使用IO产生频率(如PWM)的最大速度。有四种模式:

 GPIO_Low_Speed(低速), GPIO_Medium_Speed(中等速度), GPIO_Fast_Speed(快速), GPIO_High_Speed(低速)

               GPIO_OType:指定选择管脚的输出类型,有以下两种配置:

GPIO_OType_PP(推挽方式输出),GPIO_OType_OD(开漏方式输出)

温馨提示:

推挽输出:推挽输出就是单片机引脚可以直接输出高电平电压。低电平时接地,高电平时输出单片机电源电压。这种方式可以不接上拉电阻。但如果输出端可能会接地的话,这个时候输出高电平可能引发单片机运行不稳定,甚至可能烧坏引脚。推挽方式的驱动力更大。

开漏输出:开漏输出就是不输出电压,低电平时接地,高电平时不接地。如果外接上拉电阻,则在输出高电平时,电压会拉到上拉电阻的电源电压。这种方式适合在连接的外设电压比单片机低的时候。

               GPIO_PuPd:指定选择管脚的上拉和下拉模式。有三种配置:

GPIO_PuPd_NOPULL(不上拉也不下拉),GPIO_PuPd_UP(上拉),GPIO_PuPd_DOWN(下拉)

温馨提示:

这些都是io内部的内部上拉或者下拉模式,也可以接上拉或下拉电阻,通过硬件连接,实现外部上拉或外部下拉。

3.void  GPIO_StructInit  (GPIO_InitTypeDef* GPIO_InitStruct)

函数解释:GPIO结构体的初始化。对GPIO_InitStruct结构体进行默认配置。

参数:GPIO_InitStruct,直接传入该结构体的指针。在该函数内会对结构体进行初始化。

4.void  GPIO_PinLockConfig (GPIO_TypeDef* GPIOx,uint16_t  GPIO_Pin)

函数解释:锁定GPIO寄存器,锁定的寄存器是GPIOx_MODER,   GPIOx_OTYPER,    GPIOx_OSPEEDR,   GPIOx_PUPDR,    GPIOx_AFRL    and    GPIOx_AFRH。在下一次复位前,被锁定的管脚不能被修改。

参数:(1)GPIOx,GPIO的分组,如 GPIOA,GPIOB,GPIOC等的宏定义。

            (2)  GPIO_Pin:指定具体的io脚,如GPIO_Pin_0,GPIO_Pin_1这样的宏定义。

GPIO的读写函数

1.uint8_t  GPIO_ReadInputDataBit  (GPIO_TypeDef* GPIOx , uint16_t GPIO_Pin)

函数解释:读取io输入管脚的值

参数:(1)GPIOx,GPIO的分组,如 GPIOA,GPIOB,GPIOC等的宏定义。

            (2)  GPIO_Pin:指定具体的io脚,如GPIO_Pin_0,GPIO_Pin_1这样的宏定义。

返回值:输入管脚的值 Bit_SET(高电平),Bit_RESET(低电平)

2.uint16_t  GPIO_ReadInputData (GPIO_TypeDef* GPIOx )

函数解释:读取输入io数据,该函数用于读取一个io分组的所有数据

参数:GPIOx,GPIO的分组,如 GPIOA,GPIOB,GPIOC等的宏定义。

返回值:一个io端口的所有数据(输入状态)

3.uint8_t  GPIO_ReadOutputDataBit  (GPIO_TypeDef* GPIOx , uint16_t GPIO_Pin)

函数解释:读取io输出管脚的值

参数:(1)GPIOx,GPIO的分组,如 GPIOA,GPIOB,GPIOC等的宏定义。

            (2)  GPIO_Pin:指定具体的io脚,如GPIO_Pin_0,GPIO_Pin_1这样的宏定义。

返回值:输出管脚的值 Bit_SET(高电平),Bit_RESET(低电平)

4.uint16_t  GPIO_ReadOutputData (GPIO_TypeDef* GPIOx )

函数解释:读取输出io数据,该函数用于读取一个io分组的所有数据

参数:GPIOx,GPIO的分组,如 GPIOA,GPIOB,GPIOC等的宏定义。

返回值:一个io端口的所有数据(输出状态)

5.void  GPIO_SetBits(GPIO_TypeDef* GPIOx,uint16_t  GPIO_Pin)

函数解释:对io管脚进行置位(输出高电平)。这个函数使用GPIOx_BSRR寄存器来实现原子读或者修改操作。在这种情况下,在读和修改访问时发生一个IRQ中断是没有危险的。

参数:(1)GPIOx,GPIO的分组,如 GPIOA,GPIOB,GPIOC等的宏定义。

            (2)  GPIO_Pin:指定具体的io脚,如GPIO_Pin_0,GPIO_Pin_1这样的宏定义。

6.void  GPIO_ResetBits(GPIO_TypeDef* GPIOx,uint16_t  GPIO_Pin)

函数解释:对io管脚进行复位(输出低电平)。这个函数使用GPIOx_BSRR寄存器来实现原子读或者修改操作。在这种情况下,在读和修改访问时发生一个IRQ中断是没有危险的。

参数:(1)GPIOx,GPIO的分组,如 GPIOA,GPIOB,GPIOC等的宏定义。

            (2)  GPIO_Pin:指定具体的io脚,如GPIO_Pin_0,GPIO_Pin_1这样的宏定义。

7.void  GPIO_WriteBit(GPIO_TypeDef* GPIOx,uint16_t  GPIO_Pin,BitActionBitVal)

函数解释:对某一位进行写入操作。

参数:(1)GPIOx,GPIO的分组,如 GPIOA,GPIOB,GPIOC等的宏定义。

            (2)GPIO_Pin:指定具体的io脚,如GPIO_Pin_0,GPIO_Pin_1这样的宏定义。

            (3)BitVal:写入高电平或者低电平(Bit_RESET:写入低电平 Bit_SET:写入高电平)

8.void  GPIO_Write(GPIO_TypeDef* GPIOx,uint16_t  PortVal)

函数解释:对GPIO端口进行写入操作,适用于对统一端口的多个管脚的写入。

参数:(1)GPIOx,GPIO的分组,如 GPIOA,GPIOB,GPIOC等的宏定义。

            (2)BitVal:写入高电平或者低电平(Bit_RESET:写入低电平 Bit_SET:写入高电平)

9.void  GPIO_ToggleBits(GPIO_TypeDef* GPIOx,uint16_t  GPIO_Pin)

函数解释:翻转指定的GPIO口,如果当前io是高电平,则变为低电平。如果当前io是低电平,则变为高电平。

参数:(1)GPIOx,GPIO的分组,如 GPIOA,GPIOB,GPIOC等的宏定义。

            (2)GPIO_Pin:指定具体的io脚,如GPIO_Pin_0,GPIO_Pin_1这样的宏定义。

以上出自一位优秀的博主,下面是原文链接:https://blog.csdn.net/niuyuce/article/details/84846768

二、位带操作控制GPIO输入输出

        位操作就是可以单独的对一个比特位读和写,这个在 51 单片机中非常常见。 51 单片机中通过关键字 sbit 来实现位定义,
        如下文代码:
sbit KEY1=P3^1;
sbit KEY2=P3^0;
sbit KEY3=P3^2;
sbit KEY4=P3^3;
sbit LSA=P2^2;    
sbit LSB=P2^3;
sbit LSC=P2^4;
sbit bee=P2^5;
sbit led=P2^6;

sbit KEY_S5=P3^4;
        STM32 没有这样的关键字,而是通过访问位带别名区来实现。
        在 STM32 中,有两个地方实现了位带,一个是 SRAM 区的最低 1MB 空间,另一个是外设区最低 1MB 空间。这两个 1MB 的空间除了可以像正常的 RAM 一样操作外,他们还有自己的位带别
名区,位带别名区把这 1MB 的空间的每一个位膨胀成一个 32 位的字(也就是4个字节),当访问位带别名区的这些字节时,就可以达到访问位带区某个比特位的目的。
        外 设 外 带 区 的 地 址 为:0X40000000~0X40100000,
1MB 的 大 小 在 103 系 列 大 / / 小 容 量 型 号 的 单 片 机 中 包 含 了 片 上 外 设 的 全 部 寄 存 器, 这 些 寄 存 器 的 地 址 为:0X40000000~0X40029FFF 。 外 设 位 带 区 经 过 膨 胀 后 的 位 带 别 名 区 地 址 为: 0X42000000~0X43FFFFFF,这恰好落在了CM3片上的保留地区,不会与其他寄存器的地址产生冲突。
        下面是两个位带区与位带别名区的转换计算公式:
        
        这其中的“0x42000000”与"0x22000000"都是别名区的起始地址,“0x40000000”与“0x20000000”都是位带区的起始地址,“A”代指的是我们将要转换的原位带区地址。
        这里的 “*8*4” 的原因是上面提到的,位带别名区一个位会膨胀成4个字节,一个字节是8位,所以总共也就是32个位,所以这里有一个“*8*4”.
        至于这里的“n*4”我们之前几篇文章也都提到过,每两位之间的地址都是相差4的,也就是偏移4,所以这里添加了一个“n*4”.
        下面是一个可以应用的公式:
        这个公式与上面的公式意思是一样的,“(addr&0xF0000000)”的作用是提取出“4或2”,其实就是起始地址,判断是外设还是SRAM,“+0x2000000”很好理解不多说了,
        这两个位带区可偏移地址只有低6位,“(addr&0x00FFFFFF)<<5”首先,“&”一个0x00FFFFFF是为了屏蔽掉前两位,为了不影响下一步的<<5”操作,这个左移5其实就相当于“*8*4”,2^5=32;同理,bitnum<<2,就相当于*4,2^2=4;
        
        实际应用见下面代码:
        
#define GPIOB_ODR_Addr				(GPIOB_BASE+0X0C)
#define PBout(n)					 * (unsigned int*)((GPIOB_ODR_Addr & 0xF0000000)+0x02000000+((GPIOB_ODR_Addr &0x00FFFF)<<5)+(n<<2))


void Delay(uint32_t count)
{
	for(;count!=0;count--);
}

int main(void)
{	
	
	/* LED端口初始化 */
	LED_GPIO_Config();
	LED1_ON;

	/* 按键端口初始化 */
	Key_GPIO_Config();
	
	
	while(1)                            
	{	   
		PBout(0) = 1;
		Delay(0xFFFFF);
		PBout(0) = 0;
		Delay(0xFFFFF);
	}
}

这一代码通过位带操作的方法间接控制GPIOB的输出,从而控制LED灯的亮灭。

三、时钟配置

        时钟是单片机的心脏。

        那么它的作用是什么呢?

        首先呢,是HSE : High Speed External Clock signal ,即高速的外部时钟。

        来源:无源晶振( 4-16M ),通常使用8M。(也可以来源于有源晶振。当使用有源晶振时,时钟从 OSC_IN 引脚进入,OSC_OUT 引脚悬空,当选用无源晶振时,时钟从 OSC_IN 和 OSC_OUT 进入,并且要配谐振电容。

        控制: RCC _CR时钟控制寄存器的位16 : HSEON控制

        相对来说,HSE精度比较高,而LSE精度比较小。

        在这里让寄存器HSEON置1为启动。

        32中总共有五个时钟源,  除去刚刚提到的HSE高速外部时钟外,还有:

HSI是高速内部时钟,RC振荡器,频率为8MHz。 

③LSI是低速内部时钟,RC振荡器,频率为40kHz。

④LSE是低速外部时钟,接频率为32.768kHz的石英晶体。

⑤PLL为锁相环倍频输出,其时钟输入源可选择为HSI/2、HSE或者HSE/2。倍频可选择为2~16倍,但是其输出频率最大不得超过72MHz。

PLL时钟源的其中一个寄存器XTPRE的作用就是控制HSE不分频或2分频。

    

我们一般如何配置呢?一般都是将XTPRE配置为0,将SRC配置为1;

在时钟配置这里啊,来简单说一下为什么我们一般都使用HSE而不是HSI,

看这里的“注意”这一部分,官方推荐的时钟频率是72M,这也是最稳定的一个比较大的频率,而如果使用HSI的话,很明显是达不到要求的。

        来看一下下面的这个流程,首先呢,HSE发出的信号不分频,也就是经过PLLXTPRE上面的这一条线,再经过PLLSRC后可以向上走进入内部,也可以拐弯进入下一个“PLLMUL”,我们关闭上面的分路,进入PLLMUL,在这里控制进行分频,选择9倍频,就配置成了72M(因为前面发出的是8M),然后进入后面的CLK,在这里我们可以看到,有三条线连着SW,分别是HSE、HSI、PLLCLK,说明SW的来源可以是这三个任意。

        在这一流程中的SRC、MUL、SW都是由PLLCFGR这一寄存器控制的。

另外就是,SW,这一个位,是一个“系统时钟切换位”,如下图所示:

可以通过配置SW而强制转换所使用的系统时钟。

接着上文的分析,

从SW出来,经过SYSCLK后,就是我们第一条总线AHB的预分频器,这一预分频器连接着另外两条总线的预分频器,而在两条总线的预分频器后连接着很多外设。

下面开始讲总线的配置,总线的配置需要用到的也是PLLCFGR这一寄存器,AHB总线需要用到HPRE这一位,一般配置为1,APB1用到的是PPRE1这一位,一般配置为2,也就是36M,APB2用到的是PPRE2这一位,配置一般为1,因为是高速嘛。

下面是时钟树上比较重要的几个时钟:

        其中这个RTC,是芯片内部的一个独立时钟,一般用来进行实时时钟工程时需要用到,可以独立运行时间。

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值