在类似于 Arduino 的 AVR 微控制器中实现引脚编号
Posted
技术标签:
【中文标题】在类似于 Arduino 的 AVR 微控制器中实现引脚编号【英文标题】:Implementing pin numbers in AVR microcontroller similar to Arduino 【发布时间】:2016-11-16 13:49:24 【问题描述】:我想为 Atmel ATMega32U4 实现类似于 Arduino 的引脚号的东西。我查看了 Arduino 的 digitalWrite
命令和相关的源文件以了解它们是如何做到的,但我认为这有点令人费解,所以我想实现一个更基本的版本。
这个想法是让整数 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,但是当我测试输出时它没有响应。我想我必须写入 some 内存位置,但不是与预期寄存器对应的位置。有人发现我尝试这样做的方式有问题吗?
【问题讨论】:
为什么不使用 gcc 提供的标准文件 for avr?另外:如果您使用 pin_write 方法为引脚编写一些初始化,那么您只会在 prog 的启动阶段浪费大量代码。所有这些都可以在编译时通过模板代码完成,而不是单独使用每个引脚。同一作业设置 ddr 和端口寄存器一次而不是 8 次。 @Klaus,你指的是什么标准文件?我不知道现有的代码可以做我想做的事。此外,我不关心现阶段的启动优化 - 但感谢您的建议。 这对我来说似乎是在浪费宝贵的 RAM 和 ROM 空间。你的问题不是这里的主题。我们是 noi 讨论网站,它太自以为是了。编辑:对不起,我想到了最后一句话。请提供更多细节什么是错的。见How to Ask。 【参考方案1】:作为对我给出的评论的回答:
你指的是什么标准文件?
来自安装了 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
是很浪费的。并且定义自己的寄存器名称和引脚名称是没有用的。如您所见,所有引脚都已定义。
您认为只有一个逻辑端口号位于魔术定义端口上的“某处”的想法对我来说不是很自然。如果您设计电路和软件,则必须按照数据表中的说明处理引脚。没有“逻辑引脚号”。这个想法对更大的代码大小没有任何帮助:-)
此外,您还必须记住哪些引脚还具有多种功能,例如用于 uart 和所有其他硬件的东西。因此,移动到另一台设备总是需要审查引脚/端口的使用情况。这里的逻辑端口号也是硬件和软件自然方式之间的一个复杂层。
【讨论】:
【参考方案2】:您的代码无法运行的原因是这一步:
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;
【讨论】:
谢谢,成功了!以上是关于在类似于 Arduino 的 AVR 微控制器中实现引脚编号的主要内容,如果未能解决你的问题,请参考以下文章