1 | /*
|
2 | */
|
3 | #ifndef F_CPU
|
4 | #define F_CPU = 16000000UL
|
5 | #endif
|
6 |
|
7 | #include <avr/io.h>
|
8 | #include <avr/interrupt.h>
|
9 | #include <util/delay.h>
|
10 | #include <avr/iom16.h>
|
11 | #include <stdint.h>
|
12 |
|
13 | //Variablen
|
14 | uint16_t timer1 = 0;
|
15 | char pulse_received = 0;
|
16 | int pulselenght = 4; //Zahl der Perioden
|
17 | int messungen = 5; //Zahl der Messungen des ADCs bevor das Ergebnis ausgewertet wird
|
18 | unsigned long i = 0;
|
19 | char nothing_received = 0;
|
20 | uint32_t result = 0;
|
21 | int z = 0;
|
22 | unsigned long grenzwert = 50000ULL;
|
23 | unsigned long timeout = 500000ULL;
|
24 | uint8_t distance = 0;
|
25 | uint16_t runtime = 0;
|
26 |
|
27 |
|
28 | //Funktionsprototypen
|
29 | void ultrasonic(int a);
|
30 | uint16_t time_meassurement(void);
|
31 | void init_adc(void);
|
32 | uint16_t adc_single_conversion(void);
|
33 | uint16_t adc_average_conversion(int);
|
34 | void led_ausgabe(uint8_t z);
|
35 | void enable_led_portc(int y);
|
36 |
|
37 |
|
38 |
|
39 | int main(void)
|
40 | {
|
41 |
|
42 | //Grüne Status-LED anschalten
|
43 | DDRD |= (1<<DDD1);
|
44 | PORTD |= (1<<PD1);
|
45 | DDRC = 0xFF;
|
46 | init_adc();
|
47 |
|
48 |
|
49 | /* Timer Konfiguration:
|
50 | Target Timer Count = (1 / Target Frequency) / (1 / Timer Clock Frequency) - 1
|
51 | => Target Timer Count = 1/80.000 Hz (T/2) / (1/16.000.000 Hz -1
|
52 | = 199
|
53 | Following up: Register-configuration, CTC-modus with top-value of 199 */
|
54 |
|
55 | // Timer 0 konfigurieren (8-Bit-Timer)
|
56 | TCCR0 = (1<<WGM01); // CTC Modus
|
57 | TCCR0 |= (1<<CS00); // CPU-Takt,d.h. prescaler = 1
|
58 | OCR0 = 199;
|
59 |
|
60 | // Compare Interrupt erlauben und Zähler starten
|
61 | TIMSK |= (1<<OCIE0);
|
62 |
|
63 | // Global Interrupts aktivieren
|
64 | sei();
|
65 |
|
66 |
|
67 |
|
68 | while(1)
|
69 | {
|
70 | ultrasonic(pulselenght *2 ); //Multiplikation mit 2, da in einer Periode 2x getoggelt werden muss
|
71 | _delay_ms(1000); //Verzögerung zwischen den Pulsen
|
72 | //Ausschalten für permanentes Pulsen zum Messen des Empfangsignals*/
|
73 |
|
74 | }
|
75 |
|
76 | return 0;
|
77 | }
|
78 |
|
79 | void ultrasonic(a)
|
80 | {
|
81 |
|
82 | //PD5 als Ausgang
|
83 | DDRD |= (1<< DDD5);
|
84 | _delay_ms(a);
|
85 | DDRD &= ~(1<<DDD5); //ebenfalls für permanentes Senden des Signals auszuschalten
|
86 | if(!nothing_received) timer1 = time_meassurement();
|
87 |
|
88 | /*Umrechnung des Zählerstandes in Sekunden
|
89 | 16 MHz getaktet => 16 Mio. Takte/s
|
90 | => Zählerstand = Takte
|
91 | => x Takte bis zum Zählerstand x
|
92 | Vergangene Zeit = Zählerstand / CPU-Takt
|
93 | */
|
94 | runtime = (uint16_t)(timer1 / F_CPU);
|
95 | distance = 172 * runtime * 100; //Schallgeschwindigkeit bei Raumtemperatur in etwa 343 m/s, geht in die Formel mit 1/2 ein; distance in cm, daher der Faktor 100
|
96 | led_ausgabe(distance);
|
97 | }
|
98 |
|
99 |
|
100 | uint16_t time_meassurement(void)
|
101 | {
|
102 | TCCR1A = 0x00; // Kein PWM oder sonstiges Gedöhns, lediglich als 16-Bit-Timer verwenden
|
103 | TCCR1B = (1<<CS11); // Timer läuft mit CPU-Takt/8
|
104 | /*
|
105 | Sofern der durchschnittliche Messwert keinen gewissen Schwellwert überschreitet, gilt das als
|
106 | wäre kein Echo erkannt.
|
107 | Weiterhin wird eine Variable i als Zählvariable verwendet, um eine Obergrenze festzulegen, ab
|
108 | wann definitiv kein Echo empfangen wurde.
|
109 | Der Wert 500000 entspricht etwas mehr als berechnete 464.000 Takte die der AVR für 29ms braucht,
|
110 | was in etwa einem Abstand von 5 m entsprechen würde.
|
111 | */
|
112 | i = 0;
|
113 | while(!pulse_received)
|
114 | {
|
115 | if(adc_average_conversion(messungen) <= grenzwert)
|
116 | {
|
117 | i++;
|
118 | if(i >= timeout) nothing_received = 1;
|
119 | }
|
120 | else pulse_received = 1;
|
121 | }
|
122 | cli(); //Interrupts deaktivieren
|
123 | uint16_t b = TCNT1; //Lese den Zählerstand ab
|
124 | TCCR1B &= ~(1<<CS11); //Timer anhalten
|
125 | TCNT1 = 0x00; //Timerstand zurücksetzen
|
126 | sei(); //und wieder die Interrupts aktivieren
|
127 | return b;
|
128 | }
|
129 |
|
130 | void init_adc(void)
|
131 | {
|
132 | ADMUX |= (1<<REFS1) | (1<<REFS0); //interne 2,56V als Referenz benutzen
|
133 | ADCSRA |= (1<<ADPS0) | (1<<ADPS1) | (1<<ADPS2); //ADC-Frequenz: 16MHz/128 = 125kHz
|
134 | ADCSRA |= (1<<ADEN); //Aktiviere den ADC
|
135 | //Das erste Ergebnis der Wandlung nach dem Initalisieren ist Murks, also:
|
136 | ADCSRA |= (1<<ADSC); //Führe eine Wandlung durch
|
137 | while(ADCSRA & (1<<ADSC)) {} //So lange wie das Bit ADSC nicht auf 0, d.h. logisch false zurück springt, tu nix und warte
|
138 | result = ADCW; //Lese das Ergebnis, nun ist der Wandler bereit
|
139 | }
|
140 |
|
141 | uint16_t adc_single_conversion(void)
|
142 | {
|
143 | ADMUX = 0x00; //Wandlung auf Kanal 0, Pin PA0;
|
144 | ADCSRA |= (1<<ADSC); //starte Einzelwandlung
|
145 | while(ADCSRA & (1<<ADSC)) {}
|
146 | return ADCW;
|
147 | }
|
148 |
|
149 | uint16_t adc_average_conversion(int d)
|
150 | {
|
151 | for(z = 0; z < d; z++)
|
152 | {
|
153 | result =+ adc_single_conversion();
|
154 | }
|
155 | result = (uint16_t) (result/d);
|
156 | return result;
|
157 | }
|
158 |
|
159 | //Timer-Interrupt-Routine: Wechsel von High auf Low; der Puls kann ausgegeben werden durch Toggeln von DDRD
|
160 | ISR (TIMER0_COMP_vect)
|
161 | {
|
162 | //PD5 toggeln
|
163 | PORTD ^= (1 << PD5);
|
164 | }
|
165 |
|
166 | void led_ausgabe(uint8_t z)
|
167 | {
|
168 | if(z <= 20) enable_led_portc(7);
|
169 | if(z <= 40) enable_led_portc(6);
|
170 | if(z <= 60) enable_led_portc(5);
|
171 | if(z <= 80) enable_led_portc(4);
|
172 | if(z <= 100) enable_led_portc(3);
|
173 | if(z <= 120) enable_led_portc(2);
|
174 | if(z <= 140) enable_led_portc(1);
|
175 | if(z > 140) enable_led_portc(0);
|
176 | }
|
177 |
|
178 | void enable_led_portc(int y)
|
179 | {
|
180 | DDRC = 0xFF;
|
181 | PORTC = 0x00; //alle LEDs ausschalten zu Beginn
|
182 | switch(y)
|
183 | {
|
184 | case 0 : PORTC = (1<<PC0);break;
|
185 | case 1 : PORTC = (1<<PC1);break;
|
186 | case 2 : PORTC = (1<<PC2);break;
|
187 | case 3 : PORTC = (1<<PC3);break;
|
188 | case 4 : PORTC = (1<<PC4);break;
|
189 | case 5 : PORTC = (1<<PC5);break;
|
190 | case 6 : PORTC = (1<<PC6);break;
|
191 | case 7 : PORTC = (1<<PC7);break;
|
192 | }
|
193 | }
|