STM32F103VET6学习(3)

一.前言

这次的对象仍然是LED的点亮,不过由此开始就是自己写库函数实现。

也就是外设的最初定义与封装是自己完成的。同样也是结合数据手册来写。因为我写的是用C语言,对于编译器来说它什么都不知道,因此所有的定义都不能想当然。为了使地址与寄存器一一对应,在定义地址时,就以寄存器的名字来对应相应的地址,这样见名知意也方便。
有人可能不好找到数据手册,附在下面,自提即可。

中文数据手册:
链接:https://pan.baidu.com/s/12G6-A_V2b-v7itHy_oy3Vw
提取码:df9b

英文数据手册:
链接:https://pan.baidu.com/s/1uDyv1Yl7K-0tV3h6VY-p9g
提取码:en4u

二.正文

正文有两大部分内容,简介如下:
(1)基址的宏定义以及寄存器的封装,放在stm32f10x_gpio.h文件中
(2)端口操作与设置函数,放在stm32f10x_gpio.c文件中

1.基址宏定义与寄存器的封装

基址部分要定义的就是总线的地址,通过数据手册知道有APB1、AHB、APB2总线三条总线,每条总线上都挂有寄存器:
存储器映像
因此基于此,便可以进行寄存器的定义。
在这里的截图我并没有截全,只是表示在总线上挂有寄存器的意思。下来的基址宏定义就根据这个来写,我会分别截图来看的。
通过上图可以看到,对于每一条总线,其上寄存器的地址都是自下而上的分布,也就是下面的数值小,往上依次增加,注意是每条总线的地值范围。下来就开始写地址的定义。
1.总线基址的宏定义

首先,通过下图截图可以看到:
总线地址
总线的地址从APB1总线开始,最小的是0x4000 0000,而其他的总线基地址都是在0x4000 0000 基础上的偏移,故将0x4000 0000作为总线的基地址:

#define PERIPH_BASE 0x40000000

总线APB1搭载低速外设,APB2搭载高速外设,我使用高速外设,因此在此就不定义APB1总线的基地址。
APB2总线基地址
从数据手册的截图可以看到,APB2总线的基地址是在总线PERIPH_BASE的基础上加了0x0001 0000,也就是加了0x1 0000,因此以此作为APB2总线的基地址:

#define APB2PERIPH_BASE (PERIPH_BASE+0x10000)

AHB总线上挂载了外设时钟,在第一次就说过外设时钟必须要打开,因此AHB总线的基地址也要定义。
AHB总线基地址
与刚才的思想相同,由于我要用的是RCC时钟,因此定义AHB总线就仍从PERIPH_BASE总线的基础上定义,之后在定义RCC时就在AHBPERIPH_BASE基础上定义。因此定义AHB总线基地址为

#define AHBPERIPH_BASE (PERIPH_BASE+0x20000)

2.寄存器的定义
为了方便,对无符号整型和无符号短整型的变量类型定义别名:

typedef unsigned int uint32_t;
typedef unsigned short uint16_t;

下来是为了防止编译器优化变量,使用volatile关键字。有些变量在未用时,CPU会从缓存中随机取值作为变量的值,籍此提高执行速度。而我们为了让变量保持不变,就要防止自动优化。
同样也给这个关键字用宏定义取个别名:

#define __IO volatile

下来就是封装GPIO寄存器和RCC外设时钟结构体,待用。

typedef struct
{
	__IO uint32_t CRL;    //端口配置低寄存器,地址偏移0x00
	__IO uint32_t CRH;    //端口配置高寄存器,地址偏移0x04
	__IO uint32_t IDR;    //端口数据输入寄存器,地址偏移0x08
	__IO uint32_t ODR;    //端口数据输出寄存器,地址偏移0x0C
	__IO uint32_t BSRR;   //端口位设置/清除寄存器,地址偏移0x10
	__IO uint32_t BRR;    //端口位清除寄存器,地址偏移0x14
	__IO uint32_t LCKR;   //端口配置锁定寄存器,地址偏移0x18
}GPIO_TypeDef;

