Attiny 412 ext. Interrupt triggert ohne event

#7886229
Lesenswert?

Hallo liebe MC.net Community. Ich möchte mit einem attiny 412 die Werte eines Rotary Encoders auslesen, diese in einer kurzen Nachricht auf ein 433 MHz Signal aufmodulieren und an einen Empfänger senden.

Ich habe dazu einen Code geschrieben der auch teilweise funktioniert. Auf das Buttonpress event wird immerhin getriggert. Auf die anderen events wie Encoder_left und Encoder_right leider nicht. Außerdem triggert es relativ unzuverlässig (meistens zu oft nach buttonpress).

Ein Schmitttrigger ist dem Entprellschaltkreis nachgeschaltet. Bei Schaltvorgang wird der clk, dt, oder eben sw pin auf GND gezogen und vom Schmitttrigger invertiert. Nur falls sich jemand wundert wieso auf steigende Flanke getriggert wird.

Ich hatte Sorge, dass mein Schmitttrigger irgendwie kaputt ist. Aber ein Blick aufs Oszi zeigt ein ausreichend sauberes Signal. Ich wäre sehr froh darüber wenn mir jemand sagen kann wo der oder die Fehler sind.

Angehängte Dateien:
Moderator (Firma: Titel) Persönliche Seite #7886601
Lesenswert?

IrgendSoEinTyp I. schrieb:

Aber ein Blick aufs Oszi

Was sieht man da? Was ist Gelb und was ist Blau?

zeigt ein ausreichend sauberes Signal.

Dann sollte das wohl auch nur "ausreichend" (in Schulnoten 4) funktionieren, oder? Warum ist das alles so krumm und schief? (Warum) ist da Brumm überlagert?

Wastl schrieb:

IrgendSoEinTyp I. schrieb:

Encoder_left und Encoder_right

Was ist das für ein seltsamer Rotary Encoder?

Ich hätte hier auch Encoder_A und Encoder_B erwartet...

#7886610
Lesenswert?

IrgendSoEinTyp I. schrieb:

Hallo liebe MC.net Community. Ich möchte mit einem attiny 412 die Werte eines Rotary Encoders auslesen,

Welche Werte? Die Logikpegel oder eine Schritt nach links/rechts?

diese in einer kurzen Nachricht auf ein 433 MHz Signal aufmodulieren und an einen Empfänger senden.

Ich habe dazu einen Code geschrieben der auch teilweise funktioniert.

Wo kann man den bewundern?

Auf das Buttonpress event wird immerhin getriggert. Auf die anderen events wie Encoder_left und Encoder_right leider nicht. Außerdem triggert es relativ unzuverlässig (meistens zu oft nach buttonpress).

Für den Taster sollte man die gleiche Schaltung mit Pull Up und RC-Filter nutzen. Eine Entprellung in Software kann auch nicht schaden.

Ein Schmitttrigger ist dem Entprellschaltkreis nachgeschaltet.

Braucht man nicht, den hat der AVR schon an allen IOs eingebaut.

Bei Schaltvorgang wird der clk, dt, oder eben sw pin

Falsch! Es gibt keine clk und dt Pin am Drehgeber, nur Kanal A und B!

auf GND gezogen und vom Schmitttrigger invertiert. Nur falls sich jemand wundert wieso auf steigende Flanke getriggert wird.

Ich wundere mich eher daß du glaubt, daß man aus deinen Screenshot was ablesen können soll. Miss erstmal die beiden Kanäle A und B und schau wie die aussehen. Nach deinen externen Schmitt-Triggern müssen die sauber sein und immer phasenverschoben zueinander.

Ich hatte Sorge, dass mein Schmitttrigger irgendwie kaputt ist. Aber ein Blick aufs Oszi zeigt ein ausreichend sauberes Signal.

Nö. Schau dir den Ein- und Ausgang eines Kanals gleichzeitig an.

Ich wäre sehr froh darüber wenn mir jemand sagen kann wo der oder die Fehler sind.

Kann keiner. Bestenfalls Hinweise geben.

#7886625
Lesenswert?

Falk B. schrieb:

Falsch! Es gibt keine clk und dt Pin am Drehgeber, nur Kanal A und B!

