Themabewertung:
  • 0 Bewertung(en) - 0 im Durchschnitt
  • 1
  • 2
  • 3
  • 4
  • 5
Servosteuerung mit attiny85
#1
Der Beitrag ist vielleicht etwas offtopic, aber ich denke wenn ein Radio mit µPC-Technik bedient werden soll kann man auch sowas mal in einem Radioforum zeigen.

Die Aufgabe: Messen der Abstimmspannung und darstellen mittels der Position einer servogetriebenen Skalenscheibe.

In Analogtechnik wäre der Aufwand bestimmt geringer, aber es soll hauptsächlich eine Übung sein.

Mit einem Arduino hatte ich es schon mal realisiert. Dieser ist aber nur um eine Skalenscheibe zu drehen überqualifiziert. Das Umsetzen auf einen attiny war für einen Anfänger wie mich eine Herausforderung. Allein schon das Einrichten der Entwicklungsumgebung musste ich mir mühsam mithilfe englischer tutorials aneignen.

Ich benutze die Arduino-IDE 1.8.2 mit den digistump driver. Verschiedene Einstellungen waren notwendig:

- Die Angabe einer boardverwalter-URL (http://digistump.com/package_digistump_index.json)
- Auswahl des Board (Digispark (Default - 16,5mHz))
- Auswahl der Programmer-ISP (USBtinyISP)

Die "Servo.h" -Bibliothek des arduino funktioniert beim attiny wegen den timer-Interrupts nicht. Ich benutze stattdessen zur Ausgabe des PWM-Signals die Bibliothek "SoftRcPulseOut.h"

So sieht der Quellcode aus:

Zitat:#include <SoftRcPulseOut.h>

SoftRcPulseOut servo;
int pos = 0;
#define REFRESH_PERIOD_MS 20
#define NOW               1

// the setup routine runs once when you press reset:
void setup() {                
 // initialize the digital pin as an output.
 pinMode(1, OUTPUT); // servo pulse and green LED
 pinMode(2, INPUT); // potmeter
 servo.attach(1);
 servo.setMaximumPulse(2200);  
}

// the loop routine runs over and over again forever:
void loop() {
   int val = analogRead(1); // 1 means pin 2
   pos = map(val, 0, 1023, 0, 180);
   servo.write(pos);
   SoftRcPulseOut::refresh(NOW);
   delay(15);                           // waits for the servo to get there
 
 }

Entgegen dem Arduino wird das attiny-Entwicklungsboard erst nach der Kompilierung auf Aufforderung mit USB verbunden.

Der Testaufbau:

   

   

   

Zitieren
#2
Eine schöne Bastelei mit Lerneffekt! Smiley20

Solche Ausflüge in die Digitaltechnik brauche ich auch, das lockert auf.

Gruß
Wilhelm
Niemandes Herr, Niemandes Knecht,
so ist es gut, so ist es recht

von Fallersleben
Zitieren
#3
interessantes Projekt, für mich leider zu hoch. Immerhin dieses Ergebnis hatte ich auch schon 1961 mithilfe eines Potentiometers, einer Kreuzschaltung und entsprechendem Kreuzspul-Instrument erzielt. Damit wurde analog die Stellung eines Dampfschiebers von Zu bis Auf angezeigt.
Gruß Franz
Zitieren
#4
für dieses Ergebnis hätte auch ein Haushaltsgummi genügt :-) Es geht hier wirklich nur um die Einarbeitung in die µPC-Technik.
Zitieren
#5
Hallo Jupp,

habe (noch) nicht ganz verstanden, wie Dein Projekt funktioniert….
Vor allen Dingen, wenn die Servo-Bibliothek eingebunden ist, warum steht dann ein delay(15) in der Routine?
Oder arbeitet die Library auch nicht mit Interrupts?

Aber egal, der Link zur Bibliothek funktioniert (bei mir) nicht.

Es sollte folgender Links ein:
https://raw.githubusercontent.com/digist...index.json

Oder in Deinem Link indes auf index ändern.
Grüße aus Wassenberg,
Norbert.
Zitieren
#6
(04.06.2017, 16:24)norbert_w schrieb: Hallo Jupp,

habe (noch) nicht ganz verstanden, wie Dein Projekt funktioniert….
Vor allen Dingen, wenn die Servo-Bibliothek eingebunden ist, warum steht dann ein delay(15) in der Routine?
Oder arbeitet die Library auch nicht mit Interrupts?

Aber egal, der Link zur Bibliothek funktioniert (bei mir) nicht.

Es sollte folgender Links ein:
https://raw.githubusercontent.com/digist...index.json

Oder in Deinem Link indes auf index ändern.

Hallo Norbert,
hast recht, ich hatte mich vertippt. Der link ist korrigiert, danke! Die Infos stammen u.a. vom Digispark Wiki:

Connecting and Programming Your Digispark

das delay hab ich eingebaut weil der Servo zu schnell getaktet wurde. Er war ruppig und man hat ihn schwingen hören. Mit dem delay läuft er ruhig und leise.
Zitieren
#7
Hallo Jupp,

