Problem bei der Ausgabe des 2,5-kHz-Signals ATTiny85 mit Digispark

Ich arbeite relativ neu mit der Digispark-IDE und habe einige Probleme mit etwas, das wirklich einfach sein sollte.

Ich versuche, ein 2,5-kHz-Rechtecksignal auszugeben, das mit +/- 500 Hz oszilliert. Es geht also von 2000 Hz bis 3000 Hz von einem Mikrocontroller, der einen ATTiny85 mit Digisparks IDE verwendet.

Ich habe dieses Tutorial online gefunden: https://digistump.com/wiki/digispark/tricks , das erklärt, was ich tun möchte. Wie sie sagten, um die HW-PWM-Frequenz zu ändern, habe ich den Wert von 64 auf 8 des MS_TIMER_TICK_EVERY_X_CYCLES innerhalb von wiring.c geändert

Aus diesem Grund gelang es mir, einen höheren Frequenzwert zu erhalten. Aber ich konnte es nicht tunen, deshalb habe ich mit der Software-PWM-Manipulation fortgefahren

Ich habe diesen Code verwendet:

#include <TinySoftPwm.h>
#include <DigiUSB.h>

#define HW_PWM_PIN               0 /* Used to check HW PWM with analogWrite() */
#define SW_PWM_BUILT_IN_LED_PIN  1 /* Digispark Model A (Rev2) built-in LED pin number (Change it to 2 for Model B) */
#define TIME_TEST_PIN            5 /* Used to check with oscilloscope micros(), millis() are still OK */

void setup()
{
   TinySoftPwm_begin(128, 0); /* 128 x TinySoftPwm_process() calls before overlap (Frequency tuning), 0 = PWM init for all declared pins */
   pinMode(TIME_TEST_PIN, OUTPUT);
   DigiUSB.begin(); 
}

void loop()
{
static uint32_t PwmStartUs=micros();
static uint32_t PwmStartMs=millis();
static uint8_t  Pwm=0;
static int8_t   Dir=1;
static boolean  State=LOW;
static uint32_t BlinkStartMs=millis();

  /***********************************************************/
  /* Call TinySoftPwm_process() with a period of 60 us       */
  /* The PWM frequency = 128 x 60 # 7.7 ms -> F # 130Hz      */
  /* 128 is the first argument passed to TinySoftPwm_begin() */
  /***********************************************************/
  if((micros() - PwmStartUs) >= 60)
  {
    /* We arrive here every 60 microseconds */
    PwmStartUs=micros();
    TinySoftPwm_process(); /* This function shall be called periodically (like here, based on micros(), or in a timer ISR) */
  }

  /*************************************************************/
  /* Increment/decrement PWM on LED Pin with a period of 10 ms */
  /*************************************************************/
  if((millis()-PwmStartMs) >= 10)
  {
    /* We arrived here every 10 milliseconds */
    PwmStartMs=millis();
    Pwm+=Dir; /* increment or decrement PWM depending of sign of Dir */
    TinySoftPwm_analogWrite(SW_PWM_BUILT_IN_LED_PIN, Pwm); /* Software PWM: Update built-in LED for Digispark */
    analogWrite(HW_PWM_PIN, Pwm); /* Copy Pwm duty cycle to Hardware PWM */
    if(Pwm==255) Dir=-1; /* if PWM reaches the maximum: change direction */
    if(Pwm==0)   Dir=+1; /* if PWM reaches the minimum: change direction */
  }

  /* Blink half period = 5 ms */
  if(millis()-BlinkStartMs>=5)
  {
    BlinkStartMs=millis();
    digitalWrite(TIME_TEST_PIN, State);
    State=!State;
  }

  /* Check USB is still working */
  if(DigiUSB.available()) /* Just hit "Enter" in the digiterm to check USB */
  {
    DigiUSB.read(); /* just to clear the Rx buffer */
    DigiUSB.println(F("DigiUSB is still alive!"));
  }
  DigiUSB.refresh();

}

Egal was ich mache, ich lese einen oszillierenden Wert von 4,065 kHz - 4,049 kHz.

Was mache ich falsch? Ist mein Oszilloskop kaputt? Soll ich das anders machen?

FYI an andere EE.SE-Mitglieder: Diese Frage ist ein Copy & Paste aus den Digistump-Foren (Link) , und obwohl sie dort noch nicht beantwortet wurde, kann die Frage des OP natürlich jederzeit durch eine Antwort dort gelöst werden .

Antworten (1)

Sie können dies auf Timer 0 im CTC-Modus tun. Wenn Ihre MCU mit 8 MHz läuft, benötigen Sie eine Prescaler-Einstellung von 8, um den gewünschten Frequenzbereich zu erreichen. Durch Umkehren der Häufigkeitsformel aus dem Datenblatt erhalten Sie die korrekten Zählwerte für den Überlauf. Wir tun dies separat in unserer Lieblingssprache (meine ist Haskell):

module Main (main) where

f_cpu = 8e6
freq n c = f_cpu / (2 * n * (1 + c))
count n f = f_cpu / (f * 2 * n) - 1

main :: IO ()
main = mapM_ (print . round . count 8) [ 2e3, 2.5e3, 3e3 ]

was uns 249, 199 bzw. 166 gibt. Wir sind jetzt bereit, das Programm zu schreiben:

#include <avr/io.h>
#include <util/delay.h>

void setup()
{
    TCCR0A = _BV(WGM01) | _BV(COM0A0);  // CTC mode w toggle channel A on compare match
    TCCR0B = _BV(CS01);                 // clock select prescale = 8
    DDRB |= _BV(0);                     // enable channel A = OC0A (PB0) output
}

void loop()
{
    static uint8_t i = 0;

    switch (++i & 0x3)
    {
        case 0: OCR0A = 249; break;     // 2kHz
        case 1: OCR0A = 199; break;     // 2.5kHz
        case 2: OCR0A = 166; break;     // 3kHz
        case 3: OCR0A = 199; break;     // 2.5kHz
    }

    _delay_ms(2000);
}

int main()
{
    setup();
    for (;;)
        loop();
}

Dadurch werden die Frequenzen 2, 2,5 und 3 kHz in 500-Hz-Schritten jeweils 2 Sekunden lang durchlaufen. Ich habe auf einem nackten ATTiny85 mit 8 MHz getestet und es auf meinem Oszilloskop gemessen: 2,01 kHz, 2,52 kHz und 3,01 kHz sind das, was ich bekomme.

Wenn Sie Timer 0 nicht verwenden können, sollte es einfach sein, dasselbe mit Timer 1 zu erreichen (er hat mehr Vorskalierungsoptionen und einen größeren Bereich).