typedef struct
{
	__IO uint32_t APB2ENR;
}RCC_TypeDef;

下来就是各个GPIOx(x=A,B,…,G)和RCC的定义。看数据手册:
APB2
可以看到这7个GPIO口(GPIOA - GPIOG)的地址有规律。自下而上,高四位都是4001,也就是刚才定义的APB2PERIPH_BASE的地址的高四位,不同之处就在于低四位的三四位,GPIOA从0800开始,每次加4(十六进制加法),便得到了这七个口的基地址:

#define GPIOA_BASE (APB2PERIPH_BASE + 0x0800)
#define GPIOB_BASE (APB2PERIPH_BASE + 0x0C00)
#define GPIOC_BASE (APB2PERIPH_BASE + 0x1000)
#define GPIOD_BASE (APB2PERIPH_BASE + 0x1400)
#define GPIOE_BASE (APB2PERIPH_BASE + 0x1800)
#define GPIOF_BASE (APB2PERIPH_BASE + 0x1C00)
#define GPIOG_BASE (APB2PERIPH_BASE + 0x2000)

为了一致性,在这里把RCC外设的基址也定义了(RCC外设是挂载在AHBPERIPH_BASE总线上的):

#define RCC_BASE (AHBPERIPH_BASE + 0x1000)

同时由于这七个口每个下面都有刚才定义的7个寄存器(BSR,BSRR,ODR,IDR,…),而这7个寄存器都是被封装在了结构体GPIO_TypeDef中,因此要能使用这7个寄存器,并能具体指明使用哪个,就必须把GPIOA-GPIOG对应的总线强制转换为GPIO_TypeDef类型的指针:

/*GPIO外设声明*/
#define GPIOA ((GPIO_TypeDef *) GPIOA_BASE)
#define GPIOB ((GPIO_TypeDef *) GPIOB_BASE)
#define GPIOC ((GPIO_TypeDef *) GPIOC_BASE)
#define GPIOD ((GPIO_TypeDef *) GPIOD_BASE)
#define GPIOE ((GPIO_TypeDef *) GPIOE_BASE)
#define GPIOF ((GPIO_TypeDef *) GPIOF_BASE)
#define GPIOG ((GPIO_TypeDef *) GPIOG_BASE)

这样就把变量名与真实的寄存器的地址对应起来了。
同样,也需要RCC外设的声明:

//RCC外设的声明
#define RCC ((RCC_TypeDef *) RCC_BASE)
//RCC的AHB1时钟使能寄存器的地址,强制转换成指针
#define RCC_APB2ENR *(unsigned int*)(RCC_BASE + 0x18)

3.管脚定义
在之前定义管脚时,用的都是0x01左移若干位来操作的。这样还是有些不方便,因此对管脚也来一个方便使用的定义。设置输出高电平,我们用BSRR寄存器(也就是 端口位设置/清除寄存器),输出低电平用的是BRR寄存器。看下BSRR寄存器的说明:
BSRR寄存器
可以看到BSRR寄存器的0-15这16位是设置端口位输出电平的,而16-31这16位则是清除前面0-15这16位的。而BRR寄存器的高16位保留不可操作,低16位用于清除端口位,也就是设置为低。因此只需要定义0-15号管脚这16个管脚就可以了,而且直接把1左移n位(n=0,1,2,…,15)的结果计算出来赋值给对应的管脚即进行宏定义,之后直接使用即可:

#define GPIO_Pin_0 ((uint16_t)0x0001)
#define GPIO_Pin_1 ((uint16_t)0x0002)
#define GPIO_Pin_2 ((uint16_t)0x0004)
#define GPIO_Pin_3 ((uint16_t)0x0008)
#define GPIO_Pin_4 ((uint16_t)0x0010)
#define GPIO_Pin_5 ((uint16_t)0x0020)
#define GPIO_Pin_6 ((uint16_t)0x0040)
#define GPIO_Pin_7 ((uint16_t)0x0080)
#define GPIO_Pin_8 ((uint16_t)0x0100)
#define GPIO_Pin_9 ((uint16_t)0x0200)
#define GPIO_Pin_10 ((uint16_t)0x0400)
#define GPIO_Pin_11 ((uint16_t)0x0800)
#define GPIO_Pin_12 ((uint16_t)0x1000)
#define GPIO_Pin_13 ((uint16_t)0x2000)
#define GPIO_Pin_14 ((uint16_t)0x4000)
#define GPIO_Pin_15 ((uint16_t)0x8000)
#define GPIO_Pin_ALL ((uint16_t)0xFFFF)

2.工作模式与速率的配置

1.说明:
输出速率有三种(输入不讲速率),分别是2MHz、10MHz和50MHz;工作模式有多种,先分输入和输出,输入又分为模拟输入、浮空输入、上拉输入和下拉输入,输出分为开漏输出、推挽输出、复用开漏输出和复用推挽输出。在选用模式和速率时,这些有多种搭配,逐一列出来就很麻烦,因此同样将它们分别封装成结构体,采用枚举类型,也就是将所有的速率封装、所有的模式封装,这样就有两个结构体,最后再在一个新的结构体中定义前两种结构体类型的变量作为分别的工作模式与速率的选择,再定义一个选择管脚的变量即可。由于管脚数并没那么多,因此定义其变量类型为无符号short型就可以了。
2.代码实现
(1)速率结构体配置
速率的配置可以在数据手册中查到,如下图:
速率配置
对照着上表可以,同时为了见名知意,方便起见,写出GPIOSpeed_TypeDef结构体如下:

typedef enum
{
	GPIO_Speed_10MHz = 0x01,   //10MHz,  (01)B
	GPIO_Speed_2MHz = 0x02,    //2MHz,   (10)B
	GPIO_Speed_50MHz = 0x03    //50MHz,  (11)B
}GPIOSpeed_TypeDef;

(2)工作模式的配置(8种)
工作模式有8种好理解,主要是其取值有些不好看,如下表:
工作模式配置
从上表可以看到如下几点:

  • 工作模式有8个bit位
  • bit4决定输入还是输出,写0输入,写1输出
  • bit3和bit2对应的是CNF[1:0]位,是真正要写入到CRH和CRL寄存器中的值。为输入模式时,bit3和bit2分别为00、01则分别为模拟输入和浮空输入。
  • bit1和bit0应的是MODE[1:0]位,暂时不用理会。
  • bit6和bit5用于区分上、下拉输入,这个取值是可以自拟的,为了区分,以01表示下拉输入,10表示上拉输入。

因此,就可以写出定义工作模式的结构体GPIOMode_TypeDef:

typedef enum
{
	GPIO_Mode_AIN = 0X00,    //模拟输入
	GPIO_Mode_IN_FLOATING = 0x04,   //浮空输入
	GPIO_Mode_IPD = 0X28,       //下拉输入
	GPIO_Mode_IPU = 0X48,      //上拉输入
	
	GPIO_Mode_Out_OD = 0X14,   //开漏输出
	GPIO_Mode_Out_PP = 0X10,   //推挽输出
	GPIO_Mode_AF_OD = 0X10,    //复用开漏输出
	GPIO_Mode_AF_PP = 0X18     //复用推挽输出
}GPIOMode_TypeDef;

(3)结构体GPIO_InitTypeDef:

typedef struct
{
	uint16_t GPIO_Pin;      //管脚
	GPIOSpeed_TypeDef GPIO_Speed;   //速率
	GPIOMode_TypeDef GPIO_Mode;     //工作模式
}GPIO_InitTypeDef;

