打印
[牛人杂谈]

M051的GPIO学习

[复制链接]
2893|24
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
antusheng|  楼主 | 2017-12-13 19:04 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
GPIO, pi, IO, gp, AC

看官方的例子,一共有四个,我们可以知道,这应该可以至少作为四种功能使用。先看中断功能。
沙发
antusheng|  楼主 | 2017-12-13 19:09 | 只看该作者
先看例子,会发现,还是要进行系统初始化的,使用内部RC高速振荡器,等待振荡器稳定后,设置为HCLK的时钟源,使能外部晶振,等待晶振稳定,激活PLL,使能串口0时钟,选择串口时钟源为PLL。
因为要使用P30和P31的串口0,这里还要配置这两个端口的串口功能。
这就是系统的初始化,每次重复一遍容易记得住。
void SYS_Init(void)
{
    /*---------------------------------------------------------------------------------------------------------*/
    /* Init System Clock                                                                                       */
    /*---------------------------------------------------------------------------------------------------------*/
    /* Enable Internal RC 22.1184MHz clock */
    CLK_EnableXtalRC(CLK_PWRCON_OSC22M_EN_Msk);

    /* Waiting for Internal RC clock ready */
    CLK_WaitClockReady(CLK_CLKSTATUS_OSC22M_STB_Msk);

    /* Switch HCLK clock source to Internal RC and HCLK source divide 1 */
    CLK_SetHCLK(CLK_CLKSEL0_HCLK_S_HIRC, CLK_CLKDIV_HCLK(1));

    /* Enable external XTAL 12MHz clock */
    CLK_EnableXtalRC(CLK_PWRCON_XTL12M_EN_Msk);

    /* Waiting for external XTAL clock ready */
    CLK_WaitClockReady(CLK_CLKSTATUS_XTL12M_STB_Msk);

    /* Set core clock as PLL_CLOCK from PLL */
    CLK_SetCoreClock(PLL_CLOCK);

    /* Enable UART module clock */
    CLK_EnableModuleClock(UART0_MODULE);

    /* Select UART module clock source */
    CLK_SetModuleClock(UART0_MODULE, CLK_CLKSEL1_UART_S_PLL, CLK_CLKDIV_UART(1));

    /*---------------------------------------------------------------------------------------------------------*/
    /* Init I/O Multi-function                                                                                 */
    /*---------------------------------------------------------------------------------------------------------*/

    /* Set P3 multi-function pins for UART0 RXD and TXD */
    SYS->P3_MFP &= ~(SYS_MFP_P30_Msk | SYS_MFP_P31_Msk);
    SYS->P3_MFP |= (SYS_MFP_P30_RXD0 | SYS_MFP_P31_TXD0);

}


使用特权

评论回复
板凳
antusheng|  楼主 | 2017-12-13 19:15 | 只看该作者
本帖最后由 antusheng 于 2017-12-13 19:17 编辑

之后想用串口,对串口0进行配置,启动串口0,RST,设置波特率打开。
这些其实都跟金本节主题无关。
接下来进入相关环节。
我们知道中断是属于输入的一种应用,称为中断输入,用户获取外部的中断信号。
因此要将需要的端口设置成输入模式
设置好了输入模式,还需要启动这个端口的中断功能,就是使能何种中断,这个例子是上升沿
接下来呢,还需要将该中断源加入中断向量表。就是在中断控制器那里要启动,因为GPIO的中断,是多个端口共用一个中断源入口,这里是P0P1共用。。


    /* Configure P1.3 as Input mode and enable interrupt by rising edge trigger */
    GPIO_SetMode(P1, BIT3, GPIO_PMD_INPUT); //设置成输入模式
    GPIO_EnableInt(P1, 3, GPIO_INT_RISING);  //设置成上升沿中断
    NVIC_EnableIRQ(GPIO_P0P1_IRQn);  //启动对应的中断向量入口
这个是P1.3端口的中断输入配置
————————————————————

使用特权

评论回复
地板
antusheng|  楼主 | 2017-12-13 19:18 | 只看该作者
其实GPIO还有另外一种模式可以具备输入功能,那就是准双向模式
这里以P4.5举例
   /*  Configure P4.5 as Quasi-bidirection mode and enable interrupt by falling edge trigger */
    GPIO_SetMode(P4, BIT5, GPIO_PMD_QUASI);       //设置为准双向模式
    GPIO_EnableInt(P4, 5, GPIO_INT_FALLING);         //设置为下降沿中断
    NVIC_EnableIRQ(GPIO_P2P3P4_IRQn);               //启动对应的中断入口
我们可以看到这个中断源是P2,P3,P4共用的。

使用特权

