最近开始学stm32,着实感觉到了stm32和51之间的区别,但也有联系,总我感觉32与51之间最大的区别就是在使用某个外设之前,要对该外设进行时钟的使能(以达到降低功耗的目的),和相关配置。
刚学完跑马灯,下面对跑马灯用到的对IO口的配置相关知识分别对应官方库函数和寄存器进行总结。
如有错误或不足,请在下方留言。
文章内容基于正点原子战舰。
IO口的状态
IO口有八大模式:─ 输入浮空( GPIO_Mode_IN_FLOATING = 0x04,)
─ 输入上拉( GPIO_Mode_IPU = 0x48,)
─ 输入下拉( GPIO_Mode_IPD = 0x28,)
─ 模拟输入(GPIO_Mode_AIN = 0x0,)
─ 开漏输出( GPIO_Mode_Out_OD = 0x14,)
─ 推挽式输出( GPIO_Mode_Out_PP = 0x10,)
─ 推挽式复用功能(GPIO_Mode_AF_PP = 0x18)
─ 开漏复用功能( GPIO_Mode_AF_OD = 0x1C,)
IO口有三种速 -2MHZ( GPIO_Speed_2MHz=1,)
-10MHz( GPIO_Speed_10MHz = 1,)
-50MHz( GPIO_Speed_50MHz=3,) //当看到这些配置相应的值是否会感到疑惑呢,稍后讲解。
跑马灯的原理图
显然led的硬件连接很简单分别连接了IO口PE5和PB5,另一端串联一个电阻共同接地。
实验的代码分析
我们知道任何外设的驱动都要使能相应的时钟,首先看stm32系统的时钟框图
经查阅资料可知,GPIO的时钟在APB2的外设时钟使能寄存器上,相关函数的定义在stm32f10x_rcc.h中 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; //配置APB2ENBR寄存器
}
else
{
RCC->APB2ENR &= ~RCC_APB2Periph; //配置APB2ENR寄存器
}
}
//与该函数相关的一些宏定义 检查RCC_APB2Periph参数的有效性
#define RCC_APB2Periph_AFIO ((uint32_t)0x00000001)
#define RCC_APB2Periph_GPIOA ((uint32_t)0x00000004)
#define RCC_APB2Periph_GPIOB ((uint32_t)0x00000008)
#define RCC_APB2Periph_GPIOC ((uint32_t)0x00000010)
#define RCC_APB2Periph_GPIOD ((uint32_t)0x00000020)
#define RCC_APB2Periph_GPIOE ((uint32_t)0x00000040)
#define RCC_APB2Periph_GPIOF ((uint32_t)0x00000080)
#define RCC_APB2Periph_GPIOG ((uint32_t)0x00000100)
#define RCC_APB2Periph_ADC1 ((uint32_t)0x00000200)
#define RCC_APB2Periph_ADC2 ((uint32_t)0x00000400)
#define RCC_APB2Periph_TIM1 ((uint32_t)0x00000800)
#define RCC_APB2Periph_SPI1 ((uint32_t)0x00001000)
#define RCC_APB2Periph_TIM8 ((uint32_t)0x00002000)
#define RCC_APB2Periph_USART1 ((uint32_t)0x00004000)
#define RCC_APB2Periph_ADC3 ((uint32_t)0x00008000)
#define RCC_APB2Periph_TIM15 ((uint32_t)0x00010000)
#define RCC_APB2Periph_TIM16 ((uint32_t)0x00020000)
#define RCC_APB2Periph_TIM17 ((uint32_t)0x00040000)
#define RCC_APB2Periph_TIM9 ((uint32_t)0x00080000)
#define RCC_APB2Periph_TIM10 ((uint32_t)0x00100000)
#define RCC_APB2Periph_TIM11 ((uint32_t)0x00200000)
#define IS_RCC_APB2_PERIPH(PERIPH) ((((PERIPH) & 0xFFC00002) == 0x00) && ((PERIPH) != 0x00))
//与该函数相关的枚举变量定义 检查NewState参数的有效性
typedef enum {DISABLE = 0, ENABLE = !DISABLE} FunctionalState;
#define IS_FUNCTIONAL_STATE(STATE) (((STATE) == DISABLE) || ((STATE) == ENABLE))
我们来看这个使能GPIO时钟函数的源代码,函数没有返回值,接受两个参数 unint32_t(unsigned int)类型的RCC_APB2Periph和FunctionalState(枚举变量)类型的NewState 。
函数首先检查传入值的有效性,我们可以看到和RCC_APB2Periph相关的宏定义中,规定了相关参数的取值范围,相关的值实际上是APB2 外设时钟使能寄存器(RCC_APB2ENR)相关位的配置,在这里我们也可以看出库函数实际上就是操作寄存器,对操作寄存器进行了一系列的封装。我们这里从硬件来看需要启动GPIOB和GPIOE的时钟使能,则RCC_APB2Periph分别为RCC_APB2Periph_GPIOB,RCC_APB2Periph_GPIOE。再看参数NewState 有相关定义可知{DISABLE = 0, ENABLE = !DISABLE}则当NewState为ENABLE时,开启使能,GPIO相关使能完毕。(实际上库函数就是对寄存器RCC_APB2ENR的相关操作,理解该函数便可写出相关的寄存器版本)
与51单片机不同的是每次使用IO口还要对IO口进行初始化,配置IO的模式(MODE),速度(SPEED)及针脚(PIN),
GPIO初始化函数 void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct),其源代码:
/**
* @brief Initializes the GPIOx peripheral according to the specified
* parameters in the GPIO_InitStruct.
// 根据指定初始化GPIOx外设GPIO_InitStruct中的参数。
* @param GPIOx: where x can be (A..G) to select the GPIO peripheral.
// 其中x可以是(A..G)来选择GPIO外设
* @param GPIO_InitStruct: pointer to a GPIO_InitTypeDef structure that
* contains the configuration information for the specified GPIO peripheral.
// GPIO_InitStruct:指向GPIO_InitTypeDef结构的指针包含指定GPIO外设的配置信息。
* @retval None
*/
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct)//GPIO初始化函数
{
uint32_t currentmode = 0x00, currentpin = 0x00, pinpos = 0x00, pos = 0x00;
uint32_t tmpreg = 0x00, pinmask = 0x00;
//设置相关变量 currentmode存储CRL CRH配置信息 tmpreg 存储当前及最终CRL CRH配置信息
/* Check the parameters */
assert_param(IS_GPIO_ALL_PERIPH(GPIOx)); //检查GPIO的有效性
assert_param(IS_GPIO_MODE(GPIO_InitStruct->GPIO_Mode)); //检查GPIO_Mode的有效性
assert_param(IS_GPIO_PIN(GPIO_InitStruct->GPIO_Pin)); //检查GPIO_Pin的有效性
/*---------------------------- GPIO Mode Configuration GPIO模式配置 -----------------------*/
currentmode = ((uint32_t)GPIO_InitStruct->GPIO_Mode) & ((uint32_t)0x0F);
//取GPIO_Mode中的低四位,这里的做法和GPIO_Mode的值有关,可自行参考结构体中的值进验证
if ((((uint32_t)GPIO_InitStruct->GPIO_Mode) & ((uint32_t)0x10)) != 0x00)
//该判断的意思是如果模式GPIO_Mode的第五位不是零就执行该语句,由结构体中的模式的值可得如果
//第五位为1,则该模式为输出模式
{
/* Check the parameters *///检查速度的GPIO_Speed的有效性
assert_param(IS_GPIO_SPEED(GPIO_InitStruct->GPIO_Speed));
/* Output mode */
currentmode |= (uint32_t)GPIO_InitStruct->GPIO_Speed;
//如果为输出模式则用模式GPIO_Mode的低四位|GPIO_Speed,便可得到输出模式寄存器中相关配置的
//值,可自行验证
}
/*---------------------------- GPIO CRL Configuration GPIO CRL配置------------------------*/
/* Configure the eight low port pins */ //设置低八位 CRL寄存器
if (((uint32_t)GPIO_InitStruct->GPIO_Pin & ((uint32_t)0x00FF)) != 0x00)
//由GPIO_Pin的范围可知,该语句的意思是判断0-7脚是否有定义
{
tmpreg = GPIOx->CRL; //获取当前CRL配置
for (pinpos = 0x00; pinpos < 0x08; pinpos++) //循环检查引脚 ,判断引脚位置
{
pos = ((uint32_t)0x01) << pinpos; //循环一次pos便左移一次
/* Get the port pins position */ //判断引脚位置
currentpin = (GPIO_InitStruct->GPIO_Pin) & pos;
// 通过pos&GPIO_Pin(传入参数的引脚)对比判断
if (currentpin == pos) //如果相等则当前循环值刚好是引脚位置
{
pos = pinpos << 2; //pos左移二位(扩大四倍),目的是配置CRL寄存器
/*我们知道IO口配置寄存器CRL和CRH都是32位寄存器,把pos*4即可得到对应引脚在相关配置寄
存器中对应的位置*/
/* 清除相应的低控制寄存器位*/
/* Clear the corresponding low control register bits */
pinmask = ((uint32_t)0x0F) << pos; //pinmask设置CRL对应的位上的pinmask为1
tmpreg &= ~pinmask; //将CRL对应位清零
/* Write the mode configuration in the corresponding bits */
/*将模式配置写入相应的位*/
tmpreg |= (currentmode << pos); //对应位写入
/* Reset the corresponding ODR bit */
/*重置相应的ODR位*/
if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPD) //如果是下拉输入
{
GPIOx->BRR = (((uint32_t)0x01) << pinpos); //相应位置零 BRR寄存器间接控制了
//ODR寄存器
}
else //否则
{
/* Set the corresponding ODR bit */
if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPU) 如果是上拉输入
{
GPIOx->BSRR = (((uint32_t)0x01) << pinpos); //相应位置一,BSRR寄存器间接控
//制了ODR寄存器
上一篇:32位嵌入式系统的字节对齐
下一篇:STM32流水灯的几种实现方法
推荐阅读最新更新时间:2024-11-13 09:19
设计资源 培训 开发板 精华推荐
- MC34071DR2G 低压快速数模转换器的典型应用
- 使用 Diodes Incorporated 的 ZR78L028 的参考设计
- LT4275CHDD LTPoE++ 38.7W 至 90W 受电设备的典型应用电路
- SG3525A驱动功率场效应管用于脉宽调制器控制电路的典型应用
- AT89S52最小系统板
- KIT33932EKEVBE: 评估套件 - MC33932EK,双通道5A油门控制
- TEA1721BDB1065: TEA1721 Universal Mains White Goods Flyback SMPS Demo Board
- ADM1186-2ARQZ 四路电压升降序器和监视器的典型应用电路
- 夏普SBW115回音壁WIFI多功能遥控按键板
- LT3091IFE 简单电缆压降补偿的典型应用