2016-11-16 39 views
0

我想为Atmel ATMega32U4实现与Arduino的引脚号类似的东西。我看过Arduino的digitalWrite命令和相关的源文件,看看它们是如何做的,但我认为这有点复杂,所以我想实现一个更基本的版本。在AVR微控制器中实现类似于Arduino的引脚号

这个想法是有整数1到n代表AVR芯片上的每个I/O引脚。我开始用指针数组的DDR/PORT寄存器,其中该指数将代表销地址:

volatile uint8_t *pin_port_dir_regs[] = { 
    0,    // NOT USED 
    0,    // NOT USED 
    0,    // NOT USED 
    0,    // NOT USED 
    0,    // NOT USED 
    0,    // NOT USED 
    0,    // NOT USED 
    &DDRE,   // PIN_7 
    &DDRB,   // PIN_8 
    0,    // NOT USED 
    0,    // NOT USED 
    0,    // NOT USED 
    0,    // NOT USED 
    0,    // NOT USED 
    0,    // NOT USED 
    &DDRB,   // PIN_15 
    &DDRB    // PIN_16 
}; 

volatile uint8_t *pin_port_out_regs[] = { 
    0,     // NOT USED 
    0,     // NOT USED 
    0,     // NOT USED 
    0,     // NOT USED 
    0,     // NOT USED 
    0,     // NOT USED 
    0,     // NOT USED 
    &PORTE,   // PIN_7 
    &PORTB,   // PIN_8 
    0,     // NOT USED 
    0,     // NOT USED 
    0,     // NOT USED 
    0,     // NOT USED 
    0,     // NOT USED 
    0,     // NOT USED 
    &PORTB,   // PIN_15 
    &PORTB    // PIN_16 
}; 

我还需要在每一个的DDRx/PORTx寄存器的知道比特数,所以我创造了另一个数组:

const uint8_t pin_bits[] = { 
    _BV(0), // NOT USED 
    _BV(0), // NOT USED 
    _BV(0), // NOT USED 
    _BV(0), // NOT USED 
    _BV(0), // NOT USED 
    _BV(0), // NOT USED 
    _BV(0), // NOT USED 
    _BV(6), // PIN_7 
    _BV(4), // PIN_8 
    _BV(0), // NOT USED 
    _BV(0), // NOT USED 
    _BV(0), // NOT USED 
    _BV(0), // NOT USED 
    _BV(0), // NOT USED 
    _BV(0), // PIN_14 
    _BV(1), // PIN_15 
    _BV(3) // PIN_16 
}; 

要设置引脚模式,并写入到脚,我创建了以下功能:

void pin_mode(uint8_t pin, uint8_t direction) { 
    // defeference the pointer to the direction register 
    uint8_t port_dir_register = *(pin_port_dir_regs[pin]); 

    // get pin mask 
    uint8_t mask = pin_bits[pin]; 

    // set its mode 
    if (direction == INPUT) { 
     port_dir_register &= ~mask; 
    } else { 
     port_dir_register |= mask; 
    } 
} 

void pin_write(uint8_t pin, uint8_t level) { 
    // defeference the pointer to the output register 
    uint8_t port_out_register = *(pin_port_out_regs[pin]); 

    // get pin mask 
    uint8_t mask = pin_bits[pin]; 

    // set output 
    if (level == LOW) { 
     port_out_register &= ~mask; 
    } else { 
     port_out_register |= mask; 
    } 
} 

什么是应该发生的是,你会打电话如pin_mode(7, OUTPUT)将引脚7设置为输出,然后pin_write(7, HIGH)将输出设置为1(其中OUTPUT和HIGH是预定义的宏)。代码编译并成功上传到AVR,但是当我测试输出时,它不响应。我想我必须写一些内存位置,但不是对应于预期的寄存器。有没有人看到我试图做到这一点的方式有问题?

+0

为什么你不使用gcc提供的标准文件avr?另外:如果你使用类似pin_write方法的方式为引脚写入一些初始化,则只会为prog的开始阶段浪费大量代码。所有这些都可以在编译时通过模板代码完成,而不是单独针对每个引脚进行操作。为相同的作业设置一次不是8次的ddr和端口寄存器。 – Klaus

+0

@Klaus,你指的是什么标准文件?我没有意识到现有的代码可以做我想做的事情。 此外,我并不关心现阶段的启动优化 - 但感谢您的建议。 – Sean

+0

这看起来很浪费宝贵的RAM和ROM空间给我。你的问题不在这里讨论。我们是noi讨论网站,它太过于自负。编辑:对不起,我有最后一句话。请提供更详细的信息。见[问]。 – Olaf

回答

1

的原因,为什么你的代码不能工作这一步是:

uint8_t port_out_register = *(pin_port_out_regs[pin]); 

你要写信给位于特定地址的硬件寄存器。要做到这一点,你需要:

*(pin_port_out_regs[pin]) = some_value; 

否则,你只会修改堆栈内存没有收益。

尝试改变这个代替:

void pin_write(uint8_t pin, uint8_t level) 
{ 
    uint8_t *port_out_register = pin_port_out_regs[pin]; 
    uint8_t mask = pin_bits[pin]; 

    if (level == LOW) { 
     *port_out_register &= ~mask; 
    } else { 
     *port_out_register |= mask; 
    } 
} 
+0

谢谢,这样做! – Sean

2

至于答案的评论我给:

你指的是什么标准的文件吗?

从安装了avrlibc的avr-gcc是每个可用控制器的标准文件集合。

如果你的代码是一样的东西:

#include <avr/io.h> // automatic include correct io from device like 
        // <avr/iom32u4.h> in your case. That file defines 
        // all registers and pin descriptions and also irq 
        // vectors! 

// So you can write: 
int main() 
{ 
    DDRA= 1 << PA4; // enable output Pin 4 on Port A 
    PORTA= 1 << PA4; // set output Pin4 on Port A high 

    // and normally on startup all needed output pins will be 
    // set with only one instruction like: 
    // To set Pin 2..4 as output 
    DDRA= (1 << PA4) | (1 << PA3) | (1 << PA2); 
} 

做一个简单的PORTA= 1 << PA4的函数调用是一个很大的浪费。并且定义你自己的寄存器名称和引脚名称是没用的。所有的引脚已经被定义,你可以看到。

你的想法只有一个逻辑端口号位于“某处”魔术定义的端口看起来不是很自然。如果您设计电路和软件,则必须按照数据表中所述处理引脚。没有“逻辑引脚号”。这个想法只对更大的代码大小有帮助:-)

此外,你必须记住哪些引脚也有像uarts和所有其他硬件东西的多个功能。因此移动到其他设备将始终需要查看引脚/端口使用情况。这里的逻辑端口号也是硬件和自然软件之间的一个复杂层。

相关问题