当我们向GPIO写0时会发生什么

时间:2014-02-27 15:06:46

标签: embedded avr gpio

我想知道当我们向GPIO写“0”时会发生什么(实际意图是拉下输出)。

我不确定控制器是否会将GPIO引脚拉低并将其内部连接到地,或者只是在内部断开引脚,并使其与微控制器连接的外设(LED)断开连接。

我的实际任务是:

我必须从4个gpio引脚发送8位数据(Ex PORTA4-7)。所以要做到这一点,我必须写高位(D4-D7)然后(D0-D3)。

我的方法是:

% Writing the higher bits first
PORTA=PORTA|(DATA & 0xF0);

%Writing the lower bits
PORTA=PORTA|((DATA & 0x0F)<<4);

主要疑问是“这段代码是否会干扰剩余的GPIO(PORTA0-3)”。因为我将它们连接到4个ADC输入。

3 个答案:

答案 0 :(得分:3)

了解GPIO如何耦合在一起的最佳方法是访问数据表。 TI的MSP430数据表就是一个很好的例子。

输入/输出部分非常明确地说明了GPIO寄存器如何与IO焊盘本身进行交互。这是端口P1.0-P.17(来自上面数据表的第80页)。

enter image description here

<强>输出

这里的基本要点是:

  • 如果IO焊盘处于输出模式,向P1OUT.x写入'0'(例如bit0为P1OUT.0)将驱动输出为低电平。

这是一个简单的简化版本:

enter image description here

<强>输入

同样,这里是检查在“输入”模式下使用IO垫

enter image description here

这里的基本要点是,在这里写入P1OUT.x对输出没有影响;输出缓冲区是三态的!

<强>危险

如果您的程序在多个任务中共享一个GPIO端口寄存器,那么您必须确保对该寄存器的访问是线程安全。对于嵌入式系统,这通常意味着“确保ISR不会搞砸”。考虑 -

//... (from main thread)
P1OUT = P1OUT | 0x02;

//... (from some ISR)
P1OUT = P1OUT | 0x01;

这通常转化为以下的反汇编

//... (from main thread)
mov  P1OUT,  %some_reg
operate on %some_reg
mov   %some_reg, P1OUT

//... (from some ISR)
mov  P1OUT,  %some_reg
operate on %some_reg
mov   %some_reg, P1OUT

//and then the time series looks like   (P1OUT = 0 initially)
[0]:  mov P1OUT,  %some_reg_main_thread  (%reg_m = 0)
[1]:  or  0x02,   %some_reg_main_thread  (%reg_m = 2)
[2]:  ISR fires!
[3]:  mov P1OUT,  %some_reg_isr_thread   (%reg_i = 0)
[4]:  or  0x01,   %some_reg_isr_thread   (%reg_i = 1)
[5]:  mov %some_reg_isr_thread, P1OUT    (P1OUT  = 1)
[6]:  ISR exits
[7]:  mov %some_reg_main_thread, P1OUT   (P1OUT  = 2)

(end result: P1OUT=2, but should be 3 (0x01 | 0x02))

此外,IO Pads并不像它们看起来那么简单,您应该始终访问数据表以了解它是多么糟糕/微妙/混淆/等等。

例如,在上面的TI示例中,以下危险适用于您的原始问题:

(而P1DIR.x = 0)

  • P1REN.x + P1OUT.x

    • 如果P1REN.x为'1',并且您切换P1OUT.x,也会打开和关闭上拉电阻

enter image description here

当然,IO pad拓扑是特定于MCU的。在推进项目之前,我建议您查看数据表中的IO-Pad拓扑。它应该是你最好的朋友。

代码惯例

尽可能考虑使用| =,&amp; =和^ =运算符,例如:

P1OUT |= (1<<2)+(1<<3)   //assert pin2 & pin3 high

如果不能,请考虑使用压缩结构或经过单元测试的封装例程:

struct myporta {
    uint    b3_0    : 4;     //use unsigned int-type
    uint    b4      : 1;
    uint    b5_10   : 6;
    uint    b25_b11 : 15;
    uint    b26     : 1;
    uint    b31_27  : 5;
} __attribute__ ((packed));  //gcc syntax

...
//write to my LED
myporta *porta = &PORTA;   //if you w

porta->b26 = 1;            //turn the led on

没有什么比gpio互相破坏更糟糕了!

答案 1 :(得分:1)

MCU将主动驱动引脚为低电平,您可以将其视为接地引脚more info here

PORTA=PORTA|(DATA & 0xF0);

这不会影响4个下部引脚,但仍然不正确。如果PORTA已设置为高电平且数据包含零,则不会将引脚驱动为低电平。你想掩盖DATA的相关部分,你只想保留你没有改变的PORTA部分:

PORTA = (PORTA & 0x0F) | (DATA & 0xF0);

然后类似地设置4 lsb:

PORTA = (PORTA & 0xF0) | (DATA & 0x0F);

答案 2 :(得分:1)

是的,写入PORTA会干扰所有位,即使其中一些是DDRA的指定输入。

你必须确保在低位读取ADC读数时不要写高位。

甚至像

这样的东西
% Writing the higher bits first
PORTA=PORTA|(DATA & 0xF0);

%Writing the lower bits
PORTA=PORTA|((DATA & 0x0F)<<4);

不起作用。读取右侧的PORTA将为其引脚提供一些值,即0或1,即使电压介于两者之间。然后将该值写入同一引脚,暂时将其驱动为高电平或低电平。