【问题标题】:AVR I/O: DIgital vs Analog Input ProgrammingAVR I/O:数字与模拟输入编程
【发布时间】:2021-11-20 20:08:12
【问题描述】:

我正在尝试使用一些以前从 Github 开发的软件,并遇到了一些有趣的软件编码。

他在大多数 PortB 引脚上使用带有数字 I/O 的 Atmel ATtiny45,但 PB3 用作来自外部电位器的模拟 (AtoD) 输入。 在他的代码中,他有以下 sn-p:

  if(PINB & MANCLK){
       PORTB |= CLKOUT;
       } else {
       PORTB &= ~CLKOUT;
       }
    }

(注意:“CLKOUT”是数字输出引脚。)

“MANCLK”是 PB3 的模拟输入引脚,那么,“if(PINB & MANCLK)...”这行有什么作用? 它实际上是否考虑了该引脚上的所有模拟值,因此,低于 2.5V 的任何东西都被认为是逻辑低电平,而超过 2.5V 的任何东西都被认为是高电平,或者这甚至是一个有效的陈述吗? 'MANCLK' 只是被视为“0”还是“1”? 不知道在哪里可以找到有关这种特殊情况的有用信息。

感谢您提供的任何帮助。

问候, 授予

[更新] 这是代码....

`

 #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);`

【问题讨论】:

  • PINBPORTBavr/io.h 中定义,但您能否发布定义 MANCLKCLKOUT 的实际代码行,以便我们弄清楚代码是什么做? (你说“MANCLK”是“PB3”,但这并没有让我充满信心;我宁愿看代码。)
  • 嗨...不知道如何在我的代码中添加...我尝试过但一直收到“代码格式不正确”的错误。
  • 我在下面发布了详细的答案。如果它令人满意地回答了您的问题,您介意单击绿色复选标记以接受它作为答案吗?

标签: avr atmel


【解决方案1】:

如果我们扩展一些预处理器宏,您的代码会变得更容易解释:

if (PINB & (1 << 3))
{
  PORTB |= (1 << 4);
}
else
{
  PORTB &= ~(1 << 4);
}

现在只要看看上面的代码,我们就可以知道它做了以下事情:它读取 PINB 寄存器的第 3 位,如果为 1,则将 PORTB 寄存器的第 4 位设置为 1。否则,它清除(设置到 0) PORTB 寄存器的第 4 位。

如果你不明白为什么会这样,我建议你在 C 书籍或 C 教程中查找以下 C 位逻辑运算符的定义:&amp;|&lt;&lt;、@987654325 @,&amp;=

描述代码的一种更简单的方法是将值从 PINB 的第 3 位复制到 PORTB 的第 4 位。

现在要更好地了解代码的作用,您需要找到 ATtiny45 数据表并阅读 PINB 和 PORTB 寄存器的定义。然后你会发现以下内容:

  1. 读取 PINB 的第 3 位是您对 PB3 进行数字读取的方法。它不使用 ADC,也没有您想象的超精确阈值。阈值在数据表的“电气规格”部分或类似部分中的某处定义。

  2. 由于您的程序中 DDRB 的第 4 位为 1,因此 PB4 被配置为 数字输出。因此,将值写入 PORTB 的第 4 位就是我们设置 PB4 的输出值的方式。向其写入 0 使其驱动为低电平 (0 V),向其写入 1 使其驱动为高电平。

总之,这是一些非常基本的 AVR 代码,它执行数字读取并将该值写入数字输出。

【讨论】:

    猜你喜欢
    • 2015-06-08
    • 1970-01-01
    • 2012-12-19
    • 2015-05-24
    • 2021-11-25
    • 1970-01-01
    • 2013-09-27
    • 2011-12-15
    • 2017-05-17
    相关资源
    最近更新 更多