时间:2010-07-23 18:55:08

标签: c variables avr

8 个答案:

答案 0 :(得分:4)

你的问题仍然有点不清楚(正如答案中各种各样的解释所表明的那样)。我假设你想通过物理引脚号来引用引脚。如果这不正确,请澄清您的问题,以便我们提供更好的答案。

如果有人拿枪瞄准我的话,我大致会怎么做:

免责声明:我没有对此进行测试,也没有特别注意检查文档。该代码是针对Linux上的avr-gcc / avr-libc编写的,尽管它可能在其他地方有用。

// Map from physical pin number to associated direction register.
volatile uint8_t *ddr_map[] = {
    NULL, // Vcc, GND, or some other non-IO pin.
    &DDRB,
    &DDRB,
    &DDRC,
    // etc...  Values will vary for different target chips.
};

// Map from physical pin number to port mask.
uint8_t mask_map[] = {
    0x00,
    _BV(0),
    _BV(1),
    _BV(0),
    // etc...  Values will vary for different target chips.
}

typedef enum {
    IN,
    OUT
} PinDir;

void setMode(int pin, PinDir dir) {
    if(dir == OUT) {
        *ddr_map[pin] |= mask_map[pin];
    } else {
        *ddr_map[pin] &= ~mask_map[pin];
    }
}

请参阅http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_port_pass

这就是为什么这不是一个好主意:

  1. 它不抽象出任何有意义的行为(它实际上消除了抽象 - 物理引脚数低于逻辑端口/引脚)。而且,对于不同的封装格式,物理引脚号不一定相同。 PORTB的引脚可能不会被分配到QFP封装上与PDIP封装相同的物理引脚编号。因此,此代码实际上更多令人困惑。

  2. 增加了开销。你有一个额外的函数调用(需要花费周期和堆栈)和两个(或更多)用于查找的数组(除非你采取特殊措施,否则它们会花费AVR上的闪存和RAM,在这种情况下它们需要额外的周期和闪存或EEPROM)更不用说所有的间接(数组查找,指针解除引用)和额外的比较和分支。在台式机和网络开发你可以嘲笑我对这么小的成本的担忧,但在AVR上,浪费会产生更大的影响。 (注意:你或许可以说服编译器优化其中的一部分,但是如果你使用-Os则会很困难。现在你担心甚至比以前更低级别的细节......)

  3. 提供的操纵销的方法并不复杂,值得以这种方式隐藏。你应该习惯在头脑中转换十六进制和二进制(这并不难)。即使您不想使用十六进制,_BV()宏也可以使针脚操作变得非常简单(或者只使用(1 << x),这样更便携,并且会被更多程序员识别。)

  4. 顺便说一句,PORTBDDRB等不是常量。它们是与特定地址或寄存器相关联的变量。尝试用CONST_THINGY |= 0x03之类的东西修改常量会产生编译错误。

    变量

    C没有您描述的功能。它是一种低级语言(有时被称为“高级程序集”),它不提供许多花哨的功能(按照今天的标准)。这就是为什么它是AVR的首选语言 - 你希望靠近硬件,而且你不需要额外的开销。

    C有什么指针。根据你的问题和评论,我猜你不是很熟悉它们,所以这里有一个简单的解释:

    • &运算符返回指向变量的指针,使用方式如下:pointer = &variable;
    • *实际上有几个用途。
      • 第一个是声明一个指针变量(即一个包含指针而不是int,char或float的变量):int *pointer;请注意,您必须指定它将指向的变量类型。 / LI>
      • 第二种用法是所谓的解除引用指针。基本上,这意味着通过指针访问变量。如果pointer指向variable,则*pointer = 42;会将variable设置为42,other_var = *pointer会将other_var设置为variable的值}}。
    • 还有指针算术,但这超出了这个答案的范围。

    所有这一切的重点在于,您可以有效地将变量本身视为值,存储它们并传递它们。除了操纵它们的值之外,你无法以任何有意义的方式修改它们,但你也不需要。

答案 1 :(得分:3)

答案 2 :(得分:1)

答案 3 :(得分:1)

C具有宏功能,可以像这样使用

#define oof(a, b) a##b

int x1 = 5;
oof(x, 1) = 10;
printf("%d", x1); //prints 10
int oof(x, 2) = 2;
printf("%d", x2); //printf 2

它可以是一个函数,可以使用其他函数,可以调用其他宏,等等。 这里的“ ##”是预处理程序运算符,用于将其旁边的对象连接起来。

答案 4 :(得分:0)

答案 5 :(得分:0)

答案 6 :(得分:0)

答案 7 :(得分:0)

对于一般情况,指针尽可能接近。 C在运行时不一定具有任何名称概念,特别是在微控制器上(某些名称通常存在于具有动态链接的OS中,但即使在那里也不需要)。

对于引脚号方案,查找表以确定任何给定数字的端口,端口位等等都可以。这就是Arduino采用的技术,它试图在AVR上抽象出C ++编程。他们喜欢重命名,例如调用PWM信号“analogWrite”,C ++“接线”和程序“草图”,所有I / O引脚都在开发板上的位置后编号。缺点是,只要你编写第一块板以外的其他东西就会产生巨大的混乱,并且当你想要做一些低级别的事情时,必须弄清楚它们库里的副作用。