在此定义的管脚,刚有说过了。之后如果再定义一个GPIO_InitTypeDef类型的结构体变量,就可以调用GPIO_InitTypeDef这个结构体的各个属性,然后就可以把与之对应的枚举类型的结构体的成员进行“使用”。比如:

GPIO_InitTypeDef GPIO_InitStructure;   //定义结构体类型的变量
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;   //速率为10MHz
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;  //开漏输出

这是使用的一个例子,有注释很详细,也不难理解,不再解释了。

3.操作函数

操作函数有三个,分别是高电平输出函数、低电平输出函数和GPIO初始化函数(初始化包括管脚选择、工作模式选择等)。前面写的内容都是写在stm32f10x_gpio.h文件中的初始定义,下来才是真正的对单片机操作的函数,而这里的内容有了之前的初始定义后就相对好多了,这部分的内容全部写在stm32f10x_gpio.c文件中,具体如下。
1.高电平输出函数
具体直接看代码就可以了,有的地方如果不明白可以翻回去再看看之前的结构体定义:

/*
函数名:GPIO_SetBit(),Set就是设置的意思,Bit就是位
参数说明:
(1)GPIOx:GPIO_TypeDef类型的的指针,x可以为A~G
(2)GPIO_Pin:GPIO管脚,可以取GPIO_Pin_0 ~ GPIO_Pin_15
*/
void GPIO_SetBit(GPIO_TypeDef* GPIOx, uint32_t GPIO_Pin)
{
	/*BSRR寄存器用来设置高电平*/
	GPIOx->BSRR = GPIO_Pin;
}

2.低电平输出函数

/*
函数名:GPIO_ResetBit(),Reset就是重置的意思,认为其为0
*/
void GPIO_ResetBit(GPIO_TypeDef* GPIOx, uint32_t GPIO_Pin)
{
	/*BRR寄存器用来设置低电平*/
	GPIOx ->BRR = GPIO_Pin;
}

3.GPIO初始化函数
这个函数十分重要,是整个程序的核心。在此要设置的是,CRH和CRL两个寄存器,在之前我们也看过了,配置其输出时主要就是这两个寄存器。简单概括的说,前面做好了寄存器等的定义,在这里就要把这些定义和配置统统利用起来并烧录到板子里面去,而这个就是让板子对这一切做一个初始化的准备的过程。
CRH和CRL寄存器的配置几乎一样,主要不同在于一个是低位,一个是高位,只要注意位的高低与对应的寄存器L、H即可了。
下面是代码,有详细的注释,死磕是一定可以理解的:

void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct)
{
	uint32_t currentmode = 0x00, currentpin = 0x00,pinpos=0x00,pos=0x00;
	uint32_t temreg=0x00,pinmask=0x00;
	
	/*----------------GPIO模式配置---------------------*/
	
	//将已有状态的低四位保存到currentmode中
	currentmode = ((uint32_t)GPIO_InitStruct->GPIO_Mode) & (uint32_t)(0x0F);
	
	//先判断是输入还是输出,即判断bit4是1还是0
	if((((uint32_t)GPIO_InitStruct->GPIO_Mode) & (uint32_t)(0x10)) != 0)
	{
		//如果为输出就配置输出速度
		currentmode |= (uint32_t)GPIO_InitStruct->GPIO_Speed;
	}

	/*-------------配置GPIO_CRL寄存器,即配置低八位------------*/
	
	if(((uint32_t)GPIO_InitStruct->GPIO_Pin & 0x00FF) != 0x00)
	{
		temreg = GPIOx->CRL;   //先读取原来低八位的状态并暂存
		//逐位匹配来找到是哪个管脚
		for(pinpos=0x00;pinpos<0x08;pinpos++)
		{
			pos = (uint32_t)0x01 << pinpos;
			currentpin = (GPIO_InitStruct->GPIO_Pin) & pos;
			if(currentpin == pos)
			{
				pos = pinpos<<2; //相当于乘以4,因为每个管脚对应四位
				
				//将这四位清零,其他位状态保留
				pinmask = ((uint32_t)0x0F) >> pos;
				temreg &= ~pinmask;
				
				//给这四位写入模式配置,保留其他位原先的设置
				temreg |= (currentmode << pos);
				
				/*是否为下拉输入*/
				if(GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPD)  
				{
					//为下拉输入则将BRR寄存器写1,使对应位置0
					GPIOx->BRR = (uint32_t)0x01 << pinpos;
				}
				else
					/*是否为上拉输入*/
					if(GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPU)  
					{
						//为上拉输入则将BSRR寄存器写1,使对应位置1
						GPIOx->BSRR = (uint32_t)0x01 << pinpos;
					}
			}
		}
	}
	//将处理后的状态写入到CRL寄存器当中
	GPIOx->CRL = temreg;
	
	/*-----------配置GPIO_CRH寄存器,即配置高八位---------------*/
	if(GPIO_InitStruct->GPIO_Pin > 0x00FF)
	{
		temreg = GPIOx->CRH;   //先读取原来的高八位的状态并暂存
		
		//逐位匹配,找到具体的Pin
		for(pinpos=0x00;pinpos<0x08;pinpos++)
		{
			pos = (((uint32_t)0x01) << (pinpos+0x08));
			currentpin = (GPIO_InitStruct->GPIO_Pin) & pos;
			if(currentpin == pos)
			{
				pos = pinpos << 2;
				
				//把这四位清零,其他位保留不变
				pinmask = ((uint32_t)0x0F) << pos;
				pos &= ~pinmask;
				
				temreg |= currentmode << pos;
				
				//是否为下拉输入
				if(GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPD)  
				{
					GPIOx->BRR = (uint32_t)0x01 << (pinpos + 0x08);
				}
				else
					/*是否为上拉输入*/
					if(GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPU)  
					{
						GPIOx->BSRR = (uint32_t)0x01 << (pinpos+0x08);
					}
			}
		}
		GPIOx->CRH = temreg;
	}
}

至此,就把GPIO初始化函数写完了,所有的宏定义呀、配置函数等等的,全部都写完了。最后就是具体的利用这些函数来实现点亮LED的功能了。
4.主函数main()
在主函数里,完成我们想要实现的功能就可以了。这里实现的是点亮LED,在上一次看过的原理图就知道是低有效。如果是绿色的,对应Pin0管脚;如果是红色,对应的是Pin5管脚。在主函数之外,还需要一个SystemInit()函数来“骗过”编译器不报错,具体原因上次说过了,这次就不说啦。下面是主函数和SystemInit()中的代码:

int main(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;   //定义GPIO初始化结构体变量
	
	//开启外设时钟
	RCC_APB2ENR |= (1<<3);
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 ;      //0号管脚
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;  //工作模式
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz; //输出速率
	
	GPIO_Init(GPIOB, &GPIO_InitStructure);   //GPIO初始化工作
	GPIO_ResetBit(GPIOB, GPIO_Pin_0);   //让GPIOB的0号管脚输出低电平
	
	while(1);
}
void SystemInit()
{
}

4.全部代码

由于代码从Keil5复制过来后中文部分会乱码,所以我把注释删除了。这部分内容看了挺久了,真的不想再去重复做这个工作了,有点懒,但不要怪我。刚好要是有人看下面的,还可以自己给自己解释。
工程文件结构如下图所示:
The Structure Of The Project

:其中的stm32f103.h和stm32f10x_gpio.h其实就是我说的stm32f10x_gpio.h文件,我开始时比较生疏,所以就分开写了,不过是可以放在一起的。stm32f10x_gpio.h和stm32f10x_gpio.c这两者是需要自己写的,如下是全部的代码。

(1)stm32f10x_gpio.h文件

#define  __IO volatile
typedef unsigned int uint32_t;
typedef unsigned short uint16_t;