Naja bei Arduino heißen die eben so: https://www.az-delivery.de/en/products/drehimpulsgeber-modul

DT soll wohl Detent bedeuten, d.h. wo die Rastung erfolgt. Da ist dann dessen Pegel unbestimmt und sollte nicht zur Triggerung benutzt werden. Es gibt auch Encoder, wo die Rastung außerhalb der Flankenwechsel beider Signale erfolgt. Und welche, die Richtung und Takt liefern.

#7886638
Lesenswert?

Oh, da hab ich wohl vergessen den Code hochzuladen.

1
#include <avr/io.h>
2
#include <util/delay.h>
3
#include <avr/interrupt.h>
4
#include <avr/sleep.h> // Required for sleep modes
5
#include <stdint.h>    // For standard integer types, e.g., uint8_t
6

7
// --- Pin Definitions ---
8
#define TX_PIN    PIN6_bm // PA6 for transmission
9
#define CLK_PIN   PIN7_bm // PA7 for rotary encoder clock (interrupt)
10
#define DT_PIN    PIN1_bm // PA1 for rotary encoder data
11
#define SW_PIN    PIN2_bm // PA2 for rotary encoder switch (interrupt)
12

13
// --- Task Queue Configuration ---
14
#define BUFFER_SIZE 8 // Size of the circular buffer for tasks
15

16
// --- Event Types (messages to be stored in the queue) ---
17
typedef enum {
18
    NO_EVENT = 0,
19
    ENCODER_RIGHT = 0x01, // Example value for right turn
20
    ENCODER_LEFT = 0x02,  // Example value for left turn
21
    BUTTON_PRESS = 0x04   // Example value for button press
22
} EventType;
23

24
// --- Task Queue Variables ---
25
volatile EventType taskQueue[BUFFER_SIZE]; // Array for the task queue
26
volatile uint8_t queueHead = 0;             // Index for adding new tasks
27
volatile uint8_t queueTail = 0;             // Index for removing tasks
28

29
// --- Function to add a task to the queue ---
30
void enqueue_task(EventType event) {
31
    uint8_t nextHead = (queueHead + 1) % BUFFER_SIZE;
32
    // Check if queue is not full before enqueuing
33
    if (nextHead != queueTail) {
34
        taskQueue[queueHead] = event;
35
        queueHead = nextHead;
36
    }
37
}
38

39
// --- Function to remove a task from the queue ---
40
EventType dequeue_task() {
41
    // Check if queue is empty
42
    if (queueHead == queueTail) {
43
        return NO_EVENT;
44
    }
45
    EventType event = taskQueue[queueTail];
46
    queueTail = (queueTail + 1) % BUFFER_SIZE;
47
    return event;
48
}
49

50
// --- Transmission Functions (approx. 5kbps) ---
51
// For 5kbps, each bit duration is 1 / 5000 Hz = 200 us.
52
// Adjust BIT_DURATION_US empirically for accurate timing if needed.
53
#define BIT_DURATION_US 193 // Adjusted for potential instruction overhead
54

55
void send_bit(uint8_t bit) {
56
    if (bit) {
57
        PORTA.OUTSET = TX_PIN;  // High (Transmitter ON)
58
    } else {
59
        PORTA.OUTCLR = TX_PIN;  // Low (Transmitter OFF)
60
    }
61
    _delay_us(BIT_DURATION_US);
62
}
63

64
void send_byte(uint8_t data) {
65
    // Start bit (active low, so 0)
66
    send_bit(0);
67

68
    // Data bits (LSB first)
69
    for (uint8_t i = 0; i < 8; i++) {
70
        send_bit(data & 0x01);
71
        data >>= 1;
72
    }
73

74
    // Stop bit (active high, so 1)
75
    send_bit(1);
76
}
77

