例子为单片机的“Hello World”级的流水灯实验——虽然只有一个,其中并不是将完整的代码给出,只是给出关键部分来说明“如何调用ST公司的的库来完成对硬件的控制,以及对库文件代码进行跟踪和分析至寄存器级”。所以从第一段代码往下看就可以了,要用到的函数和变量大部分会说明,至于寄存器级的,那就只能翻手册了。
GPIO(General Purpose Input/Output) - 通用输入/输出
main.c :此函数为主函数,控制LED,亮1s,灭1s
1 2 3 4 5 6 7 8 9 10 11 12 |
int main(void) { //LED初始化 LED_Configuration(); while(1) { GPIO_SetBits(GPIOB,GPIO_Pin_5); //置为1 Systick_DelayMs(1000); //延时1s,自己实现的,暂不说明 GPIO_ResetBits(GPIOB,GPIO_Pin_5); //置为0 Systick_DelayMs(1000); //延时1s } } |
stm32f10x_gpio.c GPIO_SetBits和GPIO_ResetBits
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
/** * @brief Sets the selected data port bits. * @param GPIOx: where x can be (A..G) to select the GPIO peripheral. * @param GPIO_Pin: specifies the port bits to be written. * This parameter can be any combination of GPIO_Pin_x where x can be (0..15). * @retval None */ void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin) { /* Check the parameters */ assert_param(IS_GPIO_ALL_PERIPH(GPIOx)); assert_param(IS_GPIO_PIN(GPIO_Pin)); GPIOx->BSRR = GPIO_Pin; } /** |
led.c 需要用到以下库文件:stm32f10x_rcc.c,stm32f10x_gpio.c-->STM32的I/O口初始化函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
void LED_Configuration(void) { /*设置PB.5为输出模式,用于LED*/ //定义一个GPIO数据结构,存放设置的参数 GPIO_InitTypeDef GPIO_InitStructure; //要使用一个I/O口时,需要先打开相应I/O口所在口的时钟,如使能PB端口时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //先设置要配置的引脚,LED0-->PB.5 端口配置 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //配置为推挽输出模式 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //配置I/O口速度为50MHz GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //根据设定参数初始化GPIOB GPIO_Init(GPIOB, &GPIO_InitStructure); } |
下面为LED_Configuration中涉及到的结构体变量和一些其他变量的出处:
stm32f10x_gpio.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
/** * @brief GPIO Init structure definition */ typedef struct GPIOSpeed_TypeDef GPIO_Speed; /*!< Specifies the speed for the selected pins. GPIOMode_TypeDef GPIO_Mode; /*!< Specifies the operating mode for the selected pins. |
stm32f10x_gpio.h :每个GPIO口有0x10个。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
/** @defgroup GPIO_pins_define * @{ */ #define GPIO_Pin_0 ((uint16_t)0x0001) /*!< Pin 0 selected */ |
stm32f10x_gpio.h : GPIO的模式,比51多多了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
/** * @brief Configuration Mode enumeration */ typedef enum |
具体区别翻手册,也没记那么清楚~
stm32f10x_gpio.h : 赋予GPIO的运行频率
1 2 3 4 5 6 7 8 9 10 |
/** * @brief Output Maximum frequency selection */ typedef enum |
枚举变量:GPIO_Speed_10MHz =1;GPIO_Speed_2MHz =2;GPIO_Speed_50MHz =3,速度写着呢,选一个就ok了~
stm32f10x.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
#define PERIPH_BASE ((uint32_t)0x40000000) /*!< Peripheral base address in the alias region */
#define APB2PERIPH_BASE (PERIPH_BASE + 0x10000) #define GPIOA_BASE (APB2PERIPH_BASE + 0x0800) #define GPIOA ((GPIO_TypeDef *) GPIOA_BASE) |
GPIOA=GPIOA_BASE=0x40000000+0x10000+0x800
通过查询STM32微控制器开发手册可以得知,STM32的外设起始基地址为0x40000000,而APB2总线设备起始地址相对于外设基地址的偏移量为0x10000,GPIOA设备相对于APB2总线设备起始地址偏移量为0x800。
注意:以上只是很小的一部分定义,stm32f10x.h文件的代码行数有8000多行。
下面为LED_Configuration中再配置完参数之后,将结构体中的参数写入到寄存器中的GPIO_Init初始化函数:
stm32f10x_gpio.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 |
/** * @brief Initializes the GPIOx peripheral according to the specified * parameters in the GPIO_InitStruct. * @param GPIOx: where x can be (A..G) to select the GPIO peripheral. * @param GPIO_InitStruct: pointer to a GPIO_InitTypeDef structure that * contains the configuration information for the specified GPIO peripheral. * @retval None */ 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; /* Check the parameters */ //用断言来检查参数是否正确 assert_param(IS_GPIO_ALL_PERIPH(GPIOx)); assert_param(IS_GPIO_MODE(GPIO_InitStruct->GPIO_Mode)); assert_param(IS_GPIO_PIN(GPIO_InitStruct->GPIO_Pin)); /*---------------------------- GPIO Mode Configuration -----------------------*/ //将工作模式暂存至currentmode变量中 currentmode = ((uint32_t)GPIO_InitStruct->GPIO_Mode) & ((uint32_t)0x0F); //如果欲设置为任意一种输出模式,则再检查“翻转速率”参数是否正确 /*---------------------------- GPIO CRL Configuration ------------------------*/ for(pinpos = 0x00; pinpos < 0x08; pinpos++) if(currentpin == pos) /* Reset the corresponding ODR bit */ else //写入低8位引脚配置字 /*---------------------------- GPIO CRH Configuration ------------------------*/ for(pinpos = 0x00; pinpos < 0x08; pinpos++) if(currentpin == pos) /* Reset the corresponding ODR bit */ /* Set the corresponding ODR bit */ GPIOx->CRH = tmpreg; |
这段程序的流程:首先检查由结构体变量GPIO_InitStructure所传入的参数是否正确,然后对GPIO寄存器进行“读出->修改->写入”操作,完成对GPIO设备的配置工作。
总结起来就是:固件库首先将各个设备所有的寄存器的配置字进行预先定义在stm32f10x.h文件中,然后封装在结构或枚举变量中(存在相对应的stm32f10x_xxx.h,其中xxx代表gpio,spi,exti等外设模块),待用户调用对应的固件库函数时,会根据用户传入的参数从这些封装好的结构体或枚举变量中取出对应的配置字,最后写入寄存器中,完成对底层寄存器的配置。
stdint.h 一些具有可移植性的变量重定义
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
/* * 'signed' is redundant below, except for 'signed char' and if * the typedef is used to declare a bitfield. * '__int64' is used instead of 'long long' so that this header * can be used in --strict mode. */ /* 7.18.1.1 */ /* exact-width signed integer types */ /* exact-width unsigned integer types */ /* 7.18.1.2 */ /* smallest type of at least n bits */ /* minimum-width unsigned integer types */ /* 7.18.1.3 */ /* fastest minimum-width signed integer types */ /* fastest minimum-width unsigned integer types */ /* 7.18.1.4 integer types capable of holding object pointers */ /* 7.18.1.5 greatest-width integer types */ |