habe gerade auf der Digispark WebSite folgendes gelesen:

Code:
Synchronization:

By giving 1 or true as optional argument for the SoftRcPulseOut::refresh() method, the pulses are refreshed immediately (without waiting for the usual 20ms).

the SoftRcPulseOut::refresh() method returns 1 or true when the pulses have been refreshed. Testing this return value provides a 20ms timer.

So wie ich das verstehe, bewirkt ein Funktionsaufruf ohne Parameter 
 SoftRcPulseOut::refresh();
eine Verzögerungszeit von 20ms, während ein Aufruf
 SoftRcPulseOut::refresh(NOW); 
ohne Verzögerung arbeitet.

Vielleicht kannst Du das mal testen??

Von Arduino und Konsorten kenne ich so gut wie gar Nichts.
Ich programmier AVRs (privat) nur mit einem Editor, und dem AVR-Crosspack (GCC und AVRdude).
Grüße aus Wassenberg,
Norbert.
Zitieren
#8
(04.06.2017, 21:49)norbert_w schrieb: Von Arduino und Konsorten kenne ich so gut wie gar Nichts.
Ich programmier AVRs (privat) nur mit einem Editor, und dem AVR-Crosspack (GCC und AVRdude).

Hallo Norbert, 

also kennst Du dich mit Arduino doch sehr gut aus. Arduinos sind kleine AVR-Boards und die Arduino-Entwicklungsumgebung ist ein besserer Texteditor für den GCC der AVRs.


Hallo Jupp,

bin begeistert das Du jetzt auch mit den Microcontrollern loslegst, da werden ganz schnell tolle Projektideen von dir kommen!
Vielleicht ein UKW-/DAB-Empfänger als Zwischensockel von Mischerröhren? Frequenzmessung des AM-Oszillators mit einem AVR, dann die Einstellung eines UKW-Einchipradios proportional zur MW-Oszillatorfrequenz und Ausgabe der UKW-NF amplitudenmoduliert auf die ZF-Frequenz des alten Radios.
Ansprechpartner für Umbau oder Modernisierung von Röhrenradios mittels SDR,DAB+,Internetradio,Firmwareentwicklung. 
Unser Open-Source Softwarebaukasten für Internetradios gibt es auf der Github-Seite! Projekt: BM45/iRadio (Google "github BM45/iRadio")
Zitieren
#9
@Bernhard
ich denke bei Arduino zuerst an die Programmieroberfläche (IDE) - die kenne ich wirklich überhaupt nicht.
Von AVRs kenne ich ein wenig mehr.

@ Jupp,
mich erstaunt immer wieder wie Du Wege suchst die alte und neue Technik zu verbinden.
Das ist einfach Klasse.
Das aller Anfang schwer ist, tja das kann man leider nicht ändern.

Dranbleiben!
Grüße aus Wassenberg,
Norbert.
Zitieren
#10
(05.06.2017, 09:30)norbert_w schrieb: @ Jupp,
..mich erstaunt immer wieder wie Du Wege suchst die alte und neue Technik zu verbinden.
Das ist einfach Klasse.
Das aller Anfang schwer ist, tja das kann man leider nicht ändern.

Dranbleiben!

danke Norbert! Genau das ist meine Intuition.

Im Moment bin ich aber erst mal froh eine LED zum leuchten und einen Motor zum drehen gebracht zu haben.

Bernhard, diese Ziele zu erreichen traue ich dir eher zu als mir.
Zitieren
#11
Nicht so bescheiden Jupp! Du kannst ja jetzt schon ein Servo mit einem Potentiometer drehen. Stell Dir vor am Servo hängt das Abstimmpotentiometer eines TDA7088 oder des KEMO-Moduls. Alles was Du jetzt noch brauchst ist die Frequenzmessung. Die bekommst Du hiermit. Hat der Tiny einen Timereingang?

Code:
#ifndef FreqCount_h
#define FreqCount_h

#include <Arduino.h>

class FreqCountClass {
public:
 static void begin(uint16_t msec);
 static uint8_t available(void);
 static uint32_t read(void);
 static void end(void);
};

extern FreqCountClass FreqCount;

#endif


#ifndef FreqCount_timers_h_
#define FreqCount_timers_h_

// Arduino Mega
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
 // #define COUNTER_USE_TIMER1 // T1 is not connected
 // #define COUNTER_USE_TIMER3 // T3 is not connected
 // #define COUNTER_USE_TIMER4 // T4 is not connected
 #define COUNTER_USE_TIMER5    // T5 is pin 47
 #define TIMER_USE_TIMER2

// Teensy 3.0 & 3.1 & LC
#elif defined(__MK20DX128__) || defined(__MK20DX256__) || defined(__MKL26Z64__) || defined(__MK64FX512__) || defined(__MK66FX1M0__)
 #define COUNTER_USE_LPTMR     // LPTMR is pin 13  (has LED connected)
 #define TIMER_USE_INTERVALTIMER

