直接使用寄存器地址来进行STM32开发要对每一个寄存器的地址通过查找芯片手册,对每一个寄存器的各个位的含义也要查清楚,然后决定对那个地址的哪些位进行怎样的设置。这种编程效率低,程序的可移植性差,程序的可读性差。
STM32提供了对外设寄存器操作的功能函数,对寄存器操作只需要调用相应的库函数就可以,一般函数名单词组合与功能密切相关,增强了程序的可读性。
1、以CMSIS固件库项目为基础,在项目文件夹下新增"Lib"文件夹,在"Lib"文件夹下新增"inc"和"src"文件夹,在"inc"文件夹中复制"stm32f10x_gpio.h"和"stm32f10x_rcc.h"两个头文件,在"src"文件夹中复制"stm32f10x_gpio.c"和"stm32f10x_rcc.c"两个库函数程序文件,分别包含RCC和GPIO操作相关的库函数。
2、在“项目条目管理”对话框中添加"Lib"组,并向组中添加两个库函数程序文件。
3、修改"Include Paths",添加".\Lib\inc"路径
4、重写main.c中的main函数
① 包含需要的头文件:"stm32f10x_gpio.h"和"stm32f10x_rcc.h"
② 使能GPIOC时钟
在RCC库函数中定义了RCC_APB2PeriphClockCmd函数,函数原型为:
void RCC_APB2PeriphClockCmd ( uint32_t RCC_APB2Periph, FunctionalState NewState )
其中参数RCC_APB2Periph取值为在"stm32f10x_rcc.h"中预定义的值,这里对GPIOC的操作为RCC_APB2Periph_GPIOC,定义如下:
这个值与前面程序中的表达式"0x1<<4"得到的值是一致的。
函数的第二个参数NewState取值为FunctionState类型,FunctionState类型是在"stm32f10x_rcc.c"文件中定义的一个枚举类型,有DISABLE和ENABLE两个取值,定义如下:
现在要使能GPIOC,则调用函数如下:
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOC, ENABLE );
③ 配置GPIOC_0为推挽式输出,50MHz速度。
对GPIO引脚的配置调用"stm32f10x_gpio.c"中的库函数GPIO_Init实现,函数原型为:
void GPIO_Init ( GPIO_TypeDef * GPIOx, GPIO_InitTypeDef * GPIO_InitStruct )
其中第一个参数GPIOx的取值在"stm32f10x.h"中定义,这里用GPIOC;第二个参数GPIO_InitStruct为GPIO_InitTypeDef指针类型,GPIO_InitTypeDef类型在"stm32f10x_gpio.h"文件中定义:
这个结构体中的GPIO_Pin为IO口的引脚编号,这里为第一个LED灯,则取0;
GPIO_Speed为GPIOSpeed_TypeDef类型,GPIOSpeed_TypeDef类型在"stm32f10x_gpio.h"文件中定义:
为枚举类型,有三个枚举值。这里取GPIO_Speed_50MHz;
GPIO_Mode为GPIOMode_TypeDef类型,GPIOMode_TypeDef类型在"stm32f10x_gpio.h"文件中定义:
这些枚举值分别为:
(1)GPIO_Mode_AIN 模拟输入
(2)GPIO_Mode_IN_FLOATING 浮空输入
(3)GPIO_Mode_IPD 下拉输入
(4)GPIO_Mode_IPU 上拉输入
(5)GPIO_Mode_Out_OD 开漏输出
(6)GPIO_Mode_Out_PP 推挽输出
(7)GPIO_Mode_AF_OD 复用开漏输出
(8)GPIO_Mode_AF_PP 复用推挽输出
这里选择GPIO_Mode_Out_PP。
要配置GPIOC_0的工作模式,首先用GPIO_InitTypeDef类型定义一个变量,然后给这个结构体变量的成员赋值,最后调用GPIO_Init函数完成配置:
GPIO_InitTypeDef GPIOC_0_mode;
GPIOC_0_mode.GPIO_Pin = GPIO_Pin_0;
GPIOC_0_mode.GPIO_Speed = GPIO_Speed_50MHz;
GPIOC_0_mode.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOC, &GPIOC_0_mode);
④ 通过GPIO_ResetBits和GPIO_SetBits函数来控制LED1的复位和置位,函数原型如下:
void GPIO_ResetBits ( GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin ) //复位函数
void GPIO_SetBits ( GPIO_TypeDef * GPIOx, uint16_t GPIO_Pin ) //置位函数
函数的参数分别为哪个GPIO口和哪个引脚。
如果对GPIOC_0复位,则:GPIO_ResetBits( GPIOC, GPIO_Pin_0) ;
如果对GPIOC_0置位,则:GPIO_SetBits( GPIOC, GPIO_Pin_0) ;
最后的main.c程序如下:
#include "stm32f10x.h" #include "stm32f10x_rcc.h" #include "stm32f10x_gpio.h" void delay(int t) { int i; for( ;t>0; t--) for(i=0;i<1000;i++); } int main() { GPIO_InitTypeDef GPIOC_0_mode; RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOC, ENABLE ); //使能GPIOC时钟 GPIOC_0_mode.GPIO_Pin = GPIO_Pin_0; GPIOC_0_mode.GPIO_Speed = GPIO_Speed_50MHz; GPIOC_0_mode.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_Init(GPIOC, &GPIOC_0_mode); //配置GPIOC_0引脚为推挽输出,50MHz速度 while(1) { GPIO_ResetBits( GPIOC, GPIO_Pin_0); //将GPIOC_0复位 delay(1000); GPIO_SetBits( GPIOC, GPIO_Pin_0); //将GPIOC_0置位 delay(1000); } } |
5、连接错误及其解决方法。
在上述项目中,最后完成项目配置和编程后,编译连接时出现如下错误:
出现这个错误是由于STM32外设库函数的开发中用到了断言机制。正常使用库函数时,需要包含"stm32f10x_conf.h"头文件,在该文件中有如下定义
/* Exported macro ------------------------------------------------------------*/ #ifdef USE_FULL_ASSERT /** * @brief The assert_param macro is used for function's parameters check. * @param expr: If expr is false, it calls assert_failed function which reports * the name of the source file and the source line number of the call * that failed. If expr is true, it returns no value. * @retval None */ #define assert_param(expr) ((expr) ? (void)0 : assert_failed((uint8_t *)__FILE__, __LINE__)) /* Exported functions ------------------------------------------------------- */ void assert_failed(uint8_t* file, uint32_t line); #else #define assert_param(expr) ((void)0) #endif /* USE_FULL_ASSERT */ |
在该头文件中定义了assert_param宏,根据项目配置,如果设置了"USE_FULL_ASSERT"参数,则对函数参数进行检查,检查不成功转到"assert_failed"函数处理;如果没有设置使用断言,则相当于不对参数进行检测。
解决方法:将这一段宏定义直接复制到"stm32f10x.h"文件中,因为所有的外设库函数文件都直接或间接地包含了该头文件。
6、编译连接项目,下载程序,开发板的LED1闪烁。