我翻阅过资料,但是 始终未能寻找到我想要的答案。( 希望知道的大哥能告知下o( ̄▽ ̄)ブ )
只能知道他是一个c的缺省库,用于printf这样的函数转换。
4.再就是将所有的外设函数都添加进工程中
5.最后添加中断函数的存放文件 stm32f4xx_it.c 以及自己创建的main.c 文件。
进行到这里,就可以开始对寄存器操作赋值来控制芯片的led灯。首先看原理图:
这里我们可以看到LED灯属于共阳接法,都挂载再GPIOH(通用输入\输出端口)上,为了能够点亮RLED,需要将PH10置零。值得注意的是,与51等芯片不同的是端口的设置已经被挂载到了AHB1上,所以为了能让数据正常的传输到GPIO上,首先要初始化AHB1总线时钟。
而RCC的起始地址为 0X40023800 ,
所以 具体的实现语句是 *(unsigned int *)0X40023830 |=(1<<7) ;
目的是为了打开GPIOH的总线时钟
接着再设置GPIO的模式、输出类型、输出速度、上拉或下拉以及输入输出的数据
由于GPIO默认为输入模式,推挽输出、低速运行、不进行上拉下拉操作以及输出低电平
所以这里只需要设置模式
具体实现语句是 *(unsigned int * )0X40021C00 |=(1<<20);
目的是将模式调整为输出。
至此,我们只需要编译并下载程序,RLED灯就能被点亮。这是对寄存器直接进行操作,下面介绍利用库函数来操作寄存器。
关于RCC时钟方面,后面会有专门的一章,所以先不进行详细的讲解,主要是注释GPIO的库函数。
#define GPIOH_LED 0X00000080
#define RLED 0X0200
int mai(void)
{
GPIO_InitTypeDef* GPIO_InitStruct
//打开AHB1时钟
RCC_AHB1PeriphClockCmd(GPIOH_LED, ENABLE);
//定义初始化结构体,由于我们只需要初始化模式和引脚且初始化函数中并没有对其他设置进行了防错检查,所以可以不设置。
GPIO_InitStruct->GPIO_Pin = RLED;
GPIO_InitStruct->GPIO_Mode = GPIO_Mode_OUT;
//设置GPIOH的模式为输出
GPIO_Init(GPIOH,GPIO_InitStruct);
}
以上仅仅用到了GPIO的初始化函数,在最后我们对GPIO的库函数中挑选常用的函数进行分析:
/* Function used to set the GPIO configuration to the default reset state ****/
void GPIO_DeInit(GPIO_TypeDef* GPIOx); //设置GPIOx的AHB1时钟总线为默认状态(不使能) 由于内部程序较为简单不做详细分析
/* Initialization and Configuration functions *********************************/
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);//GPIO初始化函数,需要提供初始化结构体和对应GPIO
void GPIO_StructInit(GPIO_InitTypeDef* GPIO_InitStruct);//设置所有GPIO为默认状态(输入、20MHz、推挽输出、无上拉下拉 )
void GPIO_PinLockConfig(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);//配置锁定函数
/* GPIO Read and Write functions **********************************************/
uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);//读取指定GPIO中的引脚的当前输入状态
uint16_t GPIO_ReadInputData(GPIO_TypeDef* GPIOx);//读取指定GPIO的所有引脚输入状态
uint8_t GPIO_ReadOutputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);//读取指定GPIO中的引脚的当前输出状态
uint16_t GPIO_ReadOutputData(GPIO_TypeDef* GPIOx);//读取指定GPIO的所有引脚输出状态
void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);//对指定的GPIO引脚的输出值进行置位
void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);//对指定的GPIO引脚的输出值进行复位
void GPIO_WriteBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, BitAction BitVal);//对指定的GPIO进行直接复位或置位
void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal);//对指定的GPIO的ODR进行写入数据
void GPIO_ToggleBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);//翻转指定GPIO引脚
/* GPIO Alternate functions configuration function ****************************/
void GPIO_PinAFConfig(GPIO_TypeDef* GPIOx, uint16_t GPIO_PinSource, uint8_t GPIO_AF);//复位功能配置
这里挑出值得学习的代码段进行分析
#define assert_param(expr) ((expr) ? (void)0 : assert_failed((uint8_t *)__FILE__, __LINE__))
#define GPIO_PinSource0 ((uint8_t)0x00)
//这里省略其余的赋值
#define GPIO_PinSource15 ((uint8_t)0x0F)
#define IS_GPIO_ALL_PERIPH(PERIPH) (((PERIPH) == GPIOA) || \
((PERIPH) == GPIOB) || \
((PERIPH) == GPIOC) || \
((PERIPH) == GPIOD) || \
((PERIPH) == GPIOE) || \
((PERIPH) == GPIOF) || \
((PERIPH) == GPIOG) || \
((PERIPH) == GPIOH) || \
((PERIPH) == GPIOI) || \
((PERIPH) == GPIOJ) || \
((PERIPH) == GPIOK))
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct)
{
uint32_t pinpos = 0x00, pos = 0x00 , currentpin = 0x00;
/* Check the parameters */
assert_param(IS_GPIO_ALL_PERIPH(GPIOx)); //注释1
assert_param(IS_GPIO_PIN(GPIO_InitStruct->GPIO_Pin));
assert_param(IS_GPIO_MODE(GPIO_InitStruct->GPIO_Mode));
assert_param(IS_GPIO_PUPD(GPIO_InitStruct->GPIO_PuPd));
/* ------------------------- Configure the port pins ---------------- */
/*-- GPIO Mode Configuration --*/
for (pinpos = 0x00; pinpos < 0x10; pinpos++) //注释2
{
pos = ((uint32_t)0x01) << pinpos;
/* Get the port pins position */
currentpin = (GPIO_InitStruct->GPIO_Pin) & pos;
if (currentpin == pos)
{
GPIOx->MODER &= ~(GPIO_MODER_MODER0 << (pinpos * 2));
GPIOx->MODER |= (((uint32_t)GPIO_InitStruct->GPIO_Mode) << (pinpos * 2));
if ((GPIO_InitStruct->GPIO_Mode == GPIO_Mode_OUT) || (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_AF))
{
/* Check Speed mode parameters */
assert_param(IS_GPIO_SPEED(GPIO_InitStruct->GPIO_Speed));
/* Speed mode configuration */
GPIOx->OSPEEDR &= ~(GPIO_OSPEEDER_OSPEEDR0 << (pinpos * 2));
GPIOx->OSPEEDR |= ((uint32_t)(GPIO_InitStruct->GPIO_Speed) << (pinpos * 2));
/* Check Output mode parameters */
assert_param(IS_GPIO_OTYPE(GPIO_InitStruct->GPIO_OType));
/* Output mode configuration*/
GPIOx->OTYPER &= ~((GPIO_OTYPER_OT_0) << ((uint16_t)pinpos)) ;
GPIOx->OTYPER |= (uint16_t)(((uint16_t)GPIO_InitStruct->GPIO_OType) << ((uint16_t)pinpos));
}
/* Pull-up Pull down resistor configuration*/
GPIOx->PUPDR &= ~(GPIO_PUPDR_PUPDR0 << ((uint16_t)pinpos * 2));
GPIOx->PUPDR |= (((uint32_t)GPIO_InitStruct->GPIO_PuPd) << (pinpos * 2));
}
}
}
注释1: 库函数中常用IS_XXXX_XXX函数来防止函数调用时参数出现错误,来自宏定义,起到判断参数是否在规定范围内的作用,返回布尔值。返回的参数将被函数assert_param()调用,实际上是想要实现当参数错误时,需要进行报错提示,但是库函数中并没有实现报错的功能,所以需要自行建立。即建立 函数 assert_failed(),若使用该功能,需要在魔术棒的c/c++中定义USE_FULL_ASSERT
注释2: 从函数体的结构来看,该函数支持或运算,即可以一次性对多个引脚进行操作。为了能够统一化操作,将GPIOx_Pinsource 定义为16位的数值,对应的引脚x的数值对应2^x,因此再调用函数时需要配套的算法进行判断。
这里需要和复用功能配置函数区别, 复用功能函数不支持多引脚同时开启功能。接下来对代码进行分析:
void GPIO_PinAFConfig(GPIO_TypeDef* GPIOx, uint16_t GPIO_PinSource, uint8_t GPIO_AF)
{
uint32_t temp = 0x00;
uint32_t temp_2 = 0x00;
/* Check the parameters */
assert_param(IS_GPIO_ALL_PERIPH(GPIOx));
assert_param(IS_GPIO_PIN_SOURCE(GPIO_PinSource));
assert_param(IS_GPIO_AF(GPIO_AF));
temp = ((uint32_t)(GPIO_AF) << ((uint32_t)((uint32_t)GPIO_PinSource & (uint32_t)0x07) * 4)) ;
GPIOx->AFR[GPIO_PinSource >> 0x03] &= ~((uint32_t)0xF << ((uint32_t)((uint32_t)GPIO_PinSource & (uint32_t)0x07) * 4)) ;
temp_2 = GPIOx->AFR[GPIO_PinSource >> 0x03] | temp;
GPIOx->AFR[GPIO_PinSource >> 0x03] = temp_2;
}
显而易见,在进行相关引脚功能配置时,他没有和初始化函数一样,用for语句进行逐一判断。所以使用只能逐次对单一GPIO使用该函数。
比较初级的单片机,STM32的GPIO功能强化很多,也将很多功能整合了进去。对于GPIO的使用,有如下步骤:
1、配置相应GPIO所挂载的时钟总线。
2、设置GPIO初始化结构体,并使用函数配置(若使用了复用功能,要注意需要再调用复用功能配置函数)