tm4c123gxl库函数调包侠养成(二)——————GPIO与按键输入

一、GPIO概述
1、IO口是单片机最基础的操作,基础到不能再基础的那种,能熟练进行io口的操作就能够点亮第一个led灯,会自己写代码点亮灯,才能算是接触了单片机。
2、IO口,即为input和output,输入和输出的引脚,笔者也将其理解为单片机和外界进行交互的一个通道。单片机可以进行很多的操作,如我们后期的pwm,要把pwm产生的波进行输出,就要用到io口,否则pwm波就算产生了,也没有地方输出使用。
3、io的输出和输入高低电平作为基本操作,而pwm则是io口的复用操作;笔者将其理解为一个单片机为了资源的有效利用,故将io口与其他功能连在一起,这样问题来了,怎么区分是哪种功能的使用;有关这点,正点原子f103的操作是如果是单纯的io输出电平,就在mode里设置为out类型,复用则设置为AF功能。在tm4中,则是通过一个函数,不论是哪种功能,都需要配置一番。
4、所有的io口都可以作为输入和输出,但并不是所有的io口都可以作为pwm波输出,具体的功能需要查看相应的数据手册。
5、有的io口是默认被锁上的,要通过软件解锁,比如pf0就是被锁的,在使用pf0前要加上:HWREG(GPIO_PORTF_BASE + GPIO_O_LOCK) = GPIO_LOCK_KEY;
HWREG(GPIO_PORTF_BASE + GPIO_O_CR) |= 0x01;
HWREG(GPIO_PORTF_BASE + GPIO_O_LOCK) = 0;注:这一句只适合pf0,不同的io口不一样
来解锁对应io口;
那么怎么确定io口是否被锁呢,这里推荐使用TI公司的Tiva C Series PinMux软件,在里面选择型号tm4c123gh6pm,这样就是我们这款单片机对应的端口功能了,我们可以通过这款软件来确定使用哪个io口的哪个功能,避免io口重复使用。使用时即需要双击对应功能,会出现颜色变化,再点击file的save,保存相应的c文件h文件,相应的c文件中会给你配置好基础的配置,一般是到复用那一步,仍需要你配置很多东西,但它把解锁的io口的解锁函数给你写好了,笔者建议只把解锁函数复制到自己的代码中,其他函数还是自己手打的好。

7、有关软件的按键输入
采用检测按键对应io口的电平是否改变的方式来检测;这样的坏处是在代码冗长的情况下,响应不及时,推荐按键输入使用外部中断解决,我们在下一章会介绍。
如果硬要采用按键检测,会有一个按键的抖动,即按键按下的时候,并不是很规律地变高变低,而是在高电平与低电平之间有抖动,这里我们参照正点原子的方法解决它,即使用一个delay20ms的函数来进行消抖,判断20ms后该io口的电平是否还处在按下状态来判断按键是否真的按下。(大爱原子哥)

二、相关函数
1、 SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
while(!SysCtlPeripheralReady(SYSCTL_PERIPH_GPIOF))
;

这一句是使能相应的功能时钟,笔者上一篇解释过,不再重复。
2、 GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE, GPIO_PIN_1);
将该io口(PF1)复用为输出(output)
第一个参数是io口的基地址,也就是哪个gpio模块;可选A-F
第二个参数是该gpio模块的哪个口,可选0-7;
3、 GPIOPinTypeGPIOInput(GPIO_PORTF_BASE, GPIO_PIN_4);
将该io口(PF4)复用为输入(input)
这里的参数配置与上面一样,不再赘述。
4、GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_1, GPIO_PIN_1);
对io口进行写操作。
第一个参数:io口的基地址;
第二个参数:要操作的io模块的哪个口;
第三个参数:写入的值。
这里需要解释一波。对于第三个参数为什么是 GPIO_PIN_1,因为在该单片机的库中, GPIO_PIN_1之类的是由宏定义定义而来的,比如 GPIO_PIN_1是0x00000002,而库函数对单片机的操作,最后则是写成相应的寄存器操作形式函数在进行的操作;
可以强行理解为前两个参数设定了对哪几个io口进行操作,后面的那个值则是说明对该口写入什么样的值,对GPIO_PIN_1写入GPIO_PIN_1则理解为把PF1置高;
这样写的好处,一是条理清晰,易于读,二是方便对许多io口同时操作,比如
GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_1| GPIO_PIN_2| GPIO_PIN_3, GPIO_PIN_1| GPIO_PIN_2| GPIO_PIN_3);
这句就是把PF1、2、3全部置高;
GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_1| GPIO_PIN_2| GPIO_PIN_3, 0x0);
这句就是把PF1、2、3全部置低;
GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_1| GPIO_PIN_2| GPIO_PIN_3, GPIO_PIN_1| GPIO_PIN_2);
这句就是把PF1、2置高,但PF3则置低。因为这里是并的关系,所以GPIO_PIN_1| GPIO_PIN_2是把1和2的二进制位并到一起,但GPIO_PIN_3相应的仍是0,而且前面是对GPIO_PIN_1| GPIO_PIN_2| GPIO_PIN_3进行操作,所以3口置低;**GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_1, GPIO_PIN_1| GPIO_PIN_2);**这一句,虽然写入的值是GPIO_PIN_1| GPIO_PIN_2;但因为前面是对GPIO_PIN_1进行操作,所以GPIO_PIN_2的电平并不会被置高。
笔者能力有限,这边解释的也不是太清除,还请见谅。
5、 GPIOPadConfigSet(GPIO_PORTF_BASE, GPIO_PIN_4|GPIO_PIN_0, GPIO_STRENGTH_2MA, GPIO_PIN_TYPE_STD_WPU);
配置引脚的设置。(笔者认为一般是对要配置为输入的io口才配置)
第一个参数:模块基地址;
第二个参数:要配置的口;
第三个参数:输出驱动强度,2-4-6-8-10-12ma;这里一般是2ma,笔者没怎么用过其他功能。
第四个参数:指定引脚类型。
GPIO_PIN_TYPE_STD_WPU为弱上拉
GPIO_PIN_TYPE_STD_WPD为弱下拉
GPIO_PIN_TYPE_STD为推挽引脚
GPIO_PIN_TYPE_OD为开漏
GPIO_PIN_TYPE_ANALOG为模拟输入
GPIO_PIN_TYPE_WAKE_HIGH为高电平唤醒
GPIO_PIN_TYPE_WAKE_LOW为低电平唤醒
这里的功能多多,但我们一般用的就是弱上拉和弱下拉,其他的弱单纯配置一个io口输出,则不需要配置这行代码。
上拉即为把相应的io口置于高电平;下拉则是把相应的io口置于低电平;这样做是因为IO口有一个默认的电平状态,可能是高,可能是低;但你需要它是高电平,不断读取,读到低电平则执行一段代码;但如果这个io口并没有在你预想的电平状态,这样就会出问题。(让人头秃的bug就是这么来的)将其上拉或下拉则可以将其强制在一种状态,而不会产生不必要的麻烦。
4、 ReadPin1 = GPIOPinRead(GPIO_PORTF_BASE, GPIO_PIN_4);
读取相应io口的电平状态;
两个参数与上面一致;
函数原型:
int32_t GPIOPinRead(uint32_t ui32Port, uint8_t ui8Pins);
传回的值是一个32位的整型;可以用:
if((ReadPin1&GPIO_PIN_4) != GPIO_PIN_4) {
要写的代码

}

来完成操作;
三、代码逻辑
1、使能系统时钟
2、使能外设时钟
3、配置io口的复用 GPIOPinTypeGPIOInput
4、(如果是输入则要配置上拉/下拉)GPIOPadConfigSet
5、对io口读、写GPIOPinRead、GPIOPinWrite

四、代码程序
1、功能:点亮led灯,其中三个灯分别对应PF1、2、3

#include <stdint.h>
#include <stdbool.h>
#include "inc/hw_memmap.h"
#include "driverlib/gpio.h"
#include "driverlib/sysctl.h"


#define DELAY_MS  200   

void main(void)
{

    SysCtlClockSet(SYSCTL_SYSDIV_5 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN |
                       SYSCTL_XTAL_16MHZ);


    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);

    while(!SysCtlPeripheralReady(SYSCTL_PERIPH_GPIOF))
    	;


    GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE, GPIO_PIN_1 | GPIO_PIN_2 |GPIO_PIN_3);


    GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_1, 0x0);
    GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_2, 0x0);
    GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_3, 0x0);

 
    while(1)
    {
        
        GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_1, GPIO_PIN_1);
        GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_2, 0x0);
        GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_3, 0x0);

        

		SysCtlDelay(DELAY_MS*(SysCtlClockGet()/3000));


               GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_1, 0x0);
        GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_2, GPIO_PIN_2);
        GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_3, 0x0);


		SysCtlDelay(DELAY_MS*(SysCtlClockGet()/3000));

       
        GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_1, 0x0);
        GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_2, 0x0);
        GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_3, GPIO_PIN_3);

    
		SysCtlDelay(DELAY_MS*(SysCtlClockGet()/3000));
    }
}

2、补充一个按键输入的函数

#include <stdint.h>
#include <stdbool.h>
#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "inc/hw_gpio.h"
#include "driverlib/gpio.h"
#include "driverlib/sysctl.h"
#define DELAY_MS  500 
void main(void)
{
    int temp=100;
    uint8_t ReadPin1,ReadPin2;         
    bool    KeyPress1 = false;
    bool    KeyPress2 = false;
   
    SysCtlClockSet(SYSCTL_SYSDIV_5 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN |
                       SYSCTL_XTAL_16MHZ);

 
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);

  
    while(!SysCtlPeripheralReady(SYSCTL_PERIPH_GPIOF))
        ;

    GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE, GPIO_PIN_1);
 
     HWREG(GPIO_PORTF_BASE + GPIO_O_LOCK) = GPIO_LOCK_KEY;
     HWREG(GPIO_PORTF_BASE + GPIO_O_CR) |= 0x01;//解锁
     HWREG(GPIO_PORTF_BASE + GPIO_O_LOCK) = 0;


    GPIOPinTypeGPIOInput(GPIO_PORTF_BASE, GPIO_PIN_4|GPIO_PIN_0);

                                                                                                                     
    GPIOPadConfigSet(GPIO_PORTF_BASE, GPIO_PIN_4|GPIO_PIN_0, GPIO_STRENGTH_2MA, GPIO_PIN_TYPE_STD_WPU);//将输入io口上拉,为高电平,其电平变为低电平即认为其按下

  
    GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_1, 0x0);

   
    while(1)
    {
       
        ReadPin1 = GPIOPinRead(GPIO_PORTF_BASE, GPIO_PIN_4);//检测

        if((ReadPin1&GPIO_PIN_4) != GPIO_PIN_4) { //如果按下

       
            SysCtlDelay(20*(SysCtlClockGet()/3000));//消抖

        
            ReadPin1 = GPIOPinRead(GPIO_PORTF_BASE, GPIO_PIN_4);//再次检测

            if((ReadPin1&GPIO_PIN_4) != GPIO_PIN_4) {//再次判断
                KeyPress1 = true;//如果按下,则置一个标志位

               
                while(!GPIOPinRead(GPIO_PORTF_BASE, GPIO_PIN_4)) {
                    ;//等按键松开
                }
            }
        }

        if(true == KeyPress1) {//标志位对应的功能
            KeyPress1 = false;

        
            GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_1, GPIO_PIN_1);

         
            SysCtlDelay(temp*(SysCtlClockGet()/3000));

     
            GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_1, 0x0);
        }



ReadPin2 = GPIOPinRead(GPIO_PORTF_BASE, GPIO_PIN_0);

       if((ReadPin2&GPIO_PIN_0) != GPIO_PIN_0) { 

      
           SysCtlDelay(20*(SysCtlClockGet()/3000));

       
           ReadPin2 = GPIOPinRead(GPIO_PORTF_BASE, GPIO_PIN_0);

           if((ReadPin2&GPIO_PIN_0) != GPIO_PIN_0) { // If key1 is press still.
               KeyPress2 = true;

            
               while(!GPIOPinRead(GPIO_PORTF_BASE, GPIO_PIN_0)) {
                   ;
               }
           }
       }

       if(true == KeyPress2) {
           KeyPress2 = false;
           temp+=100;

       }

   }
}
发布了10 篇原创文章 · 获赞 14 · 访问量 5506

猜你喜欢

转载自blog.csdn.net/qq_43725844/article/details/89047882