Stm32高级驱动编程-第1章 GPIO驱动

如果需要其它实现方法及商业合作,邮件联系[email protected]

1.1 GPIO封装需求

对于初使化一个GPIO引脚,比如说一个LED灯的控制引脚GPIOA上的第GPIO_PIN_1,通常做法是这样子:

GPIO_InitTypeDef GPIO_InitStruct;
__HAL_RCC_GPIOA_CLK_ENABLE();
GPIO_InitStruct.Pin = GPIO_PIN_0;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

在这里插入代码片`如果硬件发生变化,现在LED灯的控制引脚改放在GPIOB上的第GPIO_PIN_2上,我们至少需要修改三处地方,这样非常不利于项目的移植。那么,我们一起尝试着能不能只更改一次地方就能满足上述需求。
对于初使化配置来说,我们其实只关心初使化引脚的位置、模式、上拉、速度、复用(F4支持)这5个参数。

1.2 GPIO 引脚编号

为使所有的GPIO引脚唯一,我们需要对GPIOA至GPIOB共计32个引脚(可以扩展)进行统一编号。

typedef  enum {
    wPIN_NO                             =     (wuint)0u,  
    #define  wPIN_PIN_MASK                    (wuint)0x000fu
    #define  wPIN_GPIO_MASK                   (wuint)0x00f0u
    
    wPIN_A0                             =     (wuint)0x0010u + 0, 
    wPIN_A1                             =     (wuint)0x0010u + 1, 
    wPIN_A2                             =     (wuint)0x0010u + 2, 
    wPIN_A3                             =     (wuint)0x0010u + 3, 
    wPIN_A4                             =     (wuint)0x0010u + 4, 
    wPIN_A5                             =     (wuint)0x0010u + 5, 
    wPIN_A6                             =     (wuint)0x0010u + 6, 
    wPIN_A7                             =     (wuint)0x0010u + 7, 
    wPIN_A8                             =     (wuint)0x0010u + 8, 
    wPIN_A9                             =     (wuint)0x0010u + 9, 
    wPIN_A10                            =     (wuint)0x0010u + 10, 
    wPIN_A11                            =     (wuint)0x0010u + 11, 
    wPIN_A12                            =     (wuint)0x0010u + 12, 
    wPIN_A13                            =     (wuint)0x0010u + 13, 
    wPIN_A14                            =     (wuint)0x0010u + 14, 
   	wPIN_A15                     		=     (wuint)0x0010u + 15, 
    wPIN_B0                             =     (wuint)0x0020u + 0, 
    wPIN_B1                             =     (wuint)0x0020u + 1, 
    wPIN_B2                             =     (wuint)0x0020u + 2, 
    wPIN_B3                             =     (wuint)0x0020u + 3, 
    wPIN_B4                             =     (wuint)0x0020u + 4, 
    wPIN_B5                             =     (wuint)0x0020u + 5, 
    wPIN_B6                             =     (wuint)0x0020u + 6, 
    wPIN_B7                             =     (wuint)0x0020u + 7, 
    wPIN_B8                             =     (wuint)0x0020u + 8, 
    wPIN_B9                             =     (wuint)0x0020u + 9, 
    wPIN_B10                            =     (wuint)0x0020u + 10, 
    wPIN_B11                            =     (wuint)0x0020u + 11, 
    wPIN_B12                            =     (wuint)0x0020u + 12, 
    wPIN_B13                            =     (wuint)0x0020u + 13, 
    wPIN_B14                            =     (wuint)0x0020u + 14, 
	wPIN_B15                       	 	=     (wuint)0x0020u + 15,
		…… …… ……
}

1.3 GPIO 引脚直接初使化函数

uint16_t wGpio_Init(wPin pin,uint32_t Mode,uint32_t Pull,uint32_t Speed)
{/*>>直接初使化GPIO引脚*/
    
    GPIO_InitTypeDef GPIO_InitStruct;
    GPIO_TypeDef *GPIO;
    
	switch(wPIN_GPIO_MASK & pin)
    {
        case wPIN_A0:{GPIO=GPIOA;__HAL_RCC_GPIOA_CLK_ENABLE();}break;
        case wPIN_B0:{GPIO=GPIOB;__HAL_RCC_GPIOB_CLK_ENABLE();}break;	
        case wPIN_C0:{GPIO=GPIOC;__HAL_RCC_GPIOC_CLK_ENABLE();}break;
        case wPIN_D0:{GPIO=GPIOD;__HAL_RCC_GPIOD_CLK_ENABLE();}break;							
        …… …… ……//省略部分代码
        default:return NULL ;
    }
	switch(wPIN_PIN_MASK & pin)
    {
        case wPIN_0:{GPIO_InitStruct.Pin = GPIO_PIN_0;}break;
        case wPIN_1:{GPIO_InitStruct.Pin = GPIO_PIN_1;}break;	
        case wPIN_2:{GPIO_InitStruct.Pin = GPIO_PIN_2;}break;
        case wPIN_3:{GPIO_InitStruct.Pin = GPIO_PIN_3;}break;							
        case wPIN_4:{GPIO_InitStruct.Pin = GPIO_PIN_4;}break;
        case wPIN_5:{GPIO_InitStruct.Pin = GPIO_PIN_5;}break;							
        case wPIN_6:{GPIO_InitStruct.Pin = GPIO_PIN_6;}break;        
        case wPIN_7:{GPIO_InitStruct.Pin = GPIO_PIN_7;}break;
        case wPIN_8:{GPIO_InitStruct.Pin = GPIO_PIN_8;}break;
        case wPIN_9:{GPIO_InitStruct.Pin = GPIO_PIN_9;}break;	
        case wPIN_10:{GPIO_InitStruct.Pin = GPIO_PIN_10;}break;
        case wPIN_11:{GPIO_InitStruct.Pin = GPIO_PIN_11;}break;						
        case wPIN_12:{GPIO_InitStruct.Pin = GPIO_PIN_12;}break;
        case wPIN_13:{GPIO_InitStruct.Pin = GPIO_PIN_13;}break;						
        case wPIN_14:{GPIO_InitStruct.Pin = GPIO_PIN_14;}break;        
        case wPIN_15:{GPIO_InitStruct.Pin = GPIO_PIN_15;}break;
        default: return NULL;
    }
    
    GPIO_InitStruct.Mode = Mode;
    if(IS_GPIO_PULL(Pull))   { GPIO_InitStruct.Pull = Pull;}
    else               {GPIO_InitStruct.Pull = GPIO_NOPULL;}
    if(IS_GPIO_SPEED(Speed)){GPIO_InitStruct.Speed = Speed;}
    else    { GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;} 
     
	HAL_GPIO_Init(GPIO, &GPIO_InitStruct); 
	return GPIO_InitStruct.Pin ;
}

对于前面的初使化可以直接调用此函数实现。初使化一个GPIO引脚,比如说一个LED灯的控制引脚GPIOA上的第GPIO_PIN_1,高级做法是这样子:

wGpio_Init(wPIN_A1,GPIO_MODE_OUTPUT_PP, GPIO_NOPULL, GPIO_SPEED_FREQ_HIGH);

当硬件发生变化,现在LED灯的控制引脚改放在GPIOB上的第GPIO_PIN_2上,则初使化函数可改为:

wGpio_Init(wPIN_B2,GPIO_MODE_OUTPUT_PP, GPIO_NOPULL, GPIO_SPEED_FREQ_HIGH);

可以看出,此次引脚更改,只需更改一次地方。我们接着思考,我们现在想点亮这个LED灯,遇到了问题?我们先假设输出低电平时led亮,高电平不亮。通常的做法是

HAL_GPIO_WritePin(GPIOA,GPIO_PIN_1,GPIO_PIN_RESET);//点亮led灯

这里又遇到了GPIOA 以及GPIO_PIN_1的地方,而我们使用的却是wPIN_A1。这应该如何调用呢?

1.4 GPIO结构体引用

对于1个Led灯,我们主要关心是哪个引脚控制,有效电平(点亮电平)是什么电平,我们可以用如下方式定义一个结构体。

typedef  struct {
    GPIO_TypeDef*           GPIOx;           //引脚句柄
    uint16_t                GPIO_Pin;        //引脚
    wFun                    EN;              //使能开关 
    wGpio_Level             Level;           //有效电平
}wGpio_Def;

1.5 GPIO 引脚带句柄式初使化函数

void wGpio_putInit(wGpio_Def* wGPIO,wPin pin,uint32_t Mode,uint32_t Pull, uint32_t Speed, wGpio_Level level)
{/*>>带句柄方式初使化GPIO引脚,并增加了有效电平位*/
	switch(wPIN_GPIO_MASK & pin)
    {
        case wPIN_A0:{wGPIO->GPIOx=GPIOA;}break;
        case wPIN_B0:{wGPIO->GPIOx=GPIOB;}break;	
        case wPIN_C0:{wGPIO->GPIOx=GPIOC;}break;
        …… …… ……//省略部分代码
        default:return ;
    }    
    wGPIO->GPIO_Pin = wGpio_Init(pin,Mode,Pull,Speed,NULL);
    wGPIO->Level = level ;
    wGPIO->EN = wENABLE ;  
}

对于类似于led的GPIO控制引脚,为了能保存GPIO_TypeDef句柄以及GPIO_PIN引脚号,我们需要先新建一个wGpio_Def结构体,这样就可以在任何时候能获取GPIO引脚所对应的具体参数。

1.6 GPIO 引脚带句柄式读函数

wGpio_State wGpio_putRead(wGpio_Def* wGPIO)
{/*>>读取电平的有效性*/
    wGpio_State             State;           //当前状态 
    if(wGPIO->EN)
    {
        if(HAL_GPIO_ReadPin(wGPIO->GPIOx, wGPIO->GPIO_Pin))
        {//读取到正限位引脚为高电平
            if(wGPIO->Level==wHigh) // 如果定义高电平有效
            {
                State = wACTIVE;
            }
            else                    // 如果定义低电平有效
            {
                State = wINACTIVE;
            }
        }
        else// 读取到正限位引脚为低电平
        {
            if(wGPIO->Level==wHigh)
            {
                State = wINACTIVE;
            }
            else
            {
                State = wACTIVE;
            }
        }
    }
    return State;
}

1.7 GPIO 引脚带句柄式写函数

void wGpio_putWrite(wGpio_Def* wGPIO,wGpio_Set CurState)
{/*>>设置电平的有效性*/
    if(wGPIO->EN == ENABLE)
    {
        if(CurState == Set_ACTIVE)
        {//设定为有效电平
            if(wGPIO->Level==wHigh) 
            {// 如果定义高电平有效
               HAL_GPIO_WritePin(wGPIO->GPIOx, wGPIO->GPIO_Pin,GPIO_PIN_SET);
            }
            else 
            {
               HAL_GPIO_WritePin(wGPIO->GPIOx, wGPIO->GPIO_Pin,GPIO_PIN_RESET);
            }
        }
        else if(CurState == Set_INACTIVE)
        {//设定为无效电平
            if(wGPIO->Level==wHigh)
            {
               HAL_GPIO_WritePin(wGPIO->GPIOx, wGPIO->GPIO_Pin,GPIO_PIN_RESET);
            }
            else
            {
                HAL_GPIO_WritePin(wGPIO->GPIOx, wGPIO->GPIO_Pin,GPIO_PIN_SET);
            }
        }
		else
		{//切换电平
			HAL_GPIO_TogglePin(wGPIO->GPIOx, wGPIO->GPIO_Pin);
		}
    } 
}

现在有了带句柄式的写函数,我们就可以很好的处理原先抛出的问题。我们不用直接的方式初使化Gpio引脚,而是引用句柄的方式初使化。再通过调用wGpio_putWrite函数使能引脚输出有效电平,由于我们初使化时定义有效电平为低电平,所以此引脚最终会输出低电平,例如:

wGpio_Def LedCss;//定义一个句柄 
wGpio_putInit (&LedCss,wPIN_A1,GPIO_MODE_OUTPUT_PP,GPIO_NOPULL,
GPIO_SPEED_FREQ_HIGH,wLow); //低电平有效
wGpio_putWrite(&LedCss, Set_ACTIVE);//点亮led灯

猜你喜欢

转载自blog.csdn.net/abc240697738/article/details/102704889