9月15日更新下载!!! 超级方便的GPIO端口配置函数,寄存器版本和库函数版本均可使用

2019-07-20 23:51发布

本帖最后由 warship 于 2018-9-15 10:48 编辑

GPIO端口的配置是每个程序都要用到的,
并且还不止配置一个,如LED灯、键盘、IO控制端口等等,
用库函数则每配置一个端口就一堆代码,看着头疼。
用寄存器版本则很不直观,
今天我看原子的寄存器版本有一个外部中断配置的函数,
用起来很方便,受到启发,
就着手参考优化了库函数的原型代码
编了这个GPIO的端口配置函数(初版时每次只配置一个引脚,现在应坛友需求,改为一次可配多个引脚)。

已经实测通过,超级好用,不敢独占,贴出来给大家分享一下,
无论你是用库函数编程,还是用寄存器编程都可以使用。
*************** 华丽的分界线 *******************************


有了这个配置函数,今后的GPIO配置就超级清爽了,
比如两个LED灯,三个按键的配置,
原先有一大堆初始化数据结构体的代码,既杂乱又哆嗦,
现在只须如下代码(示例):

//配置PA8和PD2为LED指示灯
My_GPIO_Init(GPIOA,  Pin_8,  Mode_Out_PP+Speed_50MHz);    //LED0
My_GPIO_Init(GPIOD,  Pin_2,  Mode_Out_PP+Speed_50MHz);    //LED1

//配置三个按键PC1 、PC13、PA0
My_GPIO_Init(GPIOC, Pin_1 | Pin_13, Mode_IPU);     //配置KEY1、KEY2按键   
My_GPIO_Init(GPIOA, Pin_0,  Mode_IPD);    //配置WK_UP按键
******************************************************************
应网友要求,在21楼修改了原子的试验5(外部中断试验)作为范例,
现将附件移到顶楼,方便下载测试。
附件的sys.c文件中,对原子代码不合理的部分进行了修改(文件中有注释说明),
扩展并使用了我最新研究成果,直接进行位段操作,
用位段操作编出来的代码更高效、程序的可读性更强,更容易理解上手。大家可以与原子的源码进行对比,就会有体会了。
有问题欢迎提出来讨论交流哈。
关于位段操作的研究讨论可移步http://www.openedv.com/forum.php ... 4196&extra=page%3D1






友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。
该问题目前已经被作者或者管理员关闭, 无法添加新回复
49条回答
xiatianyun
1楼-- · 2019-07-21 00:19
本帖最后由 xiatianyun 于 2018-9-8 15:08 编辑

其实如果不介意只初始化一个pin带来的for循环浪费的话完全可以简单封装一下初始化函数,就可以不用在用户程序中写一堆初始化数据结构的代码了。
[mw_shl_code=c,true]//GPIO_Init()库函数的二次封装。
//调用前必须使所用的GPIO外设时钟总线使能。
void My_GPIO_Init(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, GPIOMode_TypeDef GPIO_Mode, GPIOSpeed_TypeDef GPIO_Speed)
{
    GPIO_InitTypeDef GPIO_InitStruct; //声明初始化数据结构。
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed;
    GPIO_Init(GPIOx, &GPIO_InitStruct);
}[/mw_shl_code]
[mw_shl_code=c,true]//板载led初始化。
void led_Init(void)
{
    //GPIO_InitTypeDef GPIO_InitStruct; //声明初始化数据结构。

    //GPIOB和GPIOE时钟使能。
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE, ENABLE);

    //设置LED0。
    My_GPIO_Init(GPIOB, GPIO_Pin_5, GPIO_Mode_Out_OD, GPIO_Speed_10MHz); //开漏输出
    //设置LED1.除了端口组不一样外,其他一样。
    My_GPIO_Init(GPIOE, GPIO_Pin_5, GPIO_Mode_Out_OD, GPIO_Speed_10MHz); //开漏输出
    //初始化使led等熄灭。
    //如果不处理则led相应端口初始化输出为0,灯被点亮。
    LED0_OFF;
    LED1_OFF;
}[/mw_shl_code]

warship
2楼-- · 2019-07-21 03:01
本帖最后由 warship 于 2018-7-14 18:57 编辑

