板子使用的是洋桃电子的开发板,核心板上面的两个led灯连接的是PB0和PB1两个引脚,如下图所示
可以看到这个电路图,如果将PB0的引脚的信号设置为高电平,则电流会从LED灯的左边流向右边,即点亮LED灯。
对stm32的操作有两种方法,一种是使用固件库函数,另一种是直接使用寄存器的方法。使PB0引脚变成高电平的方法
一、使用固件库函数
1、使能时钟
从上图的STM32的结构图可以看出,GPIOB是在APB2上面的外设,所以使能时钟应该选择APB2。
在固件库中的stm32f10x_rcc.c文件中有库函数void RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState)
void RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState)
{
/* Check the parameters */
assert_param(IS_RCC_APB2_PERIPH(RCC_APB2Periph));
assert_param(IS_FUNCTIONAL_STATE(NewState));
if (NewState != DISABLE)
{
RCC->APB2ENR |= RCC_APB2Periph;
}
else
{
RCC->APB2ENR &= ~RCC_APB2Periph;
}
}```
函数的第一个参数RCC_APB2Periph是一个32位的无符号整型数据,用来指定需要激活的哪一个时钟线对对应的外设,我们此处需要激活的是APB2的外设。在这个文件中也给我们指出了对应参数的选择。
```c
/**
* @brief Enables or disables the High Speed APB (APB2) peripheral clock.
* @param RCC_APB2Periph: specifies the APB2 peripheral to gates its clock.
* This parameter can be any combination of the following values:
* @arg RCC_APB2Periph_AFIO, RCC_APB2Periph_GPIOA, RCC_APB2Periph_GPIOB,
* RCC_APB2Periph_GPIOC, RCC_APB2Periph_GPIOD, RCC_APB2Periph_GPIOE,
* RCC_APB2Periph_GPIOF, RCC_APB2Periph_GPIOG, RCC_APB2Periph_ADC1,
* RCC_APB2Periph_ADC2, RCC_APB2Periph_TIM1, RCC_APB2Periph_SPI1,
* RCC_APB2Periph_TIM8, RCC_APB2Periph_USART1, RCC_APB2Periph_ADC3,
* RCC_APB2Periph_TIM15, RCC_APB2Periph_TIM16, RCC_APB2Periph_TIM17,
* RCC_APB2Periph_TIM9, RCC_APB2Periph_TIM10, RCC_APB2Periph_TIM11
* @param NewState: new state of the specified peripheral clock.
* This parameter can be: ENABLE or DISABLE.
* @retval None
*/
即这里的第一个参数我们选择RCC_APB2Periph_GPIOB,因为我们的GPIO是在选择的GPIOB,另一个参数选择ENABLE即可。即调用函数即可使能对应的外设的时钟。
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE)
至此,时钟设置就结束了。
2、选择引脚
此处选择的引脚是PB0和PB1,在选择引脚的时候需要设置三个参数,一是我们选择的是哪一个引脚,这里选择的PB0和PB1,二是需要设置一个速度,三是设置一个输出或者输入方式,一般led灯对应的是输出,按键对应的是输入。
在stm32f10x_gpio.h中官方给我们定义了一个名为GPIO_InitTypeDef结构体用来设置引脚,结构体具体内容如下
**typedef struct
{
uint16_t GPIO_Pin; /*!< Specifies the GPIO pins to be configured.
This parameter can be any value of @ref GPIO_pins_define */
GPIOSpeed_TypeDef GPIO_Speed; /*!< Specifies the speed for the selected pins.
This parameter can be a value of @ref GPIOSpeed_TypeDef */
GPIOMode_TypeDef GPIO_Mode; /*!< Specifies the operating mode for the selected pins.
This parameter can be a value of @ref GPIOMode_TypeDef */
}GPIO_InitTypeDef;**
里面有三个成员,对应上面所述的三个参数的设置。其类型选择如下
#define GPIO_Pin_0 ((uint16_t)0x0001) /*!< Pin 0 selected */
#define GPIO_Pin_1 ((uint16_t)0x0002) /*!< Pin 1 selected */
#define GPIO_Pin_2 ((uint16_t)0x0004) /*!< Pin 2 selected */
#define GPIO_Pin_3 ((uint16_t)0x0008) /*!< Pin 3 selected */
#define GPIO_Pin_4 ((uint16_t)0x0010) /*!< Pin 4 selected */
#define GPIO_Pin_5 ((uint16_t)0x0020) /*!< Pin 5 selected */
#define GPIO_Pin_6 ((uint16_t)0x0040) /*!< Pin 6 selected */
#define GPIO_Pin_7 ((uint16_t)0x0080) /*!< Pin 7 selected */
#define GPIO_Pin_8 ((uint16_t)0x0100) /*!< Pin 8 selected */
#define GPIO_Pin_9 ((uint16_t)0x0200) /*!< Pin 9 selected */
#define GPIO_Pin_10 ((uint16_t)0x0400) /*!< Pin 10 selected */
#define GPIO_Pin_11 ((uint16_t)0x0800) /*!< Pin 11 selected */
#define GPIO_Pin_12 ((uint16_t)0x1000) /*!< Pin 12 selected */
#define GPIO_Pin_13 ((uint16_t)0x2000) /*!< Pin 13 selected */
#define GPIO_Pin_14 ((uint16_t)0x4000) /*!< Pin 14 selected */
#define GPIO_Pin_15 ((uint16_t)0x8000) /*!< Pin 15 selected */
#define GPIO_Pin_All ((uint16_t)0xFFFF) /*!< All pins selected */
typedef enum
{
GPIO_Speed_10MHz = 1,
GPIO_Speed_2MHz,
GPIO_Speed_50MHz
}GPIOSpeed_TypeDef;
typedef enum
{ GPIO_Mode_AIN = 0x0,
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 = 0x1C,
GPIO_Mode_AF_PP = 0x18
}GPIOMode_TypeDef;
所以设置引脚就可以先定义结构体GPIO_InitTypeDef对引脚进行设置,具体代码如下:
GPIO_InitTypeDef InitStrcuture;//定义一个结构体以便为其成员赋值
InitStrcuture.GPIO_Pin = GPIO_LED_PIN_0 | GPIO_LED_PIN_1;
InitStrcuture.GPIO_Speed = GPIO_Speed_10MHz;
InitStrcuture.GPIO_Mode = GPIO_Mode_Out_PP;
这样就完成了对PB0和PB1引脚的设置,当然设置完成之后需要将该设置的引脚进行初始化操作,这里利用stm32f10x_gpio.c文件的函数GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct)
可以看到该函数第一个参数这里应该选择GPIOB,而第二个参数就是之前定义的结构体
GPIO_Init(GPIOB, &InitStrcuture);
至此,就将对应的引脚设置完成。
3、对引脚赋值
接下来就可以对该引脚的电平进行操作了,这里使用固件库函数
GPIO_SetBits(GPIOB,GPIO_Pin_0);
GPIO_ResetBits(GPIOB,GPIO_Pin_1);
这样就可以使得PB0为高电平,PB1为低电平,从而使得led1亮,led2灭。
其完整代码如下
#include "stm32f10x.h"
int main(void)
{
GPIO_InitTypeDef InitStrcuture;//定义一个结构体以便为其成员赋值
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);//打开对应外设的时钟
InitStrcuture.GPIO_Pin = GPIO_Pin_0| GPIO_Pin_1;
InitStrcuture.GPIO_Speed = GPIO_Speed_10MHz;
InitStrcuture.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOB, &InitStrcuture);
while(1)
{
GPIO_SetBits(GPIOB,GPIO_Pin_0);
GPIO_ResetBits(GPIOB,GPIO_Pin_1);
}
}
二、使用寄存器
使用寄存器操作引脚,步骤与固件库函数的是极为类似的,唯一不同的是需要自己根据STM32参考手册去寻找相应外设或引脚的地址。个人觉得固件库的方法更为简单,但寄存器的学习也是必不可少的内容,下面用寄存器的方法点亮led灯。
这里同样是三部曲,一使能对应的时钟,二是配置IO口,三是对引脚赋值。
1、使能时钟
首先找到RCC时钟的地址,从下图可以看到RCC的基地址为0x40021000
使能时钟是通过APB2外设时钟使能寄存器(RCC_APB2ENR)来进行操作的。
可以从上图看到,该寄存器的偏移地址为0x18,如果在该地址的第4位填写1的话,则APB2上的GPIOB外设的时钟将被激活。
*(unsigned int*)0x40021018 = 0x8;
这样GPIO外设时钟就被激活了。
2、配置IO口
从参考手册中的寄存器映像中可以找到APB2所对应的地址范围,在这个范围中又可以找到GPIOB所对应的地址范围,可以看到这个基地址为0x40010C00
这里配置IO口的寄存器名为端口配置低寄存器(GPIOx_CRL) ,0-7为低寄存器,8-15为高寄存器。
可以看到只要将该地址的值配置好就可以设置相应的模式了。
*(unsigned int*)0x40010C00 = 0x11;
3、引脚赋值
引脚赋值使用的寄存器为端口输出数据寄存器(GPIOx_ODR)
*(unsigned int*)0x40010C0C |= 0x3
至此使得两个led灯亮。完整代码如下
#include "stm32f10x.h"
int main(void)
{
*(unsigned int*)0x40021018 = 0x8;
*(unsigned int*)0x40010C00 = 0x11;
*(unsigned int*)0x40010C0C = 0x3;
}