当我打开/关闭Arduino Uno上的LED时,我正试图了解“引擎盖下”会发生什么。
带有硬件项目的基本Hello World似乎是闪烁的板载LED。对于Arduino,有一个LED连接到引脚12。
我看了digitalWrite
的源代码:
void digitalWrite(uint8_t pin, uint8_t val)
{
uint8_t timer = digitalPinToTimer(pin);
uint8_t bit = digitalPinToBitMask(pin);
uint8_t port = digitalPinToPort(pin);
volatile uint8_t *out;
if (port == NOT_A_PIN)
return;
// If the pin that support PWM output, we need to turn it off
// before doing a digital write.
if (timer != NOT_ON_TIMER)
turnOffPWM(timer);
out = portOutputRegister(port);
uint8_t oldSREG = SREG;
cli();
if (val == LOW) {
*out &= ~bit;
}
else {
*out |= bit;
}
SREG = oldSREG;
}
这里发生了什么?
特别是函数末尾的位错位。
答案 0 :(得分:8)
AVR设备上的I / O分别安排在8个引脚的端口中。不同的设备具有不同数量的端口,这些端口使用字母命名。端口一次写入8位。
例如,要写入PORTA,您可以说PORTA = 0xFF;
,这将打开PORTA上的每个引脚。
现在,Arduino平台还有引脚,这些引脚在所有可能的AVR芯片上都有编号和标准化。有一个从特定AVR器件引脚到Arduino引脚的映射,digitalWrite()
函数必须找出它。查看特定芯片的数据表,了解端口和引脚是什么。例如,在Arduino Uno上,Arduino数字引脚0对应PORTD上的0引脚。
digitalWrite()
顶部的两个函数决定了我们需要的AVR端口和引脚。
也可能将所需的引脚连接到定时器,使用脉冲宽度调制或PWM将其打开和关闭。如果是这样,那么我们需要确保禁用此功能。
要在端口上写入引脚,我们使用一些位算术。例如,要将PORTB上的引脚4设置为高电平(Arduino引脚12),我们使用PORTB = PORTB | (1<<4);
或PORTB |= (1<<4);
。也就是说,保持所有其他引脚相同,但将引脚4设为高电平。
将引脚设置为低电平类似。我们希望单独保留其他位,因此我们and
的数字几乎都是1。 PORTB &= ~(1<<4);
。
最后一个魔力是我们不希望在设置端口位时被打断。为此,我们使用cli();
禁用中断。但是我们并不想在完成后启用中断;我们开始时可能没有启用它们。
诀窍是保存它们是否被启用,这是状态寄存器SREG中的一个位。因此,程序是保存SREG,如果它们尚未被禁用则禁用中断,执行我们想要的操作,然后将SREG恢复到我们(可能)更改I
位之前的状态。 / p>