// Teensy 2.0
#elif defined(__AVR_ATmega32U4__)
 #define COUNTER_USE_TIMER1    // T1 is pin 11  (has LED connected)
 #define TIMER_USE_TIMER4H

// Teensy++ 1.0 & 2.0
#elif defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB1286__)
 #define COUNTER_USE_TIMER1    // T1 is pin 6   (has LED connected)
 //#define COUNTER_USE_TIMER3  // T3 is pin 13
 #define TIMER_USE_TIMER2

// Sanguino
#elif defined(__AVR_ATmega644P__) || defined(__AVR_ATmega644__)
 #define COUNTER_USE_TIMER1    // T1 is pin 1
 #define TIMER_USE_TIMER2

// Arduino Duemilanove, Diecimila, LilyPad, Mini, Fio, etc
#elif defined(__AVR_ATmega328P__) || defined(__AVR_ATmega168__)
 #define COUNTER_USE_TIMER1    // T1 is pin 5
 #define TIMER_USE_TIMER2

#else
 #error "Unknown chip, please edit me with timer+counter definitions"

#endif


/**********************************************/
/*   Counter Hardware Abstraction             */
/**********************************************/

#if defined(COUNTER_USE_LPTMR) // 16 bit LPTMR on Freescale Kinetis

static inline void counter_init(void)
{
 SIM_SCGC5 |= SIM_SCGC5_LPTIMER;
 LPTMR0_CSR = 0;
 LPTMR0_PSR = 0b00000100; // bypass prescaler/filter
 LPTMR0_CMR = 0xFFFF;
 LPTMR0_CSR = 0b00100110; // counter, input=alt2, free running mode
 CORE_PIN13_CONFIG = PORT_PCR_MUX(3);
}

static inline void counter_start(void)
{
 LPTMR0_CSR = 0b00100111; // enable
}

static inline void counter_shutdown(void)
{
 LPTMR0_CSR = 0;
}

static inline uint16_t counter_read(void)
{
 LPTMR0_CNR = 0; // writing cause sync with hardware
 return LPTMR0_CNR;
}

static inline uint8_t counter_overflow(void)
{
 return (LPTMR0_CSR & 0x80) ? 1 : 0;
}

static inline void counter_overflow_reset(void)
{
 LPTMR0_CSR = 0b10100111;
}




#elif defined(COUNTER_USE_TIMER1) // 16 bit Timer 1 on Atmel AVR

static uint8_t saveTCCR1A, saveTCCR1B;

static inline void counter_init(void)
{
 saveTCCR1A = TCCR1A;
 saveTCCR1B = TCCR1B;
 TCCR1B = 0;
 TCCR1A = 0;
 TCNT1 = 0;
 TIFR1 = (1<<TOV1);
 TIMSK1 = 0;
}

static inline void counter_start(void)
{
 TCCR1B = (1<<CS12) | (1<<CS11) | (1<<CS10);
}

static inline void counter_shutdown(void)
{
 TCCR1B = 0;
 TCCR1A = saveTCCR1A;
 TCCR1B = saveTCCR1B;
}

static inline uint16_t counter_read(void)
{
 return TCNT1;
}

static inline uint8_t counter_overflow(void)
{
 return TIFR1 & (1<<TOV1);
}

static inline void counter_overflow_reset(void)
{
 TIFR1 = (1<<TOV1);
}



#elif defined(COUNTER_USE_TIMER3) // 16 bit Timer 3 on Atmel AVR

static uint8_t saveTCCR3A, saveTCCR3B;

static inline void counter_init(void)
{
 saveTCCR3A = TCCR3A;
 saveTCCR3B = TCCR3B;
 TCCR3B = 0;
 TCCR3A = 0;
 TCNT3 = 0;
 TIFR3 = (1<<TOV3);
 TIMSK3 = 0;
}

static inline void counter_start(void)
{
 TCCR3B = (1<<CS32) | (1<<CS31) | (1<<CS30);
}

static inline void counter_shutdown(void)
{
 TCCR3B = 0;
 TCCR3A = saveTCCR3A;
 TCCR3B = saveTCCR3B;
}

static inline uint16_t counter_read(void)
{
 return TCNT3;
}

static inline uint8_t counter_overflow(void)
{
 return TIFR3 & (1<<TOV3);
}

static inline void counter_overflow_reset(void)
{
 TIFR3 = (1<<TOV3);
}


#elif defined(COUNTER_USE_TIMER4) // 16 bit Timer 4 on Atmel AVR

static uint8_t saveTCCR4A, saveTCCR4B;

static inline void counter_init(void)
{
 saveTCCR4A = TCCR4A;
 saveTCCR4B = TCCR4B;
 TCCR4B = 0;
 TCCR4A = 0;
 TCNT4 = 0;
 TIFR4 = (1<<TOV4);
 TIMSK4 = 0;
}

static inline void counter_start(void)
{
 TCCR4B = (1<<CS42) | (1<<CS41) | (1<<CS40);
}

static inline void counter_shutdown(void)
{
 TCCR4B = 0;
 TCCR4A = saveTCCR4A;
 TCCR4B = saveTCCR4B;
}

