AVR I/O:数字与模拟输入编程
AVR I/O: DIgital vs Analog Input Programming
我正在尝试使用 Github 和 运行 中的一些先前开发的软件来完成一些有趣的软件编码。
他在大多数 PortB 引脚上使用带有数字 I/O 的 Atmel ATtiny45,但 PB3 用作来自外部电位器的模拟 (AtoD) 输入。
在他的代码中,他有以下片段:
if(PINB & MANCLK){
PORTB |= CLKOUT;
} else {
PORTB &= ~CLKOUT;
}
}
(注:'CLKOUT'为数字输出引脚。)
“MANCLK”是 PB3 的模拟输入引脚,所以,“if(PINB & MANCLK)...”行是做什么的?
它实际上是否考虑了该引脚上的所有模拟值,因此,低于 2.5V 的任何值都被视为逻辑低电平,高于 2.5V 的任何值都被视为高电平,或者,这甚至是一个有效的陈述吗?
'MANCLK' 是简单的“0”还是“1”?
不确定在哪里可以找到关于这种特殊情况的有用信息。
感谢您提供的任何帮助。
此致,
G运行t
[更新] 这是代码....
`
#include <stdio.h>
#include <stdlib.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#if !defined(TIMSK)
#define TIMSK TIMSK0
#endif
#if !defined(TIMER0_COMPA_vect)
#define TIMER0_COMPA_vect TIM0_COMPA_vect
#endif
// 100 us per tick
#define COUNT 199
#define TPSC (1<<CS01)
#define AUTO (1<<PB0) //Pin5, MOSI and EN (via RN33 Pins 4&5).
High = Enable
#define RUN (1<<PB1) //Pin6, MISO and ~HLT (via RN33 Pins 3&6).
Low = Halt
#define MANCLK (1<<PB3) //Pin2, Analog input, Manual Clock Speed
Setting (via potentiometer RV1)
#define CLKOUT (1<<PB4) //Pin3, CLK output
#define AUTOINT (1<<PCINT0)
#define RUNINT (1<<PCINT1)
#define CLKINT (1<<PCINT2)
#define MANDLY 500
volatile uint16_t count;
volatile uint8_t trigger;
volatile uint8_t manual;
uint16_t maxcnt;
void stopTimer(void) {
PORTB &= ~CLKOUT; // Clock output low
TCCR0B &= ~TPSC;
TCNT0;
count = 0;
trigger = 0;
}
void startTimer(void) {
PCMSK = 0;
TCCR0B |= TPSC;
}
void halt(void) {
cli();
stopTimer();
PCMSK = RUNINT|AUTOINT;
sei();
sleep_enable();
while(!(PINB & RUN)) sleep_cpu();
sleep_disable();
cli();
startTimer();
sei();
}
void manclk(void) {
cli();
stopTimer();
PCMSK = CLKINT|RUNINT|AUTOINT;
sei();
while(!(PINB & AUTO)) {
if(PINB & RUN) {
if(PINB & MANCLK) {
PORTB |= CLKOUT;
} else {
PORTB &= ~CLKOUT;
}
}
sleep_enable();
sleep_cpu();
sleep_disable();
}
cli();
startTimer();
sei();
}
int main(void)
{
DDRB = CLKOUT;
PORTB &= ~CLKOUT;
cli();
TCCR0A = 0;
TCCR0B = 0;
TCNT0 = 0;
TCCR0A |= (1 << WGM01);
OCR0A = COUNT; // Interrupts every 100us when Timer running
TCCR0B |= (1 << CS01); // Timer Start /8 pre-scaler
TIMSK |= (1 << OCIE0A);
GIMSK |= (1 << PCIE);
sei();
// Setup the ADC
//ADMUX |= (1 << MUX1); // Only using ADC2 (original)
ADMUX |= (1 << MUX0) | (1 << MUX1); //new code - MUX Select ADC3 @
PB3
ADCSRA |= (1 << ADEN);
ADCSRA |= (1 << ADPS2) | (1 << ADPS1); // 125 kHz ADC clock
//Enable Sleeping
set_sleep_mode(SLEEP_MODE_IDLE);
while (1) {
//if RUN = HLT = Low ==> HALT
if (!(PINB & RUN)) {
halt();
continue;
}
//if AUTO = ENABLE = Low ==> manclk...if AUTO (ENABLE) == LOW ==>
MANCLK
if (!(PINB & AUTO)) {
manclk();
continue;
}
ADCSRA |= (1 << ADSC);
while (ADCSRA & (1 << ADSC));
maxcnt = 5000 / (64 - ADC/16);
if (trigger) {#
PORTB ^= CLKOUT;
trigger = 0;
}
sleep_enable();
sleep_cpu();
sleep_disable();
}
return 0; // never reached
}
ISR(TIMER0_COMPA_vect) {
count++;
if (count >= maxcnt) {
count = 0;
trigger = 1;
}
}
EMPTY_INTERRUPT(PCINT0_vect);`
如果我们扩展一些预处理器宏,您的代码将变得更容易解释:
if (PINB & (1 << 3))
{
PORTB |= (1 << 4);
}
else
{
PORTB &= ~(1 << 4);
}
现在只要看看上面的代码,我们就可以知道它做了以下事情:它读取 PINB 寄存器的第 3 位,如果它是 1,它将 PORTB 寄存器的第 4 位设置为 1。否则,它清除(设置为 0)PORTB 寄存器的第 4 位。
如果你不明白为什么会这样,我建议你在C书籍或C教程中查找以下C按位逻辑运算符的定义:&
|
, <<
, |=
, &=
.
描述代码的一种更简单的方法是将值从 PINB 的第 3 位复制到 PORTB 的第 4 位。
现在要更好地理解代码的作用,您需要找到 ATtiny45 数据表 并阅读 PINB 和 PORTB 寄存器的定义。然后你会发现:
读取 PINB 的第 3 位就是对 PB3 进行 数字读取 的方式。它不使用 ADC,也没有您想象的超精确阈值。阈值在数据表或类似的“电气规范”部分的某处定义。
由于在您的程序中 DDRB 的第 4 位为 1,因此 PB4 配置为 数字输出。因此,向 PORTB 的第 4 位写入一个值就是我们设置 PB4 输出值的方式。向其写入 0 使其变为低电平 (0 V),向其写入 1 使其变为高电平。
总而言之,这是一些非常基本的 AVR 代码,可以执行数字读取并将该值写入数字输出。
我正在尝试使用 Github 和 运行 中的一些先前开发的软件来完成一些有趣的软件编码。
他在大多数 PortB 引脚上使用带有数字 I/O 的 Atmel ATtiny45,但 PB3 用作来自外部电位器的模拟 (AtoD) 输入。 在他的代码中,他有以下片段:
if(PINB & MANCLK){
PORTB |= CLKOUT;
} else {
PORTB &= ~CLKOUT;
}
}
(注:'CLKOUT'为数字输出引脚。)
“MANCLK”是 PB3 的模拟输入引脚,所以,“if(PINB & MANCLK)...”行是做什么的? 它实际上是否考虑了该引脚上的所有模拟值,因此,低于 2.5V 的任何值都被视为逻辑低电平,高于 2.5V 的任何值都被视为高电平,或者,这甚至是一个有效的陈述吗? 'MANCLK' 是简单的“0”还是“1”? 不确定在哪里可以找到关于这种特殊情况的有用信息。
感谢您提供的任何帮助。
此致, G运行t
[更新] 这是代码....
`
#include <stdio.h>
#include <stdlib.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#if !defined(TIMSK)
#define TIMSK TIMSK0
#endif
#if !defined(TIMER0_COMPA_vect)
#define TIMER0_COMPA_vect TIM0_COMPA_vect
#endif
// 100 us per tick
#define COUNT 199
#define TPSC (1<<CS01)
#define AUTO (1<<PB0) //Pin5, MOSI and EN (via RN33 Pins 4&5).
High = Enable
#define RUN (1<<PB1) //Pin6, MISO and ~HLT (via RN33 Pins 3&6).
Low = Halt
#define MANCLK (1<<PB3) //Pin2, Analog input, Manual Clock Speed
Setting (via potentiometer RV1)
#define CLKOUT (1<<PB4) //Pin3, CLK output
#define AUTOINT (1<<PCINT0)
#define RUNINT (1<<PCINT1)
#define CLKINT (1<<PCINT2)
#define MANDLY 500
volatile uint16_t count;
volatile uint8_t trigger;
volatile uint8_t manual;
uint16_t maxcnt;
void stopTimer(void) {
PORTB &= ~CLKOUT; // Clock output low
TCCR0B &= ~TPSC;
TCNT0;
count = 0;
trigger = 0;
}
void startTimer(void) {
PCMSK = 0;
TCCR0B |= TPSC;
}
void halt(void) {
cli();
stopTimer();
PCMSK = RUNINT|AUTOINT;
sei();
sleep_enable();
while(!(PINB & RUN)) sleep_cpu();
sleep_disable();
cli();
startTimer();
sei();
}
void manclk(void) {
cli();
stopTimer();
PCMSK = CLKINT|RUNINT|AUTOINT;
sei();
while(!(PINB & AUTO)) {
if(PINB & RUN) {
if(PINB & MANCLK) {
PORTB |= CLKOUT;
} else {
PORTB &= ~CLKOUT;
}
}
sleep_enable();
sleep_cpu();
sleep_disable();
}
cli();
startTimer();
sei();
}
int main(void)
{
DDRB = CLKOUT;
PORTB &= ~CLKOUT;
cli();
TCCR0A = 0;
TCCR0B = 0;
TCNT0 = 0;
TCCR0A |= (1 << WGM01);
OCR0A = COUNT; // Interrupts every 100us when Timer running
TCCR0B |= (1 << CS01); // Timer Start /8 pre-scaler
TIMSK |= (1 << OCIE0A);
GIMSK |= (1 << PCIE);
sei();
// Setup the ADC
//ADMUX |= (1 << MUX1); // Only using ADC2 (original)
ADMUX |= (1 << MUX0) | (1 << MUX1); //new code - MUX Select ADC3 @
PB3
ADCSRA |= (1 << ADEN);
ADCSRA |= (1 << ADPS2) | (1 << ADPS1); // 125 kHz ADC clock
//Enable Sleeping
set_sleep_mode(SLEEP_MODE_IDLE);
while (1) {
//if RUN = HLT = Low ==> HALT
if (!(PINB & RUN)) {
halt();
continue;
}
//if AUTO = ENABLE = Low ==> manclk...if AUTO (ENABLE) == LOW ==>
MANCLK
if (!(PINB & AUTO)) {
manclk();
continue;
}
ADCSRA |= (1 << ADSC);
while (ADCSRA & (1 << ADSC));
maxcnt = 5000 / (64 - ADC/16);
if (trigger) {#
PORTB ^= CLKOUT;
trigger = 0;
}
sleep_enable();
sleep_cpu();
sleep_disable();
}
return 0; // never reached
}
ISR(TIMER0_COMPA_vect) {
count++;
if (count >= maxcnt) {
count = 0;
trigger = 1;
}
}
EMPTY_INTERRUPT(PCINT0_vect);`
如果我们扩展一些预处理器宏,您的代码将变得更容易解释:
if (PINB & (1 << 3))
{
PORTB |= (1 << 4);
}
else
{
PORTB &= ~(1 << 4);
}
现在只要看看上面的代码,我们就可以知道它做了以下事情:它读取 PINB 寄存器的第 3 位,如果它是 1,它将 PORTB 寄存器的第 4 位设置为 1。否则,它清除(设置为 0)PORTB 寄存器的第 4 位。
如果你不明白为什么会这样,我建议你在C书籍或C教程中查找以下C按位逻辑运算符的定义:&
|
, <<
, |=
, &=
.
描述代码的一种更简单的方法是将值从 PINB 的第 3 位复制到 PORTB 的第 4 位。
现在要更好地理解代码的作用,您需要找到 ATtiny45 数据表 并阅读 PINB 和 PORTB 寄存器的定义。然后你会发现:
读取 PINB 的第 3 位就是对 PB3 进行 数字读取 的方式。它不使用 ADC,也没有您想象的超精确阈值。阈值在数据表或类似的“电气规范”部分的某处定义。
由于在您的程序中 DDRB 的第 4 位为 1,因此 PB4 配置为 数字输出。因此,向 PORTB 的第 4 位写入一个值就是我们设置 PB4 输出值的方式。向其写入 0 使其变为低电平 (0 V),向其写入 1 使其变为高电平。
总而言之,这是一些非常基本的 AVR 代码,可以执行数字读取并将该值写入数字输出。