打印
[牛人杂谈]

双向IO口和准双向IO口

[复制链接]
6635|10
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
C51的说明书上说:”Because Ports 1, 2, and 3 have fixed internal pullups, they are sometimes called quasi-bidirectional
ports. When configured as inputs, they pull high and source current (IIL) when externally pulled low.??Port 0, on the other
hand, is considered truly bidirectional, because it floats when configured as an input. "
翻译是:因为P1、2、3有固定的内部上拉电阻,所以有时称它们为准双向口。当用做输入时被拉高,低则要靠外部电路拉低。而P0则是真双向口,因为作为输入时它是悬浮的(因为没有上拉电阻)。
不是很清晰易懂.
我很纳闷,这么重要的一个问题,为什么教科书、老师都没有好好的讲清楚,包括我自己经历的也是这样.我也是一直被这个问题捆饶了很久
.我说说我的看法:
像上面说的一样,"准"就是"基本上"的意思.
其实重点在P0口.
P0口是双向指的是它被用作地址/数据端口时,只有在这个时候,P0口才处于两个开关管推挽状态,当两个开关管都关闭时,才会出现高阻状态.
当P0口用于一般I/O口时,内部接Vcc的那个开关管是与引脚(端口)脱离联系的,这个时候,只有拉地的那个开关管起作用,P0口作为输出,是必须外接上拉电阻的,不然就无法输出高电平;如果P0口作为输入,则必须先对端口写1,使拉地的开关管断开,这个时候,如果不接上拉电阻,则是高阻状态,就是一个双向口,如果接上拉电阻,则本身输出高电平,对输入信号的逻辑无影响(注意是对逻辑无影响,对实际参数有无影响我不确定,但是我认为是有的).
双向与准双向,根本原则是双向包含了高阻这个状态,而不在于是否需要先写1或者不写,P1~P3口因为有内部上拉电阻,因此无论如何不是双向;P0口内部无上拉电阻,在处于数据/地址功能时,自动完成3态的转换,是双向,处于一般I/O口时,如果不接外部上拉,而且先向端口写了1,那么就处于高阻状态,此时,它也是一个人为的双向口,这与它处于地址/数据功能时的自动双向有区别,以及与P1~P3处于输入时输出锁存器为1是有区别的。
单片机的P0~P3端口进行输入时为什么要设置为1?
给锁存器写1,那么锁存器的反向端就输出0,那么和它相接的MOS管也就在截状态,也就是呈高阻态,这样P0口上数据就会从读引脚的三态缓冲器上正确的输入,如果不写1,那么锁存器上次锁存的可能为0,那么反向端有可能出现1,这样和反向端相接的MOS管就倒通,也就是直接拉到地,那么不管你P0口上输入什么信号都会拉成低电平,输入就错了.
为什么我输入没设置1,还是正确的呢?
一般情况下,单片机复位后P0-P3端口默认值为1,如果仅作为输入,中途不改变,无需我们先设置1.
双向口与准双向口的区别主要是:准双向口I/O口操作时做数据输入时需要对其置1, 是扯淡!!!!

沙发
dongnanxibei|  楼主 | 2017-2-20 20:09 | 只看该作者
如果你想读入,必须先写1,单片机复位后,端口都被复位成FFH。
(之所以叫准双向,就是在读入前必须写1到端口)
具体要看硬件电路是怎么样的。
51单片机准双向IO口有四组,每组有0到7个口,也是Px^0-Px^7。那么例如你说的要取P1一组的话就要先写P1=0xff;,只要对P0^0一个口取数据的话就写P0^0=1;

使用特权

评论回复
板凳
dongnanxibei|  楼主 | 2017-2-20 20:09 | 只看该作者
准双向口输出类型可用作输出和输入功能而不需重新配置口线输出状态。这是因为当口线输出为1时驱动能力很弱,允许外部装置将其拉低。当引脚输出为低时,它的驱动能力很强,可吸收相当大的电流。准双向口有3个上拉晶体管适应不同的需要。在3个上拉晶体管中,有1个“极弱上拉”,当口线锁存为1时打开。当引脚悬空时,这个极弱的上拉源产生很弱的上拉电流将引脚上拉为高电平。第2个上拉晶体管称为“弱上拉”,当口线寄存器为1且引脚本身也为1时打开。此上拉提供基本驱动电流使准双向口输出为1。如果一个引脚输出为1而由外部装置下拉到低时,弱上拉关闭而“极弱上拉”维持开状态,为了把这个引脚强拉为低,外部装置必须有足够的灌电流能力使引脚上的电压将到门槛电压以下。第3个上拉晶体管称为“强上拉”。当口线锁存器由0到1跳变时,这个上拉用来加快准双向口由逻辑0到逻辑1转换。当发生这种情况时,强上拉打开约2个机器周期以使引脚能够迅速地上拉到高电平。

