【问题标题】:What happens when we write 0 to a GPIO当我们将 0 写入 GPIO 时会发生什么
【发布时间】:2014-03-31 02:42:30
【问题描述】:

我想知道当我们将“0”写入 GPIO 时会发生什么(实际目的是拉低输出)。

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

我的实际任务是:

我必须从 4 个 gpio 引脚(例如 PORTA4-7)发送 8 位数据。所以要做到这一点,我必须先写高位(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 输入。

【问题讨论】:

  • 取决于您的硬件以及您如何对其进行配置以了解发生的情况。
  • PORTA=PORTA|(DATA & 0xF0);需要 PORTA=(PORTA&0x0F)|(DATA & 0xF0);

标签: embedded avr gpio


【解决方案1】:

MCU会主动把pin拉低,你可以把它看成是pin接地more info here

PORTA=PORTA|(DATA & 0xF0);

这不会影响下面的4个引脚,但仍然不正确。如果 PORTA 已经设置为高电平并且数据包含零,您将不会将引脚驱动为低电平。您想屏蔽 DATA 的相关部分,并且只想保留 PORTA 中您未更改的部分:

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

然后类似地设置 4 lsb:

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

【讨论】:

    【解决方案2】:

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

    在读取低位的 ADC 读数时,您必须确保没有写入高位。

    甚至像

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

    不会工作。读取分配右侧的 PORTA 将为其引脚获得一些值,0 或 1,即使电压介于两者之间。然后该值将被写入同一个引脚,暂时将其驱动为高电平或低电平。

    【讨论】:

    • 你能建议我一种方法,我可以将数据写入四个 gpio 而无需触摸其他。和代码sn-p?谢谢
    • 没有。字节一次全部写入。这就是我要表达的观点。您的选择是使用不同的端口,或者在不同的时间进行写入和 ADC。我不知道您如何构建执行流程。在获取 ADC 值时设置一个标志,并在每次写入之前检查该标志怎么样?
    • 嗯,,你说的方法不错,我会修改...谢谢你的回复
    【解决方案3】:

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

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

    输出

    这里的基本要点是:

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

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

    输入

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

    这里的基本要点是,这里写入 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 Pad 并不像看起来那么简单,您应该经常访问数据表以了解它有多糟糕/精致/令人困惑/等等。

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

    (当 P1DIR.x = 0)

    • P1REN.x + P1OUT.x

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

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

    代码实践

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

    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 互相破坏更糟糕的了!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-12-10
      • 2011-04-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多