评论回复
5
antusheng|  楼主 | 2017-12-13 19:22 | 只看该作者
所有工作做好了,接下来就是发生中断后要做什么的了
void GPIOP0P1_IRQHandler(void)
{
    /* To check if P1.3 interrupt occurred */
    if(GPIO_GET_INT_FLAG(P1, BIT3))
    {
        GPIO_CLR_INT_FLAG(P1, BIT3);
        printf("P1.3 INT occurred.\n");
    }
    else
    {
        /* Un-expected interrupt. Just clear all PORT0, PORT1 interrupts */
        P0->ISRC = P0->ISRC;
        P1->ISRC = P1->ISRC;
        printf("Un-expected interrupts.\n");
    }
}
如果P1.3发生了上升沿,就会触发中断,这个时候进入这个中断处理程序,程序名都是固定的,不用修改
只需要自己写里面内容就行了
这里就是检测是否是P1.3发生了中断,上升沿嘛,发生了就是变成高电平了,检测是否是高电平,如果是,就用清理中断标识的函数清理,然后打印输出该端口发生了中断。
否则的话,就是不是该端口发生的中断,但是属于P0和P1的某个端口发生了,想再次进入该中断就要先退出,推出前还是要清理该中断标志的。方法就是对该寄存器写1,本来就是1了,再写1,就是上面的这种A=A的方式了。

使用特权

评论回复
6
antusheng|  楼主 | 2017-12-13 19:24 | 只看该作者
/**
* [url=home.php?mod=space&uid=247401]@brief[/url]       Clear GPIO Pin Interrupt Flag
*
* @param[in]   port        GPIO port. It could be P0, P1, P2, P3 or P4.
* @param[in]   u32PinMask  The single or multiple pins of specified GPIO port.
*                          It could be BIT0 ~ BIT7.
*
* [url=home.php?mod=space&uid=266161]@return[/url]      None
*
* [url=home.php?mod=space&uid=1543424]@Details[/url]     Clear the interrupt status of specified GPIO pin.
*/
#define GPIO_CLR_INT_FLAG(port, u32PinMask)         ((port)->ISRC = (u32PinMask))

使用特权

评论回复
7
antusheng|  楼主 | 2017-12-13 19:25 | 只看该作者

void GPIOP2P3P4_IRQHandler(void)
{
    /* To check if P4.5 interrupt occurred */
    if(GPIO_GET_INT_FLAG(P4, BIT5))
    {
        GPIO_CLR_INT_FLAG(P4, BIT5);
        printf("P4.5 INT occurred.\n");
    }
    else
    {
        /* Un-expected interrupt. Just clear all PORT2, PORT3 and PORT4 interrupts */
        P2->ISRC = P2->ISRC;
        P3->ISRC = P3->ISRC;
        P4->ISRC = P4->ISRC;
        printf("Un-expected interrupts.\n");
    }
}
P4.5的中断处理函数也是类似的,先检测是不是这个端口发生了中断,如果是清零,同样的方式,调用写1的宏。否则重写,重写后,就是0保持0,1被重写1后清零。


使用特权

评论回复
8
antusheng|  楼主 | 2017-12-13 19:34 | 只看该作者
带消抖动的外部中断

根据例子来看,初始化部分跟GPIO中断是一样的,我们看中断配置
 /*-----------------------------------------------------------------------------------------------------*/
    /* GPIO External Interrupt Function Test                                                               */
    /*-----------------------------------------------------------------------------------------------------*/
    printf("EINT0(P3.2) and EINT1(P3.3) are used to test interrupt \n");

    /* Configure P3.2 as EINT0 pin and enable interrupt by falling edge trigger */
    GPIO_SetMode(P3, BIT2, GPIO_PMD_INPUT);
    GPIO_EnableEINT0(P3, 2, GPIO_INT_FALLING);
    NVIC_EnableIRQ(EINT0_IRQn);

    /* Configure P3.3 as EINT1 pin and enable interrupt by rising and falling edge trigger */
    GPIO_SetMode(P3, BIT3, GPIO_PMD_INPUT);
    GPIO_EnableEINT1(P3, 3, GPIO_INT_BOTH_EDGE);
    NVIC_EnableIRQ(EINT1_IRQn);

    /* Enable interrupt de-bounce function and select de-bounce sampling cycle time is 1024 clocks of LIRC clock */
    GPIO_SET_DEBOUNCE_TIME(GPIO_DBCLKSRC_LIRC, GPIO_DBCLKSEL_1024);
    GPIO_ENABLE_DEBOUNCE(P3, BIT2 | BIT3);