static inline uint16_t counter_read(void)
{
 return TCNT4;
}

static inline uint8_t counter_overflow(void)
{
 return TIFR4 & (1<<TOV4);
}

static inline void counter_overflow_reset(void)
{
 TIFR4 = (1<<TOV4);
}


#elif defined(COUNTER_USE_TIMER5) // 16 bit Timer 5 on Atmel AVR

static uint8_t saveTCCR5A, saveTCCR5B;

static inline void counter_init(void)
{
 saveTCCR5A = TCCR5A;
 saveTCCR5B = TCCR5B;
 TCCR5B = 0;
 TCCR5A = 0;
 TCNT5 = 0;
 TIFR5 = (1<<TOV5);
 TIMSK5 = 0;
}

static inline void counter_start(void)
{
 TCCR5B = (1<<CS52) | (1<<CS51) | (1<<CS50);
}

static inline void counter_shutdown(void)
{
 TCCR5B = 0;
 TCCR5A = saveTCCR5A;
 TCCR5B = saveTCCR5B;
}

static inline uint16_t counter_read(void)
{
 return TCNT5;
}

static inline uint8_t counter_overflow(void)
{
 return TIFR5 & (1<<TOV5);
}

static inline void counter_overflow_reset(void)
{
 TIFR5 = (1<<TOV5);
}


#endif // COUNTER_USE_***



/**********************************************/
/*   Timer Hardware Abstraction               */
/**********************************************/

#if defined(TIMER_USE_INTERVALTIMER) // Teensyduino IntervalTimer

static IntervalTimer itimer;
static void timer_interrupt(void);

static inline uint16_t timer_init(uint16_t msec)
{
 return msec;
}

static inline void timer_start(void)
{
 // IntervalTimer is capable of longer intervals, but
 // LPTMR can overflow.  Limiting to 1 ms allows counting
 // up to 65.535 MHz... LPTMR on Teensy 3.1 can do 65 MHz!
 itimer.begin(timer_interrupt, 1000);
}

static inline void timer_shutdown(void)
{
 itimer.end();
}

#define TIMER_ISR_VECTOR timer_interrupt
#ifdef ISR
#undef ISR
#endif
#define ISR(name) void name (void)


#elif defined(TIMER_USE_TIMER2) // 8 bit Timer 2 on Atmel AVR

/*        1ms       2ms       4ms       8ms
16 MHz    128x125   256x125   256x250   1024x125
12 MHz    64x188    128x188   256x188 1024x94     //Not exact ms values: 2% error
8 MHz     64x125    128x125   256x125   256x250
4 MHz     32x125    64x125    128x125   256x125
2 MHz     8x250     32x125    64x125    128x125
1 MHz     8x125     8x250     32x125    64x125
*/
#if F_CPU == 16000000L
 #define TIMER2_OCR2A_1MS_VAL  124         // div 125
 #define TIMER2_TCCR2B_1MS_VAL             (1<<CS22) | (1<<CS20) // div 128
 #define TIMER2_OCR2A_2MS_VAL  124         // div 125
 #define TIMER2_TCCR2B_2MS_VAL (1<<CS22) | (1<<CS21)     // div 256
 #define TIMER2_OCR2A_4MS_VAL  249         // div 250
 #define TIMER2_TCCR2B_4MS_VAL (1<<CS22) | (1<<CS21)     // div 256
 #define TIMER2_OCR2A_8MS_VAL  124         // div 125
 #define TIMER2_TCCR2B_8MS_VAL (1<<CS22) | (1<<CS21) | (1<<CS20) // div 1024
#elif F_CPU == 12000000L
 #define TIMER2_OCR2A_1MS_VAL  187         // div 188
 #define TIMER2_TCCR2B_1MS_VAL (1<<CS22)       // div 64
 #define TIMER2_OCR2A_2MS_VAL  187         // div 125
 #define TIMER2_TCCR2B_2MS_VAL (1<<CS22) |             (1<<CS20) // div 128
 #define TIMER2_OCR2A_4MS_VAL  187         // div 250
 #define TIMER2_TCCR2B_4MS_VAL (1<<CS22) | (1<<CS21)     // div 256
 #define TIMER2_OCR2A_8MS_VAL  93          // div 125
 #define TIMER2_TCCR2B_8MS_VAL (1<<CS22) | (1<<CS21) | (1<<CS20) // div 1024
#elif F_CPU == 8000000L
 #define TIMER2_OCR2A_1MS_VAL  124         // div 125
 #define TIMER2_TCCR2B_1MS_VAL (1<<CS22)       // div 64
 #define TIMER2_OCR2A_2MS_VAL  124         // div 125
 #define TIMER2_TCCR2B_2MS_VAL (1<<CS22) |             (1<<CS20) // div 128
 #define TIMER2_OCR2A_4MS_VAL  124         // div 125
 #define TIMER2_TCCR2B_4MS_VAL (1<<CS22) | (1<<CS21)     // div 256
 #define TIMER2_OCR2A_8MS_VAL  249         // div 250
 #define TIMER2_TCCR2B_8MS_VAL (1<<CS22) | (1<<CS21)     // div 256
