avr asm从变量端口输出

时间:2017-09-17 00:07:29

标签: c++ assembly avr

将Arduino IDE用于Arduino Uno ...

我可以使用PORTB之类的宏成功发送到端口,但我无法弄清楚如何将数据发送到变量中定义的端口,如下所示:

uint8_t pin = 0; // any value...
uint8_t port = digitalPinToPort(pin);
uint8_t *portreg = portModeRegister(port);
uint8_t portsfr = _SFR_IO_ADDR(port);

asm volatile
(
    // other asm instructions...

    "out %[port], %[masklo] \n\t"
    ::
    [port]      "I" (_SFR_IO_ADDR(PORTB)) // works

    // [port]   "I" (_SFR_IO_ADDR(port)) // doesn't compile
    // [port]   "I" (portreg) // doesn't compile
    // [port]   "I" (portsfr) // doesn't compile
);

我发现以下文章哪些接缝相关但未显示示例如何:avr gcc inline asm variable input operand

3 个答案:

答案 0 :(得分:1)

AVR的inoutsbicbi指令只接受文字I / O地址,因此必须在编译时知道它们正在访问的寄存器。 pololu-led-strip-arduino库是一个库的示例,它使用模板来解决这个问题:模板参数指定Arduino引脚号,编译器在编译时查找正确的I / O地址并将其加入程序集函数的代码。

如果这种方法对你不起作用,一种更简单的方法是使用switch语句将引脚号(或使用什么引脚的其他规范)从编译时未知的东西转换为是:

switch (pin_number)
{
case 1:
  // some assembly using pin "1"
  break;
case 2
  // some assembly using pin "2"
  break;
// ...
}

第三种方法:AVR指令集确实支持指针,我相信你可以设置指针指向I / O寄存器,然后通过指针从寄存器读写。所以你应该尝试编译这样的代码并查看工具链提供的反汇编列表,看看它是如何在汇编中完成的:

void write_to_reg_through_pointer(uint16_t value) {
  volatile unsigned char * volatile ptr = &PORTB;
  *ptr = value;
}

答案 1 :(得分:1)

注意,任何I / O寄存器都可以通过它的存储单元进行寻址。因此,您只需要使用指向该内存位置的指针。 portModeRegister(port)返回指向 DDRx 寄存器或指定端口的指针。还有portOutputRegister(port) PORTx 寄存器返回指针,为{em> PINx 返回portInputRegister()

volatile uint8_t * p_port = portOutputRegister(port);

*p_port = xxx; // writing to the PORTx by a pointer
uint8_t yyy = *p_port; // reading the PORTx value

另外,您可以使用PORTx宏直接将指针指定给寄存器:

volatile uint8_t * p_port = &PORTB;

<强>实用生活

由于在ATmega328P(在Arduino中)以及许多其他AVR中,端口寄存器都以相同的顺序放置在后续位置,通常在:PIXx,DDRx,PORTx。 因此,不是存储三个指针,而是只使用较低的指针(即PINx),然后将指针作为数组访问:

volatile uint8_t * p_base = portInputRegister(port); // obtaining pointer to PINx

p_base[1] = ddr_val; // writing the DDRx value: offset + 1
p_base[2] = port_val; // writing the PORTx value: offset + 2
uint8_t pin_val pin_val = p_base[0]; // reading the PINx value: offset 0

答案 2 :(得分:0)

AVR指令集不支持IN和OUT指令的非立即参数。因此,根本没有机器指令可以完全按照您的要求进行操作。

因此,如果没有引用自动修改代码,这些代码自然禁止在从闪存运行的系统上运行,那么就无法达到你想要的效果。

AVR上可能的端口数量有限,通常为3 - 所以使用switch-case和一些枚举实现它会很容易且相对便宜:

typedef enum {PortA, PortB, PortC} portNo;

void out (portNo, value) {
   switch (portNo) {
      case PortA:
         out (PORTA, value);
         break;
      ...