使用特权

评论回复
地板
dongnanxibei|  楼主 | 2017-2-20 20:18 | 只看该作者
新唐的单片机IO通常是有四种模式的
4种 I/O 模式:
 输入模式带高阻
 推挽输出
 开漏输出
 准双向
 准双向TTL/Schmitt 触发输入模式由Px_MFP[23:16]选择
 每个I/O 引脚都可以作为中断源,支持边沿/电平触发
 准双向模式下, I/O引脚内部上拉电阻被使能
 引脚中断功能使能后,引脚的唤醒功能也将被使能
 复位后,所有I/O引脚默认模式由CIOINI(CONFIG[10])决定(只有M05xxDN/DE)
 CIOINI = 0, 复位后所有GPIO引脚为输入三态模式
 CIOINI = 1, 复位后所有GPIO引脚为准双向模式

使用特权

评论回复
5
dongnanxibei|  楼主 | 2017-2-20 20:19 | 只看该作者
输出模式
设置Px_PMD(PMDn[1:0])为 01’b, Px.n为输出模式, I/O引脚支持数字输出功能,有拉电流/灌电流能
力。 Px_DOUT 相应位的值被送到相应引脚上。

使用特权

评论回复
6
dongnanxibei|  楼主 | 2017-2-20 20:19 | 只看该作者
输入模式
设置 Px_PMD(PMDn[1:0]) 为00’b , Px.n为输入模式, I/O引脚为三态(高阻态),没有输出驱动能力。
Px_PIN的值反映相应端口引脚的状态。

使用特权

评论回复
7
dongnanxibei|  楼主 | 2017-2-20 20:20 | 只看该作者
开漏输出模式
设置 Px_PMD(PMDn[1:0])为 10’b, Px.n为开漏模式, I/O支持数字输出功能, 但仅有灌电流能力, 为了
把I/O引脚拉到高电平状态,需要外接一颗上拉电阻。 如果Px_DOUT相应位的值为“0”,引脚上输出低
电平. 如果Px_DOUT 相应位的值为“1”, 该引脚由外部上拉电阻控制输出高电平。

使用特权

评论回复
8
dongnanxibei|  楼主 | 2017-2-20 20:22 | 只看该作者
准双向模式
设置Px_PMD(PMDn[1:0]) 为 11’b, Px.n引脚为准双向模式, I/O同时支持数字输出和输入功能,但拉电流仅达数百uA. 要实现数字输入功能,需要先将Px_DOUT 相应位置1。 准双向输出是80C51及其派生产品所共有的模式。 若Px_DOUT相应位为”0”, 引脚上输出为“低电平”. 若Px_DOUT相应位为”1”, 该引脚将检查引脚值. 若引脚值为高,没有任何动作,若引脚值为低, 该引脚将驱动2个时钟周期的强拉电流,然后关闭强输出驱动, 然后引脚状态由内部上拉电阻控制

使用特权

评论回复
9
dongnanxibei|  楼主 | 2017-2-20 20:27 | 只看该作者
这个例程讲述了新唐单片机如何使用这些IO模式的
/**************************************************************************//**
* [url=home.php?mod=space&uid=288409]@file[/url]     main.c
* [url=home.php?mod=space&uid=895143]@version[/url]  V3.00
* $Revision: 6 $
* $Date: 15/09/02 10:04a $
* [url=home.php?mod=space&uid=247401]@brief[/url]    Show how to set GPIO pin mode and use pin data input/output control.
* @note
* Copyright (C) 2013~2015 Nuvoton Technology Corp. All rights reserved.
*
******************************************************************************/
#include "stdio.h"
#include "M451Series.h"


#define PLL_CLOCK       72000000