#elif F_CPU == 4000000L
 #define TIMER2_OCR2A_1MS_VAL  124         // div 125
 #define TIMER2_TCCR2B_1MS_VAL             (1<<CS21) | (1<<CS20) // div 32
 #define TIMER2_OCR2A_2MS_VAL  124         // div 125
 #define TIMER2_TCCR2B_2MS_VAL (1<<CS22)       // div 64
 #define TIMER2_OCR2A_4MS_VAL  124         // div 125
 #define TIMER2_TCCR2B_4MS_VAL (1<<CS22) |             (1<<CS20) // div 128
 #define TIMER2_OCR2A_8MS_VAL  124         // div 125
 #define TIMER2_TCCR2B_8MS_VAL (1<<CS22) | (1<<CS21)     // div 256
#elif F_CPU == 2000000L
 #define TIMER2_OCR2A_1MS_VAL  249         // div 250
 #define TIMER2_TCCR2B_1MS_VAL             (1<<CS21)     // div 8
 #define TIMER2_OCR2A_2MS_VAL  124         // div 125
 #define TIMER2_TCCR2B_2MS_VAL             (1<<CS21) | (1<<CS20) // div 32
 #define TIMER2_OCR2A_4MS_VAL  124         // div 125
 #define TIMER2_TCCR2B_4MS_VAL (1<<CS22)       // div 64
 #define TIMER2_OCR2A_8MS_VAL  124         // div 125
 #define TIMER2_TCCR2B_8MS_VAL (1<<CS22) | (1<<CS21)     // div 128
#elif F_CPU == 1000000L
 #define TIMER2_OCR2A_1MS_VAL  124         // div 125
 #define TIMER2_TCCR2B_1MS_VAL             (1<<CS21)     // div 8
 #define TIMER2_OCR2A_2MS_VAL  249         // div 250
 #define TIMER2_TCCR2B_2MS_VAL             (1<<CS21)     // div 8
 #define TIMER2_OCR2A_4MS_VAL  124         // div 125
 #define TIMER2_TCCR2B_4MS_VAL             (1<<CS21) | (1<<CS20) // div 32
 #define TIMER2_OCR2A_8MS_VAL  124         // div 125
 #define TIMER2_TCCR2B_8MS_VAL (1<<CS22)       // div 64
#else
#error "Clock must be 16, 12, 8, 4, 2 or 1 MHz"
#endif

static uint8_t saveTCCR2A, saveTCCR2B;
static uint8_t startTCCR2B;

static inline uint16_t timer_init(uint16_t msec)
{
 uint16_t gate_len;

 saveTCCR2A = TCCR2A;
 saveTCCR2B = TCCR2B;
 TCCR2B = 0;
 TCCR2A = (1<<WGM21);
 if ((msec & 7) == 0) {
   gate_len = msec >> 3;
   OCR2A = TIMER2_OCR2A_8MS_VAL;
   startTCCR2B = TIMER2_TCCR2B_8MS_VAL;
 } else if ((msec & 3) == 0) {
   gate_len = msec >> 2;
   OCR2A = TIMER2_OCR2A_4MS_VAL;
   startTCCR2B = TIMER2_TCCR2B_4MS_VAL;
 } else if ((msec & 1) == 0) {
   gate_len = msec >> 1;
   OCR2A = TIMER2_OCR2A_2MS_VAL;
   startTCCR2B = TIMER2_TCCR2B_2MS_VAL;
 } else {
   gate_len = msec;
   OCR2A = TIMER2_OCR2A_1MS_VAL;
   startTCCR2B = TIMER2_TCCR2B_1MS_VAL;
 }
 TIFR2 = (1<<OCF2A);
 TCNT2 = 0;
 return gate_len;
}

static inline void timer_start(void)
{
 GTCCR = (1<<PSRASY);
 TCCR2B = startTCCR2B;
 TIMSK2 = (1<<OCIE2A);
}

static inline void timer_shutdown(void)
{
 TCCR2B = 0;
 TIMSK2 = 0;
 TCCR2A = saveTCCR2A;
 TCCR2B = saveTCCR2B;
}

#define TIMER_ISR_VECTOR TIMER2_COMPA_vect

/*
There is a typical latency from the timer interrupt until the first
actual line of code executes.  Here is a typical compiler output of
approximately 34 cycles.  When starting, this same delay is used to
begin counting, so the first reading will not have 34 cycles of
extra measurement.  Because each measurement period gates instantly
after the previous, this approximate correction only affects the
first measurement.  If you do not define TIMER_LATENCY_CYCLES, this
extra delay is skipped (saving a tiny bit of code space), and the
only downside is a slight inaccuracy in the first measurement.
2  00000326 <__vector_13>:
2       326:       1f 92           push    r1
2       328:       0f 92           push    r0
1       32a:       0f b6           in      r0, 0x3f        ; 63
2       32c:       0f 92           push    r0
1       32e:       11 24           eor     r1, r1
2       330:       ef 92           push    r14
2       332:       ff 92           push    r15
2       334:       0f 93           push    r16
2       336:       1f 93           push    r17
2       338:       2f 93           push    r18
2       33a:       3f 93           push    r19
2       33c:       4f 93           push    r20
2       33e:       5f 93           push    r21
2       340:       8f 93           push    r24
2       342:       9f 93           push    r25
2       344:       af 93           push    r26
2       346:       bf 93           push    r27
*/
#define TIMER_LATENCY_CYCLES 34



