Hello,
ich habe mir schon an anderer Stelle hier im Forum Hilfe bei der
Hardware eingeholt. Ich arbeite im Moment an einer Schaltung, die ich in
kleinerer Stückzahl herstellen möchte, bzw. hergestellt habe (10 Stück).
Es ist handelt sich um kleine Roboter-Avatare. Wenn sich zwei Kollegen
"erkennen" (soll per IR gelöst werden) fangen sie an sich zu
"unterhalten".
Das hier ist mein aktueller Code, auf ATTINY84: IR-LED an PA6, TSOP an
PB2/INT0. Die RGB LEDs belegen PA0-PA5. Es gibt noch einen Piezo auf
PA7, der ist aber erst mal zweitrangig.
1 | #include <avr/power.h>
|
2 | #include <avr/wdt.h>
|
3 | #include <avr/sleep.h>
|
4 | #include <avr/interrupt.h>
|
5 | #include <stdlib.h>
|
6 | #include <util/delay.h>
|
7 |
|
8 | #define B1_MASK (1<<PA0)
|
9 | #define G1_MASK (1<<PA1)
|
10 | #define R1_MASK (1<<PA2)
|
11 | #define R2_MASK (1<<PA3)
|
12 | #define G2_MASK (1<<PA4)
|
13 | #define B2_MASK (1<<PA5)
|
14 |
|
15 | #define IR_MASK (1<<PA6)
|
16 | #define PIEZO_MASK (1<<PA7)
|
17 |
|
18 | #define IR_CARRIER_T 28 // for 25kHz
|
19 | #define IR_INIT_PERIODS 200 // 200 mal 28us MARK senden
|
20 | #define TOLERANZ 20 // 20% toleranz für gemessene signallänge
|
21 |
|
22 | #define TSOP_MASK (1<<PB2)
|
23 |
|
24 | volatile uint8_t scan = 0;
|
25 |
|
26 | void pulseIR(long periods){
|
27 |
|
28 | // so viele pulse wie nmöglich abarbeiten
|
29 | while(periods>0){
|
30 | PORTA |= IR_MASK;
|
31 | _delay_us( IR_CARRIER_T/2 );
|
32 | PORTA &= ~IR_MASK;
|
33 | _delay_us( IR_CARRIER_T/2 ); // 28us periode macht ziemlich genau 25khz carrier
|
34 |
|
35 | periods -= 1;
|
36 | }
|
37 | }
|
38 |
|
39 | void sendIRsignal() {
|
40 | pulseIR(IR_INIT_PERIODS); // 200*28 = rechnerisch ca. 5.600us
|
41 | }
|
42 |
|
43 | void wdt_init() {
|
44 | cli();
|
45 | wdt_reset();
|
46 | WDTCSR = (1<<WDE) | (1<<WDCE); // enable watchdog
|
47 | WDTCSR = (1<<WDIE) | WDTO_1S; // enable watchdog interrupt
|
48 | sei();
|
49 | }
|
50 |
|
51 | void sleep_now() {
|
52 | power_all_disable();
|
53 | set_sleep_mode(SLEEP_MODE_PWR_DOWN);
|
54 | sleep_enable();
|
55 | sleep_mode();
|
56 | sleep_disable();
|
57 | wdt_init();
|
58 | }
|
59 |
|
60 | // alle n sekunden feuert der WDT diesen interrupt
|
61 | ISR(WDT_vect){
|
62 | sendIRsignal(); // ir signalfolge schicken
|
63 | _delay_ms(1); // kleines delay, sonst erkennt INT0 sich selbst
|
64 | }
|
65 |
|
66 | // wenn INT0 gegen GND gezogen wird, feuert die ISR
|
67 | ISR(INT0_vect){
|
68 | scan = 1; // hier nur die flagge setzen
|
69 | }
|
70 |
|
71 | void statusBlink(int mask){
|
72 | PORTA |= mask;
|
73 | _delay_ms(100);
|
74 | PORTA &=~mask;
|
75 | }
|
76 |
|
77 | unsigned long checkIRsignal(){
|
78 |
|
79 | unsigned long periods = 0;
|
80 |
|
81 | while( !(PORTB & TSOP_MASK) ){ // TSOP ist immer noch auf GND = signal kommt an
|
82 |
|
83 | if( periods > IR_INIT_PERIODS ){ // ist das signal schon länger, als das vom gegenpart gesendete?
|
84 |
|
85 | // blinken um zu wissen, wo man ist
|
86 | statusBlink(B2_MASK);
|
87 |
|
88 | // aus der funktion springen
|
89 | return 0;
|
90 | }
|
91 |
|
92 | periods++; // ansonsten: weiter hochzählen..
|
93 | _delay_us( IR_CARRIER_T ); // ..und eine periode warten
|
94 |
|
95 | }
|
96 |
|
97 | // jetzt die gemessene signallänge mit dem erwarteten wert (200*t) abgleichen
|
98 | if( abs(periods - 200) <= (periods * TOLERANZ / 100) ){
|
99 | return 1;
|
100 | }
|
101 |
|
102 | // signal zu kurz
|
103 | statusBlink(G1_MASK); // kurzer status
|
104 |
|
105 | return 0;
|
106 | }
|
107 |
|
108 |
|
109 | int main(void) {
|
110 |
|
111 | // rgb leds, ir transmitter und piezo auf output
|
112 | DDRA = 0xFF;
|
113 |
|
114 | // tsop ir sensor auf INT0
|
115 | MCUCR &= ~(1<<ISC01);
|
116 | MCUCR &= ~(1<<ISC00); // interrupt bei fallender flanke gen GND
|
117 |
|
118 | GIFR |= (1<<INTF0); // interrupt flag löschen durch logic 1
|
119 |
|
120 | GIMSK |= (1<<INT0); // intterupt anschalten
|
121 |
|
122 | // watchdog einrichten
|
123 | wdt_init();
|
124 |
|
125 | // alle interrupts aktivieren
|
126 | sei();
|
127 |
|
128 | // endlos loop
|
129 | while( 1 ) {
|
130 |
|
131 | // wenn INT0 gegen GND gezogen wird
|
132 | if(scan){
|
133 | cli(); // interrupts pausieren
|
134 | wdt_reset(); // wdt reset - savety first
|
135 |
|
136 | long detectKollega = checkIRsignal();
|
137 |
|
138 | if(detectKollega){
|
139 | int i;
|
140 | for(i=0; i<5; i++){
|
141 | PORTA |= R2_MASK;
|
142 | _delay_ms(100);
|
143 | PORTA &=~R2_MASK;
|
144 | _delay_ms(200);
|
145 | }
|
146 | }
|
147 |
|
148 | scan=0; // flag löschen
|
149 | sei(); // interrupts wieder scharf stellen
|
150 | }
|
151 |
|
152 | sleep_now(); // cpu wieder schlafen legen
|
153 |
|
154 | }
|
155 |
|
156 | return 0;
|
157 | }
|
Meine bisherigen Probleme waren u.a. dass ein Avatar sich immer selbst
erkannt hat, wenn der WDT-Interrupt das IR-Signal gesendet hat. Ich habe
das jetzt mit einem kurzen Delay lösen könnten, aber es fühlt sich ein
bisschen "schmutzig an". Ich habe eigentlich versucht an der stelle mit
cli() alle weiteren Interrupts zu pausieren, das hat leider nicht
geholfen.
Ausserdem hat der TSOP (Ist ein 25kHz-Modell) auch gerne spontan durch
andere IR-Quellen ausgelöst. Ich habe deshalb die IR-Sende-Funktion so
angelegt, dass sie über 5ms ein ON/1/Mark sendet. In der
Empfangsfunktion möchte ich abfragen, ob das ankommende Signal eben
diese 5ms durchgehend 1/ON/Mark ist, um andere IR-Quellen
(Fernbedienungen, Lampen, etc.) auszuschliessen.
Genau an dieser Stelle arbeitet der Code aber nicht wie erwartet (wieder
einmal...). Wenn ich meine Schaltung mit einer normalen Fernbedienung
befeuere, leuchtet ständig die Blaue LED. Die sollte aber nur leuchten,
wenn die hochgezählten/durchlaufenen Perioden größer ca. 5ms sind. Was
bei meiner Fernbedienung nicht sein kann...
Ich bin Anfänger, bastle sonst nur mit Arduino/Raspberry rum und möchte
aber ein bisschen tiefer in die "kleine" Welt von ATTINYs etc.
einsteigen, GCC lernen.
Wo liegt mein Fehler? — Hoffentlich kann mich einer von euch in die
richtige Richtung schubsen.
Danke & Schönen Abend,
Hans
PS: Im Anhang der Schaltplan — Vielleicht ist auch dort der Fehler?