78
// --- Interrupt Service Routine for PORTA Pin Change Interrupts ---
79
ISR(PORTA_PORT_vect) {
80
    // Clear the interrupt flags for the pins that caused the interrupt.
81
    // This is crucial to prevent re-triggering immediately.
82
    if (PORTA.INTFLAGS & CLK_PIN) {
83
        PORTA.INTFLAGS = CLK_PIN; // Clear CLK_PIN's interrupt flag
84
        // Read DT_PIN to determine direction
85
        if (PORTA.IN & DT_PIN) {
86
            // DT_PIN is HIGH when CLK_PIN goes HIGH -> Counter-clockwise (Left)
87
            enqueue_task(ENCODER_LEFT);
88
        } else {
89
            // DT_PIN is LOW when CLK_PIN goes HIGH -> Clockwise (Right)
90
            enqueue_task(ENCODER_RIGHT);
91
        }
92
    }
93

94
    if (PORTA.INTFLAGS & SW_PIN) {
95
        PORTA.INTFLAGS = SW_PIN; // Clear SW_PIN's interrupt flag
96
        // Enqueue button press. Consider debouncing for real-world buttons.
97
        enqueue_task(BUTTON_PRESS);
98
    }
99
}
100

101
// --- Setup Function ---
102
void setup() {
103
    // Configure TX_PIN (PA6) as output
104
    PORTA.DIRSET = TX_PIN;   // Define PA6 as output
105
    PORTA.OUTCLR = TX_PIN;   // Set initial value of PA6 to 0 (OFF)
106

107
    // Configure CLK_PIN (PA7), DT_PIN (PA1), SW_PIN (PA2) as inputs
108
    PORTA.DIRCLR = CLK_PIN;
109
    PORTA.DIRCLR = DT_PIN;
110
    PORTA.DIRCLR = SW_PIN;
111

112
    // Configure interrupts for CLK_PIN (PA7) and SW_PIN (PA2) on rising edge
113
    // Make sure these are the correct bits for your specific AVR family (e.g., tinyAVR 0/1/2-series, megaAVR)
114
    PORTA.PIN7CTRL |= PORT_ISC_RISING_gc; // CLK_PIN interrupt on rising edge
115
    PORTA.PIN2CTRL |= PORT_ISC_RISING_gc; // SW_PIN interrupt on rising edge
116

117
    // Set the sleep mode to POWER_DOWN for maximum power saving.
118
    // The device will wake up on any enabled interrupt.
119
    set_sleep_mode(SLEEP_MODE_PWR_DOWN);
120

121
    // Enable global interrupts
122
    sei();
123
}
124

125
// --- Main Loop ---
126
void loop() {
127
    // First, check if there are any events to process before going to sleep.
128
    // This prevents the system from sleeping if an event arrived just before `sleep_mode()`.
129
    cli(); // Disable interrupts while checking/modifying the queue
130
    if (queueHead != queueTail) { // If the queue is not empty
131
        EventType currentEvent = dequeue_task();
132
        sei(); // Re-enable interrupts
133
        // Transmit the event. Keep interrupts disabled during transmission
134
        // to ensure accurate bit timing.
135
        cli();
136
        send_byte((uint8_t)currentEvent);
137
        sei();
138
    } else {
139
        // If the queue is empty, enable interrupts, and go to sleep.
140
        // The MCU will wake up on any enabled interrupt (CLK_PIN or SW_PIN).
141
        // Interrupts must be enabled *before* calling sleep_mode().
142
        sei();
143
        sleep_mode(); // Go to selected sleep mode
144
        // Execution resumes here after an interrupt wakes the MCU
145
    }
146
    // After waking up or processing an event, the loop continues to check the queue again.
147
    // If there were multiple events queued, it will process them one by one.
148
}

[Mod: bitte die Bedienungsanleitung über der Eingabebox lesen. Da steht u.a. das mit dem c-Tags und auch "Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang"]

#7886647
Lesenswert?

IrgendSoEinTyp I. schrieb:

Hier ein besseres Bild vom Entprellschaltkreis.

Das ist kein Schaltkreis, sondern eine Schaltung. Ein Schaltkreis ist ein Chip, IC ein Bauteil mit meist vielen Anschlüssen.

Die Schaltung ist an sich OK, wenn gleich die 22pF Kondensatoren kompletter Unsinn sind. Laß sie weg. Und positive Betriebsspannung zeichnet man oben.

Schaltplan richtig zeichnen

Der ominöse SW1 Schalter ist ein EC11 Rotary Encoder.

Ein normaler, mechanischer Drehgeber mit Tastfunktion.

#7886649
Lesenswert?