#elif defined(TIMER_USE_TIMER4H)  // 10 bit "high speed" Timer 4 on Atmel AVR

#define TIMER4H_OCR4C_VAL   124   // always div 125
#if F_CPU == 16000000L
 #define TIMER4H_TCCR4B_1MS_VAL  (1<<CS43)         // div 128
 #define TIMER4H_TCCR4B_2MS_VAL  (1<<CS43) |                         (1<<CS40) // div 256
 #define TIMER4H_TCCR4B_4MS_VAL  (1<<CS43) |             (1<<CS41)   // div 512
 #define TIMER4H_TCCR4B_8MS_VAL  (1<<CS43) |             (1<<CS41) | (1<<CS40) // div 1024
#elif F_CPU == 8000000L
 #define TIMER4H_TCCR4B_1MS_VAL              (1<<CS42) | (1<<CS41) | (1<<CS40) // div 64
 #define TIMER4H_TCCR4B_2MS_VAL  (1<<CS43)         // div 128
 #define TIMER4H_TCCR4B_4MS_VAL  (1<<CS43) |                         (1<<CS40) // div 256
 #define TIMER4H_TCCR4B_8MS_VAL  (1<<CS43) |             (1<<CS41)   // div 512
#elif F_CPU == 4000000L
 #define TIMER4H_TCCR4B_1MS_VAL              (1<<CS42) | (1<<CS41)   // div 32
 #define TIMER4H_TCCR4B_2MS_VAL              (1<<CS42) | (1<<CS41) | (1<<CS40) // div 64
 #define TIMER4H_TCCR4B_4MS_VAL  (1<<CS43)         // div 128
 #define TIMER4H_TCCR4B_8MS_VAL  (1<<CS43) |                         (1<<CS40) // div 256
#elif F_CPU == 2000000L
 #define TIMER4H_TCCR4B_1MS_VAL              (1<<CS42) |             (1<<CS40) // div 16
 #define TIMER4H_TCCR4B_2MS_VAL              (1<<CS42) | (1<<CS41)   // div 32
 #define TIMER4H_TCCR4B_4MS_VAL              (1<<CS42) | (1<<CS41) | (1<<CS40) // div 64
 #define TIMER4H_TCCR4B_8MS_VAL  (1<<CS43)         // div 128
#elif F_CPU == 1000000L
 #define TIMER4H_TCCR4B_1MS_VAL              (1<<CS42)       // div 8
 #define TIMER4H_TCCR4B_2MS_VAL              (1<<CS42) |             (1<<CS40) // div 16
 #define TIMER4H_TCCR4B_4MS_VAL              (1<<CS42) | (1<<CS41)   // div 32
 #define TIMER4H_TCCR4B_8MS_VAL              (1<<CS42) | (1<<CS41) | (1<<CS40) // div 64
#else
#error "Clock must be 16, 8, 4, 2 or 1 MHz"
#endif

static uint8_t saveTCCR4A, saveTCCR4B, saveTCCR4C, saveTCCR4D, saveTCCR4E, saveOCR4C;
static uint8_t startTCCR4B;

// input is the number of milliseconds required
// output is the number of interrupts needed for that number of milliseconds
static inline uint16_t timer_init(uint16_t msec)
{
 uint16_t gate_len;

 saveTCCR4A = TCCR4A;
 saveTCCR4B = TCCR4B;
 saveTCCR4C = TCCR4C;
 saveTCCR4D = TCCR4D;
 saveTCCR4E = TCCR4E;
 saveOCR4C = OCR4C;
 TCCR4B = 0;
 TCCR4A = 0;
 TCCR4C = 0;
 TCCR4D = 0;
 TCCR4E = 0;
 OCR4C = TIMER4H_OCR4C_VAL;
 if ((msec & 7) == 0) {
   gate_len = msec >> 3;
   startTCCR4B = TIMER4H_TCCR4B_8MS_VAL | (1<<PSR4);
 } else if ((msec & 3) == 0) {
   gate_len = msec >> 2;
   startTCCR4B = TIMER4H_TCCR4B_4MS_VAL | (1<<PSR4);
 } else if ((msec & 1) == 0) {
   gate_len = msec >> 1;
   startTCCR4B = TIMER4H_TCCR4B_2MS_VAL | (1<<PSR4);
 } else {
   gate_len = msec;
   startTCCR4B = TIMER4H_TCCR4B_1MS_VAL | (1<<PSR4);
 }
 TIFR4 = (1<<TOV4);
 TCNT4 = 0;
 return gate_len;
}