void SYS_Init(void)
{

    /*---------------------------------------------------------------------------------------------------------*/
    /* Init System Clock                                                                                       */
    /*---------------------------------------------------------------------------------------------------------*/

    /* Enable HIRC clock (Internal RC 22.1184MHz) */
    CLK_EnableXtalRC(CLK_PWRCTL_HIRCEN_Msk);

    /* Wait for HIRC clock ready */
    CLK_WaitClockReady(CLK_STATUS_HIRCSTB_Msk);

    /* Select HCLK clock source as HIRC and and HCLK clock divider as 1 */
    CLK_SetHCLK(CLK_CLKSEL0_HCLKSEL_HIRC, CLK_CLKDIV0_HCLK(1));

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

    /* Wait for HXT clock ready */
    CLK_WaitClockReady(CLK_STATUS_HXTSTB_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 as HXT and UART module clock divider as 1 */
    CLK_SetModuleClock(UART0_MODULE, CLK_CLKSEL1_UARTSEL_HXT, CLK_CLKDIV0_UART(1));

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

    /* Set PD multi-function pins for UART0 RXD(PD.0) and TXD(PD.1) */
    SYS->GPD_MFPL &= ~(SYS_GPD_MFPL_PD0MFP_Msk | SYS_GPD_MFPL_PD1MFP_Msk);
    SYS->GPD_MFPL |= (SYS_GPD_MFPL_PD0MFP_UART0_RXD | SYS_GPD_MFPL_PD1MFP_UART0_TXD);

}

void UART0_Init()
{
    /*---------------------------------------------------------------------------------------------------------*/
    /* Init UART                                                                                               */
    /*---------------------------------------------------------------------------------------------------------*/
    /* Reset UART module */
    SYS_ResetModule(UART0_RST);

    /* Configure UART0 and set UART0 baud rate */
    UART_Open(UART0, 115200);
}

/*---------------------------------------------------------------------------------------------------------*/
/*  Main Function                                                                                          */
/*---------------------------------------------------------------------------------------------------------*/
int32_t main(void)
{

    int32_t i32Err, i32TimeOutCnt;

    /* Unlock protected registers */
    SYS_UnlockReg();

    /* Init System, peripheral clock and multi-function I/O */
    SYS_Init();

    /* Lock protected registers */
    SYS_LockReg();

    /* Init UART0 for printf */
    UART0_Init();

    printf("\n\nCPU [url=home.php?mod=space&uid=72445]@[/url] %dHz\n", SystemCoreClock);

    printf("+-------------------------------------------------+\n");
    printf("|    PB.3(Output) and PD.7(Input) Sample Code     |\n");
    printf("+-------------------------------------------------+\n\n");

    /*-----------------------------------------------------------------------------------------------------*/
    /* GPIO Basic Mode Test --- Use Pin Data Input/Output to control GPIO pin                              */
    /*-----------------------------------------------------------------------------------------------------*/
    printf("  >> Please connect PB.3 and PD.7 first << \n");
    printf("     Press any key to start test by using [Pin Data Input/Output Control] \n\n");
    getchar();

    /* Configure PB.3 as Output mode and PD.7 as Input mode then close it */
    GPIO_SetMode(PB, BIT3, GPIO_MODE_OUTPUT);
    GPIO_SetMode(PD, BIT7, GPIO_MODE_INPUT);

    i32Err = 0;
    printf("GPIO PB.3(output mode) connect to PD.7(input mode) ......");

    /* Use Pin Data Input/Output Control to pull specified I/O or get I/O pin status */
    /* Set PB.3 output pin value is low */
    PB3 = 0;

    /* Set time out counter */
    i32TimeOutCnt = 100;

    /* Wait for PD.7 input pin status is low for a while */
    while(PD7 != 0)
    {
        if(i32TimeOutCnt > 0)
        {
            i32TimeOutCnt--;
        }
        else
        {
            i32Err = 1;
            break;
        }
    }

    /* Set PB.3 output pin value is high */
    PB3 = 1;

    /* Set time out counter */
    i32TimeOutCnt = 100;

    /* Wait for PD.7 input pin status is high for a while */
    while(PD7 != 1)
    {
        if(i32TimeOutCnt > 0)
        {
            i32TimeOutCnt--;
        }
        else
        {
            i32Err = 1;
            break;
        }
    }

    /* Print test result */
    if(i32Err)
    {
        printf("  [FAIL].\n");
    }
    else
    {
        printf("  [OK].\n");
    }

    /* Configure PB.3 and PD.7 to default Quasi-bidirectional mode */
    GPIO_SetMode(PB, BIT3, GPIO_MODE_QUASI);
    GPIO_SetMode(PD, BIT7, GPIO_MODE_QUASI);

    while(1);

}

使用特权

评论回复
10
捉虫天师| | 2017-2-20 20:55 | 只看该作者
/* Configure PB.3 and PD.7 to default Quasi-bidirectional mode */
    GPIO_SetMode(PB, BIT3, GPIO_MODE_QUASI);
    GPIO_SetMode(PD, BIT7, GPIO_MODE_QUASI);
这里配置为准双向模式,说是默认的模式,看来如果不配置就是这个模式。

使用特权

评论回复
11
天灵灵地灵灵| | 2017-2-20 20:56 | 只看该作者
一般ARM的IO口都是上电后默认为准双向模式,这样也与51接口类似兼容。

使用特权

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

本版积分规则

207

主题

3613

帖子

16

粉丝