这里使用的是P3.2和P3.3这两个是从51单片机继承来的外部中断引脚,也就是一个PIN对应一个中断入口,跟GPIO中断多个PIN共用端口是不同的
P3.2设置成输入模式,下降沿触发外部中断0,然后启动对应的中断入口。
P3.3设置为输入模式,双边沿触发模式,然后启动对应的外部中断1.
接下来就是设置防抖动功能了,防抖功能是使用了LIRC实现的,设置防抖时钟,这里用的是1024
然后使能防抖的接口
那意思是这个接口响应速度只有1024了。
看看还有别的设置没有
/**
* @brief       Set De-bounce Sampling Cycle Time
*
* @param[in]   u32ClkSrc   The de-bounce counter clock source. It could be GPIO_DBCLKSRC_HCLK or GPIO_DBCLKSRC_LIRC.
* @param[in]   u32ClkSel   The de-bounce sampling cycle selection. It could be \n
*                              GPIO_DBCLKSEL_1, GPIO_DBCLKSEL_2, GPIO_DBCLKSEL_4, GPIO_DBCLKSEL_8, \n
*                              GPIO_DBCLKSEL_16, GPIO_DBCLKSEL_32, GPIO_DBCLKSEL_64, GPIO_DBCLKSEL_128, \n
*                              GPIO_DBCLKSEL_256, GPIO_DBCLKSEL_512, GPIO_DBCLKSEL_1024, GPIO_DBCLKSEL_2048, \n
*                              GPIO_DBCLKSEL_4096, GPIO_DBCLKSEL_8192, GPIO_DBCLKSEL_16384, GPIO_DBCLKSEL_32768.
*
* @return      None
*
* @details     Set the interrupt de-bounce sampling cycle time based on the debounce counter clock source. \n
*              Example: _GPIO_SET_DEBOUNCE_TIME(GPIO_DBCLKSRC_LIRC, GPIO_DBCLKSEL_4). \n
*              It's meaning the De-debounce counter clock source is internal 10 KHz and sampling cycle selection is 4. \n
*              Then the target de-bounce sampling cycle time is (4)*(1/(10*1000)) s = 4*0.0001 s = 400 us,
*              and system will sampling interrupt input once per 400 us.
*/
#define GPIO_SET_DEBOUNCE_TIME(u32ClkSrc, u32ClkSel)    (GPIO->DBNCECON = (GPIO_DBNCECON_ICLK_ON_Msk | (u32ClkSrc) | (u32ClkSel)))
我们肯定防抖能力是可以设定的。

使用特权

评论回复
9
antusheng|  楼主 | 2017-12-13 19:36 | 只看该作者


/**
* @brief       Enable Pin De-bounce Function
*
* @param[in]   port        GPIO port. It could be P0, P1, P2, P3 or P4.
* @param[in]   u32PinMask  The single or multiple pins of specified GPIO port.
*                          It could be BIT0 ~ BIT7.
* @return      None
*
* @details     Enable the interrupt de-bounce function of specified GPIO pin.
*/
#define GPIO_ENABLE_DEBOUNCE(port, u32PinMask)      ((port)->DBEN |= (u32PinMask))
防抖的IO还可以是别的接口,如上所示
void EINT0_IRQHandler(void)
{
    /* For P3.2, clear the INT flag */
    GPIO_CLR_INT_FLAG(P3, BIT2);

    printf("P3.2 EINT0 occurred.\n");
}
发生中断后,就可以直接清理了,因为这个是一个PIN对一个中断入口的,没有跟别的共用。


使用特权

评论回复
10
antusheng|  楼主 | 2017-12-13 19:36 | 只看该作者
清理的实现方式仍然是写1.

使用特权

评论回复
11
antusheng|  楼主 | 2017-12-13 19:44 | 只看该作者
对于简单的IO输出功能就不用提了,输入模式和输出模式。

    /* Configure P1.2 as Output mode and P4.1 as Input mode */
    GPIO_SetMode(P1, BIT2, GPIO_PMD_OUTPUT);
    GPIO_SetMode(P4, BIT1, GPIO_PMD_INPUT);

    i32Err = 0;
    printf("GPIO P1.2(output mode) connect to P4.1(input mode) ......");

    /* Use Pin Data Input/Output Control to pull specified I/O or get I/O pin status */
    P12 = 0;
    if(P41 != 0)
    {
        i32Err = 1;
    }

    P12 = 1;
    if(P41 != 1)
    {
        i32Err = 1;
    }

    if(i32Err)
    {
        printf("  [FAIL].\n");
    }
    else
    {
        printf("  [OK].\n");
    }

    /* Configure P1.2 and P4.1 to default Quasi-bidirectional mode */
    GPIO_SetMode(P1, BIT2, GPIO_PMD_QUASI);
    GPIO_SetMode(P4, BIT1, GPIO_PMD_QUASI);
以及配置为准双向模式。
实际上有四种模式
4种 I/O 模式: 输入模式带高阻 推挽输出 开漏输出 准双向
因此这例子还不够全面
看库函数
/**
* @brief       Set GPIO operation mode
*
* @param[in]   port        GPIO port. It could be P0, P1, P2, P3 or P4.
* @param[in]   u32PinMask  The single or multiple pins of specified GPIO port. It could be BIT0 ~ BIT7.
* @param[in]   u32Mode     Operation mode. GPIO_PMD_INPUT, GPIO_PMD_OUTPUT, GPIO_PMD_OPEN_DRAIN, GPIO_PMD_QUASI
*
* @return      None
*
* @details     This function is used to set specified GPIO operation mode.
*/
void GPIO_SetMode(GPIO_T *port, uint32_t u32PinMask, uint32_t u32Mode)
{
    uint32_t i;

    for(i = 0; i < GPIO_PIN_MAX; i++)
    {
        if(u32PinMask & (1 << i))
        {
            port->PMD = (port->PMD & ~(0x3 << (i << 1))) | (u32Mode << (i << 1));
        }
    }
}
还有一个开漏输出,而例子中的普通输出就是推挽输出,这样具备输出驱动能力,而开漏输出不具备。需要外部实现驱动能力。

使用特权

评论回复
12
antusheng|  楼主 | 2017-12-13 19:52 | 只看该作者
对于PIN唤醒掉电模式的用法,就是系统进入掉电休眠模式后,通过触发IO中断,可以唤醒系统,从掉电模式恢复出来
系统的配置不变,设置一个管脚作为中断输入,这里选择P1.3为例,设置为输入模式,上升沿触发中断,然后通过特别的
掉电函数进入掉电模式。
掉电模式的进入方式

/*---------------------------------------------------------------------------------------------------------*/
/*  Function for System Entry to Power Down Mode                                                           */
/*---------------------------------------------------------------------------------------------------------*/
void PowerDownFunction(void)
{
    /* To check if all the debug messages are finished */
    UART_WAIT_TX_EMPTY(UART0);

    SCB->SCR = 4;

    CLK->PWRCON = (CLK->PWRCON & ~(CLK_PWRCON_PWR_DOWN_EN_Msk | CLK_PWRCON_PD_WAIT_CPU_Msk)) |
                  CLK_PWRCON_PD_WAIT_CPU_Msk | CLK_PWRCON_PD_WU_INT_EN_Msk;
    CLK->PWRCON |= CLK_PWRCON_PWR_DOWN_EN_Msk;

    __WFI();
}
固定的,不会用,直接复制粘贴。
在进入掉电设置前,我们看到这里先检测是否之前的串口发送完毕,等发送完毕后,开始配置掉电模式。

使用特权

评论回复
13
antusheng|  楼主 | 2017-12-13 19:56 | 只看该作者
做完这些,理论上是掉电休眠了,如果发生了中断会怎么样呢?我们看看中断函数
void GPIOP0P1_IRQHandler(void)
{
    /* To check if P1.3 interrupt occurred */
    if(GPIO_GET_INT_FLAG(P1, BIT3))
    {
        GPIO_CLR_INT_FLAG(P1, BIT3);
        printf("P1.3 INT occurred.\n");
    }
    else
    {
        /* Un-expected interrupt. Just clear all PORT0, PORT1 interrupts */
        P0->ISRC = P0->ISRC;
        P1->ISRC = P1->ISRC;
        printf("Un-expected interrupts.\n");
    }
}
仍然是检测中断是否发生,根本没有去管掉电的事情,因为只要触发中断了,就会自动唤醒,执行中断函数。因为中断函数结尾有个串口打印操作。因此程序主函数体,使用了再次检测是否发送完成,然后才操作新的串口打印操作。其实我认为这里再判断就是多余的了,如果串口不执行完成是不会回到主程序来的,因为CPU还在中断子函数里面。

使用特权

评论回复
14
zhuomuniao110| | 2017-12-13 19:58 | 只看该作者
写这么多,MARK一下,慢慢看

使用特权

评论回复
15
zhuotuzi| | 2017-12-13 21:05 | 只看该作者
终于看懂这4个例程的来龙去脉了。

使用特权

评论回复
16
dongliushui| | 2017-12-14 21:16 | 只看该作者
看完了,讲的不错。

使用特权

评论回复
17
heisexingqisi| | 2017-12-14 21:17 | 只看该作者
这么看果然容易多了。

使用特权

评论回复
18
xixi2017| | 2017-12-15 14:04 | 只看该作者
总算学透彻,多谢

使用特权

评论回复
19
稳稳の幸福| | 2017-12-15 20:07 | 只看该作者
新唐的就是比ST的容易上手。

使用特权

评论回复
20
稳稳の幸福| | 2017-12-15 20:07 | 只看该作者
新唐的就是比ST的容易上手。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

83

主题

1470

帖子

5

粉丝