static inline void timer_start(void)
{
 TCCR4B = startTCCR4B;
 TIMSK4 = (1<<TOIE4);
}

static inline void timer_shutdown(void)
{
 TCCR4B = 0;
 TIMSK4 = 0;
 OCR4C = saveOCR4C;
 TCCR4A = saveTCCR4A;
 TCCR4C = saveTCCR4C;
 TCCR4D = saveTCCR4D;
 TCCR4E = saveTCCR4E;
 TCCR4B = saveTCCR4B;
}

#define TIMER_ISR_VECTOR TIMER4_OVF_vect
#define TIMER_LATENCY_CYCLES 34


#endif // TIMER_USE_***




static inline void timer_isr_latency_delay(void)
{
#ifdef TIMER_LATENCY_CYCLES
#ifdef __AVR__
 uint8_t cycles_times_3 = TIMER_LATENCY_CYCLES / 3;
 asm volatile(
   "L_%=_loop:"
   "subi   %0, 1"    "\n\t"
   "brne   L_%=_loop"  "\n\t"
   : "+d" (cycles_times_3)
   : "0" (cycles_times_3)
 );
#endif
#endif
}


/**********************************************/
/*   Board Specific Interrupts (to hog)       */
/**********************************************/

static inline void disable_other_interrupts(void)
{
}
static inline void restore_other_interrupts(void)
{
}



#endif

static uint16_t count_msw;
static uint32_t count_prev;
static volatile uint32_t count_output;
static volatile uint8_t count_ready;
static uint16_t gate_length;
static uint16_t gate_index;


void FreqCountClass::begin(uint16_t msec)
{
 if (msec < 1) return;
 gate_index = 0;
 count_msw = 0;
 count_prev = 0;
 count_ready = 0;
 counter_init();
 gate_length = timer_init(msec);
 uint8_t status = SREG;
 cli();
 timer_start();
 timer_isr_latency_delay();
 counter_start();
 SREG = status;
}

uint8_t FreqCountClass::available(void)
{
 return count_ready;
}

uint32_t FreqCountClass::read(void)
{
 uint32_t count;
 uint8_t status;

 status = SREG;
 cli();
#if defined(TIMER_USE_TIMER2) && F_CPU == 12000000L
 float correct = count_output * 0.996155;
 count = (uint32_t) (correct+0.5);
#else
 count = count_output;
#endif
 count_ready = 0;
 SREG = status;
 return count;
}

void FreqCountClass::end(void)
{
 timer_shutdown();
 counter_shutdown();
}


ISR(TIMER_ISR_VECTOR)
{
 uint16_t count_lsw;
 uint32_t count;
 uint16_t index, length;

 count_lsw = counter_read();
 if (counter_overflow()) {
   counter_overflow_reset();
   count_msw++;
 }
 index = gate_index + 1;
 length = gate_length;
 if (index >= length) {
   gate_index = 0;
   count = ((uint32_t)count_msw << 16) + count_lsw;
   count_output = count - count_prev;
   count_prev = count;
   count_ready = 1;
   restore_other_interrupts();
 } else {
   if (index == length - 1) disable_other_interrupts();
   gate_index = index;
 }
}
Als Erstes wird eine Instanz vom Typ FreqCountClass angelegt.

FreqCountClass FreqCount;

Danach wird der Frequenzzähler gestartet.

FreqCount.begin(1000); // zählt in Hz
FreqCount.begin(1); // zählt in kHz, reicht für die Anwendung hier aus und ist schneller

Wenn eine neue Frequenzmessung erfolgt ist, muss Du nur noch den Wert einer Variablen übergeben.
Das machst Du in einer Endlosschleife immer und immer wieder und passt bei Änderungen die Stellung des Servos an, damit der UKW-Empfänger dem AM-Drehkondensator nachgeführt wird.

 if (FreqCount.available()) 
    {

    count = FreqCount.read(); // Frequenzwert in Variable count schreiben
    count = count * 1000; // wenn nur kHz gezählt werden
    }
 
Zum Schluß

 FreqCount.end();

Beendet die Frequenzzählung.

Vor den Frequenzzähler baust Du noch einen Vorverstärker J310/BF245, dann ein oder zwei BF199 als Nachfolgestufe, an den Verstärkereingang eine kleine resonante Schleife. Damit ist eine kontaktlose Aufnahme der Oszillatorfrequenz von außerhalb des Gerätes möglich. Siehe Wolfgang mit seiner Philetta.
Dann baust Du alles mit dem KEMO (oder ein TDA7088) mit Servo und Controller in eine Box, führst die NF nach drausen oder modulierst Sie mit der ZF. Wenn Du jetzt dein Gerät auf eine günstige Stelle eines AM-Radio stellst (zum Beispiel auf das Gehäuse überhalb der Misch-/Oszillatorröhre), kannst Du automatisch mit dem Radio (zum Beispiel auf MW) UKW empfangen und die UKW-Frequenz mit dem AM-Drehko einstellen. Willst Du ein anderes Röhrenradio UKW-isieren, stellst Du die Box einfach auf eine günstige Stelle des anderen Radios.
Ansprechpartner für Umbau oder Modernisierung von Röhrenradios mittels SDR,DAB+,Internetradio,Firmwareentwicklung. 
Unser Open-Source Softwarebaukasten für Internetradios gibt es auf der Github-Seite! Projekt: BM45/iRadio (Google "github BM45/iRadio")
Zitieren
#12
@Bernhard,
ja, der Tiny hat Timer-Eingänge.

   