typedef struct
{
	__IO uint32_t CRL;   
	__IO uint32_t CRH;   
	__IO uint32_t IDR;   
	__IO uint32_t ODR;   
	__IO uint32_t BSRR;  
	__IO uint32_t BRR;  
	__IO uint32_t LCKR; 
}GPIO_TypeDef;

typedef struct
{
	__IO uint32_t APB2ENR;
}RCC_TypeDef;

#define PERIPH_BASE ((unsigned int)0x40000000)
	
#define APB2PERIPH_BASE (PERIPH_BASE + 0x10000)
#define AHBPERIPH_BASE (PERIPH_BASE + 0x20000)


#define GPIOA_BASE (APB2PERIPH_BASE + 0x0800)
#define GPIOB_BASE (APB2PERIPH_BASE + 0x0C00)
#define GPIOC_BASE (APB2PERIPH_BASE + 0x1000)
#define GPIOD_BASE (APB2PERIPH_BASE + 0x1400)
#define GPIOE_BASE (APB2PERIPH_BASE + 0x1800)
#define GPIOF_BASE (APB2PERIPH_BASE + 0x1C00)
#define GPIOG_BASE (APB2PERIPH_BASE + 0x2000)


#define RCC_BASE (AHBPERIPH_BASE + 0x1000)


#define GPIOA ((GPIO_TypeDef *) GPIOA_BASE)
#define GPIOB ((GPIO_TypeDef *) GPIOB_BASE)
#define GPIOC ((GPIO_TypeDef *) GPIOC_BASE)
#define GPIOD ((GPIO_TypeDef *) GPIOD_BASE)
#define GPIOE ((GPIO_TypeDef *) GPIOE_BASE)
#define GPIOF ((GPIO_TypeDef *) GPIOF_BASE)
#define GPIOG ((GPIO_TypeDef *) GPIOG_BASE)

#define RCC ((RCC_TypeDef *) RCC_BASE)
#define RCC_APB2ENR *(unsigned int*)(RCC_BASE + 0x18)

#define GPIO_Pin_0 ((uint16_t)0x0001)
#define GPIO_Pin_1 ((uint16_t)0x0002)
#define GPIO_Pin_2 ((uint16_t)0x0004)
#define GPIO_Pin_3 ((uint16_t)0x0008)
#define GPIO_Pin_4 ((uint16_t)0x0010)
#define GPIO_Pin_5 ((uint16_t)0x0020)
#define GPIO_Pin_6 ((uint16_t)0x0040)
#define GPIO_Pin_7 ((uint16_t)0x0080)
#define GPIO_Pin_8 ((uint16_t)0x0100)
#define GPIO_Pin_9 ((uint16_t)0x0200)
#define GPIO_Pin_10 ((uint16_t)0x0400)
#define GPIO_Pin_11 ((uint16_t)0x0800)
#define GPIO_Pin_12 ((uint16_t)0x1000)
#define GPIO_Pin_13 ((uint16_t)0x2000)
#define GPIO_Pin_14 ((uint16_t)0x4000)
#define GPIO_Pin_15 ((uint16_t)0x8000)
#define GPIO_Pin_ALL ((uint16_t)0xFFFF)

typedef enum
{
	GPIO_Speed_10MHz = 0x01,
	GPIO_Speed_2MHz = 0x02,
	GPIO_Speed_50MHz = 0x03
}GPIOSpeed_TypeDef;

typedef enum
{
	GPIO_Mode_AIN = 0X00,
	GPIO_Mode_IN_FLOATING = 0x04,
	GPIO_Mode_IPD = 0X28,
	GPIO_Mode_IPU = 0X48,
	
	GPIO_Mode_Out_OD = 0X14,  
	GPIO_Mode_Out_PP = 0X10,   
	GPIO_Mode_AF_OD = 0X10,    
	GPIO_Mode_AF_PP = 0X18    
}GPIOMode_TypeDef;