以下是底层实现代码:建议复制后直接放在SYS.C文件中
**************************************************************************************
//GPIO端口配置函数
//
//输入参数:
//GPIOx:直接填写0~6代表GPIOA~G, 或使用宏定义
//BITx: 端口位号(即端口数字号,如PA4则参数为4)
//Mode: 端口模式,共8种,请使用模式的宏定义或直接填写数值,
//      函数不检查该参数,请勿超出这8种的范围自创模式!!!
//Speed:端口速度, 共3种,请使用速度的宏定义或直接填写数值(1/2/3),
//      该参数仅在输出模式下有作用,设输入模式时,可以随便填一个值
//返回值: 无. 端口组或端口号超出范围时将直接返回.
//该函数没有使用库函数, 使用时也无须包含stm32f10x_gpio.h
//使用示例:  My_GPIO_Init(GPIO_D, 5, Mode_Out_OD, Speed_50MHz); 即可设置PD5为开漏输出

void My_GPIO_Init(u8 GPIOx, u8 BITx, u8 Mode, u8 Speed)
{
  
  GPIO_TypeDef *pAddr;     //指向GPIO寄存器组基址的地址指针
        
  u32 currentmode = 0x00, pinpos = 0x00, pos = 0x00;
  u32 tmpreg = 0x00, pinmask = 0x00;
        
  if(GPIOx>6 || BITx>15) return;  //端口参数超出范围,直接返回
        
  pAddr=(GPIO_TypeDef *)(GPIOA_BASE+0x400*GPIOx); //计算对应的GPIO端口基址, 每组端口占用地址空间为400H
  
/*---------------------------- GPIO 模式配置 -----------------------*/
  currentmode = ((u32)Mode) & ((u32)0x0F); //取模式参数低4位存入currentmode
  if ( Mode & 0x10) //如果第5位为1,则为输出模式
   {
    currentmode |= (u32)(Speed & 0x03);  //仅在输出模式下,速度参数才有效
   }
/*---------------------------- GPIO CRL 配置 ------------------------*/
  if ( BITx <8 )  //如果端口线号<8, 则配置CRL
  {
    pinpos=BITx;
    tmpreg = pAddr->CRL;         //取CRL原有值
    pos = pinpos << 2;           //BIT位置计算,每线占4BIT
    /* 清0相应的CRL寄存器BIT位 */
    pinmask = ((u32)0x0F) << pos;
    tmpreg &= ~pinmask;
    /* 写模式配置相应的BIT位 */
    tmpreg |= (currentmode << pos);
    pAddr->CRL = tmpreg; //完成CRL配置
  }
/*---------------------------- GPIO CRH 配置 ------------------------*/
  else   //端口线号>=8, 则配置CRL
  {
    tmpreg = pAddr->CRH;  //取CRH原有值
    pinpos=BITx-0x08;
    pos = pinpos << 2;
        /* 清0相应的CRH寄存器BIT位 */
    pinmask = ((u32)0x0F) << pos;
    tmpreg &= ~pinmask;
     /* 写模式配置相应的BIT位 */
    tmpreg |= (currentmode << pos);
    pAddr->CRH = tmpreg; //完成CRH配置
  } //最后如果是上拉下拉输入模式,则初始化一下相应端口电平
  if (Mode == Mode_IPD) pAddr->BRR=1<<BITx;  /* 对于下拉输入模式,则复位端口 */
  else if(Mode == Mode_IPU) pAddr->BSRR=1<<BITx;  /* 对于上拉输入模式,则置位端口 */      
}


warship
3楼-- · 2019-07-21 04:40
本帖最后由 warship 于 2018-7-12 19:31 编辑

用到的宏定义:
#define        Mode_AIN                                                        0x0   //模拟输入
#define        Mode_IN_FLOATING                        0x04        //浮空输入
#define        Mode_IPD                                                        0x28        //下拉输入
#define        Mode_IPU                                                        0x48        //上拉输入

#define        Mode_Out_OD                                                0x14        //开漏输出
#define        Mode_Out_PP                                                0x10        //推挽输出
#define        Mode_AF_OD                                                0x1C        //复用开漏
#define        Mode_AF_PP                                                0x18        //复用推挽        

#define        Speed_10MHz                                                0x1
#define        Speed_2MHz                                                0x2
#define        Speed_50MHz                                                0x3
//以下宏定义是原子版本已有的
#define GPIO_A             0
#define GPIO_B             1
#define GPIO_C             2
#define GPIO_D             3
#define GPIO_E             4
#define GPIO_F             5
#define GPIO_G            6

warship
4楼-- · 2019-07-21 04:53
 精彩回答 2  元偷偷看……
warship
5楼-- · 2019-07-21 06:09
 精彩回答 2  元偷偷看……
d1z1y2
6楼-- · 2019-07-21 07:22
谢谢分享,收藏了

一周热门 更多>