Die Frage ist, welche Pins noch frei bzw. schon belegt sind?
Grüße aus Wassenberg,
Norbert.
Zitieren
#13
Hallo Norbert,

ein Quarz an XTAL1/2 wäre auch Pflicht, der interne Oszillator ist nur eine sehr ungenaue Hilfstaktquelle!
Warum überhaupt so ein kleiner Tiny? Lernen tut man am Besten doch mit den großen Boards. Ein 328p (Amazon Suchbegriff "328p") auf einem Mini oder Nanoboard kostet doch auch nur noch 5Euro/Stück. Ich habe lieber mehr als zu wenig Ausstattung im Prozessor. Irgendwann braucht man mehr I/Os, interne Timer/Counter, I2C usw. Ein Tiny mit Servo und Poti, vielleicht noch eine LED ist in Ordnung, nur viel mehr kann man mit den kleinen Dingern auch nicht machen weil die Anschlüsse dann schon belegt sind und ein Projekt wächst schnell wenn man Teilerfolge erzielt.
Ansprechpartner für Umbau oder Modernisierung von Röhrenradios mittels SDR,DAB+,Internetradio,Firmwareentwicklung. 
Unser Open-Source Softwarebaukasten für Internetradios gibt es auf der Github-Seite! Projekt: BM45/iRadio (Google "github BM45/iRadio")
Zitieren
#14
Tja Bernhard,
die Frage warum ein Tiny gewählt wurde, kann nur unser Jupp beantworten.
Ich vermute, dass die bauliche Größe eine Rolle gespielt hat - und für das Ausgangsprojekt reicht der Controller aus.

Mal ganz ehrlich, Bernhard, dass die Preise derart in den Keller gehen hätte ich nie vermutet.
Die Chinesen überschwemmen förmlich den Markt. Was man heute kauft, ist morgen viel zu teuer bezahlt.
Aber das ist ein anderes Thema.
Grüße aus Wassenberg,
Norbert.
Zitieren
#15
(05.06.2017, 19:46)norbert_w schrieb: Tja Bernhard,
die Frage warum ein Tiny gewählt wurde, kann nur unser Jupp beantworten.
Ich vermute, dass die bauliche Größe eine Rolle gespielt hat - und für das Ausgangsprojekt reicht der Controller aus.

Mal ganz ehrlich, Bernhard, dass die Preise derart in den Keller gehen hätte ich nie vermutet.
Die Chinesen überschwemmen förmlich den Markt. Was man heute kauft, ist morgen viel zu teuer bezahlt.
Aber das ist ein anderes Thema.

ich hatte ja geschrieben daß ich den attiny gewählt habe weil der Arduino überkandidelt ist. Es ging nicht um den Preis sondern um die Verhältnismäßigkeit. Und ein gewisser Spieltrieb ist natürlich dabei.

Apropos Preise:

Original JYE Tech DSO-SHELL DSO150 15001K DIY Digital Oscilloscope Kit With Hous

Bei dem Frequenzzähler-Beispiel würde ich keinen Servo dazwischenschalten, sondern direkt vom Board die Abstimmspannung ausgeben lassen. Es muss ja nichts angezeigt werden. Das AM-Radio hat ja seine Skala.
Zitieren
#16
(04.06.2017, 21:49)norbert_w schrieb: Hallo Jupp,

habe gerade auf der Digispark WebSite folgendes gelesen:

Code:
Synchronization:

By giving 1 or true as optional argument for the SoftRcPulseOut::refresh() method, the pulses are refreshed immediately (without waiting for the usual 20ms).

the SoftRcPulseOut::refresh() method returns 1 or true when the pulses have been refreshed. Testing this return value provides a 20ms timer.

So wie ich das verstehe, bewirkt ein Funktionsaufruf ohne Parameter 
 SoftRcPulseOut::refresh();
eine Verzögerungszeit von 20ms, während ein Aufruf
 SoftRcPulseOut::refresh(NOW); 
ohne Verzögerung arbeitet.

Vielleicht kannst Du das mal testen??

hab ich gemacht. Es ist wie du sagst. Mit leerem Argument kann ich das delay weglassen. Die 20ms sind anscheinend im define festgelegt.

( #define REFRESH_PERIOD_MS 20 )
Zitieren


Möglicherweise verwandte Themen…
Thema Verfasser Antworten Ansichten Letzter Beitrag
  UKW-Baustein TEA5767 mit ATtiny85 saarfranzose 1 1.645 12.07.2017, 21:22
Letzter Beitrag: saarfranzose

Gehe zu: