将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
答案 0 :(得分:1)
AVR的in
,out
,sbi
和cbi
指令只接受文字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;
...