typedef struct
{
	uint16_t GPIO_Pin;
	GPIOSpeed_TypeDef GPIO_Speed;
	GPIOMode_TypeDef GPIO_Mode;
}GPIO_InitTypeDef;

(2)stm32f10x_gpio.c文件

#include"stm32f10x_gpio.h"

void GPIO_SetBit(GPIO_TypeDef* GPIOx, uint32_t GPIO_Pin)
{
	GPIOx->BSRR = GPIO_Pin;
}

void GPIO_ResetBit(GPIO_TypeDef* GPIOx, uint32_t GPIO_Pin)
{
	GPIOx ->BRR = GPIO_Pin;
}

void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct)
{
	uint32_t currentmode = 0x00, currentpin = 0x00,pinpos=0x00,pos=0x00;
	uint32_t temreg=0x00,pinmask=0x00;

	currentmode = ((uint32_t)GPIO_InitStruct->GPIO_Mode) & (uint32_t)(0x0F);
	
	if((((uint32_t)GPIO_InitStruct->GPIO_Mode) & (uint32_t)(0x10)) != 0)
	{
		currentmode |= (uint32_t)GPIO_InitStruct->GPIO_Speed;
	}


	if(((uint32_t)GPIO_InitStruct->GPIO_Pin & 0x00FF) != 0x00)
	{
		temreg = GPIOx->CRL;  
		for(pinpos=0x00;pinpos<0x08;pinpos++)
		{
			pos = (uint32_t)0x01 << pinpos;
			currentpin = (GPIO_InitStruct->GPIO_Pin) & pos;
			if(currentpin == pos)
			{
				pos = pinpos<<2; 
				
				pinmask = ((uint32_t)0x0F) >> pos;
				temreg &= ~pinmask;
				
				temreg |= (currentmode << pos);
				
				if(GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPD)  
				{

					GPIOx->BRR = (uint32_t)0x01 << pinpos;
				}
				else
					if(GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPU)  
					{
						GPIOx->BSRR = (uint32_t)0x01 << pinpos;
					}
			}
		}
	}

	GPIOx->CRL = temreg;
	
	if(GPIO_InitStruct->GPIO_Pin > 0x00FF)
	{
		temreg = GPIOx->CRH;  
		
		for(pinpos=0x00;pinpos<0x08;pinpos++)
		{
			pos = (((uint32_t)0x01) << (pinpos+0x08));
			currentpin = (GPIO_InitStruct->GPIO_Pin) & pos;
			if(currentpin == pos)
			{
				pos = pinpos << 2;
				
				pinmask = ((uint32_t)0x0F) << pos;
				pos &= ~pinmask;
				
				temreg |= currentmode << pos;
				
				if(GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPD)  
				{
					GPIOx->BRR = (uint32_t)0x01 << (pinpos + 0x08);
				}
				else
					if(GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPU)   
					{
						GPIOx->BSRR = (uint32_t)0x01 << (pinpos+0x08);
					}
			}
		}
		GPIOx->CRH = temreg;
	}
}

int main(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	
	RCC_APB2ENR |= (1<<3);
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 ;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
	
	GPIO_Init(GPIOB, &GPIO_InitStructure);   
	GPIO_ResetBit(GPIOB, GPIO_Pin_0);    
	while(1);
}
void SystemInit()
{
}

三.结语

看了上面没有注释的代码,我承认我看了我也想打人。但是我就懒这么一下,真的累了,也想着急去看下来的新的内容。这部分看懂用了两三天,这个博客断断续续也写了两三天,一下子就是一个周啊!下来得赶紧抓紧时间,提高自己的效率,毕竟还得复习准备考研。就这样吧,本次拖拖拉拉,但是总算有个头儿了。
本次内容就到这里,完结!
(Tip:文首有STM32F10x的中英文数据手册的百度云链接哦)

发布了11 篇原创文章 · 获赞 12 · 访问量 2329

猜你喜欢

转载自blog.csdn.net/J_GMson/article/details/104086776