IrgendSoEinTyp I. schrieb:

// Set the sleep mode to POWER_DOWN for maximum power saving.

Sleep mode setze ich nie, während alles noch in Entwicklung ist. Erst nachdem alle Tests erfolgreich waren, kann man es hinzufügen. Ist kein Batteriebetrieb vorgesehen, spare ich mir natürlich diese Arbeit. Die AVRs werden ja im Normalbetrieb nicht so heiß, daß Abwärme ein Problem sein kann.

#7886690
Lesenswert?

Hier nochmal ein aussagekräftigeres Bild. Die gelbe Linie zeigt den CLK Pin, die hellblaue den DT Pin. Beide jeweils nach dem Schmitttriggerausgang. Die violette Linie zeigt wieder den CLK Pin und die dunkelblaue Linie den DT Pin. Diesmal beide kurz vor dem Schmitttriggereingang. Die Ladekurve des Kondensators sieht ziemlich unsauber aus. Vielleicht ist das das Problem für die mehrfachen Trigger. Wobei man ja dann was am Schmitttrigerausgang sehen müsste.. Weiß jemand wie der stufenweise Anstieg am Geber zustande kommt?

Angehängte Dateien:
#7886697
Lesenswert?

Peter D. schrieb:

Das mit der queue ist recht sinnfrei, wenn Du eh beim Senden mit SW-UART alles anhältst. Die kann nie mehr als ein Ereignis enthalten.

Was stört Dich an der HW-UART?

Bin ich noch relativ neu in der AVR Programmierung und weiß nicht wie ich ein Byte ohne großen Overhead versende. Die Nachricht an den Empfänger soll innerhalb von max. 2ms versendet sein. Der Sender den ich im Moment verwende schafft max. 10kbps. Bin im Moment bei 5kbps. Ein bisschen Luft habe ich also noch. Vielleicht baue ich mir auch meinen eigenen Sender aber das würde den Ramen dieses Projektes sprengen. Außerdem waren bis jetzt alle Bibliotheken die ich gefunden habe bis auf TinyRF85 zu groß für meinen MC.

Moderator (Firma: Titel) Persönliche Seite #7886732
Lesenswert?

IrgendSoEinTyp I. schrieb:

Die violette Linie zeigt wieder den CLK Pin und die dunkelblaue Linie den DT Pin.

Das ist ein stinknormale Quadratursignal mit A und B Spur. Das wertet man sinnigerweise an jeder Flanke eines der beiden Signale aus. Oder besser über einen Zustandsautomaten. Aber was sage ich: in Drehgeber steht alles drin. Das funktioniert sicher sogar ohne großartige Frickelhardware.

Weiß jemand wie der stufenweise Anstieg am Geber zustande kommt?

Zeig mal deinen Schaltungsaufbau. Das sieht aus, als ob es einen gemeinsamen Vorwiderstand für die beiden Spuren gibt, denn die eine zieht die andere mit runter.

#7886748
Lesenswert?

Falk B. schrieb:

IrgendSoEinTyp I. schrieb:

Das ist kein Schaltkreis, sondern eine Schaltung. Ein Schaltkreis ist ein Chip, IC ein Bauteil mit meist vielen Anschlüssen.

Die Schaltung ist an sich OK, wenn gleich die 22pF Kondensatoren kompletter Unsinn sind. Laß sie weg. Und positive Betriebsspannung zeichnet man oben.

Ein normaler, mechanischer Drehgeber mit Tastfunktion.

  1. Nein. Google mal Schaltkreis. "Ein Schaltkreis ist ein geschlossener, leitender Pfad, durch den elektrischer Strom fließen kann. " Das ist die Definition die du dazu finden wirst. Das was du meinst sind Integrierte Schaltkreise.

  2. Mein Gott. Ist es mir herzlich egal welche falsch verwendeten Begriffe du in diesem Beitrag noch findest. Bitte sei nützlich oder spar dir deine Kommentare. Hatte wirklich gehofft hier konstruktive Lösungsvorschläge zu bekommen.

Moderator (Firma: Titel) Persönliche Seite #7886920
Lesenswert?

IrgendSoEinTyp I. schrieb:

Google mal Schaltkreis. "Ein Schaltkreis ist ein geschlossener, leitender Pfad, durch den elektrischer Strom fließen kann"

Glaubst du der KI jeden Klugschiß? Ich bin mir sicher, die könnte von Falk in Richtung Elektronik noch einiges lernen. Aber leider bietet sie nur den Mittelwert der Infos, die sie im Netz findet.

Seis drum. Da steht noch die Frage aus, wie dein "Schaltkreis" denn nun in der Realität aussieht. Denn die Schaltung, die du gepostet hast, erzeugt keine solche Signale, wie dein Oszi sie anzeigt.

#7886959
Lesenswert?

Kann es sein, dass VCC des HC14 nicht angeschlossen ist? Dann würde er nur über die Eingangschutzdioden über den Pull-Up des Tasters versorgt. Aber natürlich nur schwach. Das würde dann erklären warum die Eingänge so komisch aussehen, Sie beteiligen sich dann an der Phantomspeisung des HC14 wenn sie High-Pegel haben.

#7886979
Lesenswert?

IrgendSoEinTyp I. schrieb:

Die Nachricht an den Empfänger soll innerhalb von max. 2ms versendet sein. Der Sender den ich im Moment verwende schafft max. 10kbps. Bin im Moment bei 5kbps.

Ich weiß ja nicht, wie die Funkstrecke aussieht, aber das könnte Dein nächstes Problem werden. 10 kbps klingt nach den simplen 433 MHz-Arduino-Funkmodulen. Damit wirst Du bei diesen Anforderungen die Übertragung nie stabil hinbekommen.

#7887002
Lesenswert?

Ich entwickle Programme nie als großen Brocken, sondern Schritt für Schritt. Also erstmal die UART zum Laufen bringen. Dann Encoder. Dann Funk. Und ganz zum Schluß Stromsparen.

Funk sollte immer in Paketen mit Protokoll und CRC erfolgen. Einzelne Bytes reduzieren die Datenrate drastisch. Funk braucht immer einige Zeit, die Verbindung aufzubauen.

#7887009
Lesenswert?

IrgendSoEinTyp I. schrieb:

Bin ich noch relativ neu in der AVR Programmierung und weiß nicht wie ich ein Byte ohne großen Overhead versende.

ATtiny402/412 send byte:

1
#include <avr/io.h>
2

3
uint8_t data[13] = {72,101,108,108,111,44,32,87,111,114,108,100,33};   // data 
4
uint8_t cnt;                                                           // SW counter
5

6
void send_byte(uint8_t dbt)
7
{
8
  while(!(USART0.STATUS & USART_DREIF_bm)){}    // wait until transmit data register is empty
9
  USART0.TXDATAL = dbt;                          // transmit data byte  
10
}
11

12
int main(void)
13
{
14
  _PROTECTED_WRITE(CLKCTRL.MCLKCTRLB, CLKCTRL_PDIV_10X_gc | CLKCTRL_PEN_bm);  // 2 MHz main clock
15

16
  USART0.BAUD = 833;                                   // 9600 Bd @ 2.00 MHz
17
  USART0.CTRLB = USART_TXEN_bm;                        // enable USART transmitter
18
  PORTA.DIRSET = PIN6_bm;                              // PA6 output (USART0 TxD)
19

20
  TCB0.CCMP = 0xC34F;                                  // 50ms @ 1.00 MHz TCB clock
21
  TCB0.CTRLA = TCB_CLKSEL_CLKDIV2_gc | TCB_ENABLE_bm;  // 1.00 MHz, enable TCB
22

23
  while(1)
24
  {
25
    if(TCB0.INTFLAGS)
26
    {
27
      TCB0.INTFLAGS = TCB_CAPT_bm;                     // clear TCB0 interrupt flag
28
      cnt++;                                           // increment
29
      if(cnt == 10)                                    // 500 ms
30
      {
31
        cnt = 0;                                       // clear
32
        for(uint8_t i=0;i<13;i++)
33
        {
34
          send_byte(data[i]);
35
        }
36
        send_byte('\r');                               // "carriage return"
37
        send_byte('\n');                               // "newline"
38
      }
39
    }
40
  }
41
}
Angehängte Dateien:

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren