1 | #include <math.h>
|
2 | #include <stdlib.h>
|
3 | #include <string.h>
|
4 | #include <avr/wdt.h>
|
5 | #include <avr/sleep.h>
|
6 | #include <util/delay.h>
|
7 | #include <avr/interrupt.h>
|
8 |
|
9 | #define BAUD 9600UL // Baudrate für serielle Kommunikation
|
10 | #define F_CPU 4000000UL // Systemtakt in Hz
|
11 | #define uart_buffer_size 32
|
12 | #define MYUBRR F_CPU/16/BAUD-1 // Takt für den USART
|
13 |
|
14 | char uart_tx_buffer[uart_buffer_size]; // Sendepuffer TX USART
|
15 | unsigned char counter; // Anzeige die Angeschaltet werden soll
|
16 | unsigned char status[2] = {0x01, 0x02}; // Status LED's (Rot, Grün)
|
17 | unsigned char digit[10] = {0x82, 0xF3, 0x4A, 0x62, 0x33, 0x26, 0x06, 0xF2, 0x00, 0x20}; // Bitmaske für die Zahlen
|
18 | volatile unsigned char display[2] = {0x10, 0x08}; // Siebensegmentanzeige (Einer- , Zehnerstelle)
|
19 | volatile unsigned char segment[2];
|
20 | volatile uint8_t uart_tx_flag = 1; // Flag, String komplett gesendet
|
21 |
|
22 | uint8_t MyADCSRA;
|
23 |
|
24 | ISR(TIMER0_OVF_vect) // Aufruf bei Interrupt von Timer0 (8 Bit)
|
25 | {
|
26 | PORTC |= 0xFF; // Die bisherige Anzeige ausschalten
|
27 |
|
28 | counter++; // Welches ist die nächste Stelle, um Eins erhöhen
|
29 |
|
30 | counter &= 0x01; // Wenn Stelle 2 erreicht, zurücksetzen
|
31 |
|
32 | PORTD = segment[counter]; // Jeweiligen Wert ausgeben
|
33 | PORTC = display[counter]; // Jeweilige Stelle einschalten
|
34 | }
|
35 |
|
36 | ISR(ADC_vect)
|
37 | {
|
38 | MyADCSRA |= (1 << ADIF); // Speicher für Interrupt Flag
|
39 | }
|
40 |
|
41 | ISR(USART_UDRE_vect)
|
42 | {
|
43 | static char* uart_tx_p = uart_tx_buffer;
|
44 | uint8_t data;
|
45 |
|
46 | data = *uart_tx_p++;
|
47 |
|
48 | if(data == 0)
|
49 | {
|
50 | UCSRB &= ~(1<<UDRIE); // UDRE Interrupt ausschalten
|
51 | uart_tx_p = uart_tx_buffer; // Pointer zurücksetzen
|
52 | uart_tx_flag = 1; // Flag setzen, Übertragung beeendet
|
53 | }
|
54 |
|
55 | else
|
56 | {
|
57 | UDR = data; // Daten senden
|
58 | UDR = 0x0A;
|
59 | }
|
60 | }
|
61 |
|
62 | uint16_t ReadChannel(uint8_t mux)
|
63 | {
|
64 | uint8_t i;
|
65 | uint16_t result = 0;
|
66 |
|
67 | MyADCSRA = 0; // Interrupt Variable Funktion
|
68 | cli(); // Global Interrupt Disabled
|
69 |
|
70 | // AD-Wandler wird eingeschaltet
|
71 | // Frequenzvorteiler Teilungsfaktor 32
|
72 | // bei 4Mhz Taktfrequenz der CPU
|
73 | //
|
74 | // AD_TAKT = CPU_TAKT / TEILUNGSFAKTOR = 4Mhz / 32 = |125kHz|
|
75 | // PERIODENDAUER = 1 / 125kHz = |8 mikroSec.|
|
76 | //
|
77 | // Es werden alle 8 mikroSec. neue werte vom AD-Wandler eingelesen.
|
78 | // Zusätzlich noch den ADC Interrupt (ADIE) einschalten zur
|
79 | // weiterverarbeitung für Sleep mode (ADC Noise Canceler)
|
80 | ADCSRA |= (1 << ADEN) | (1 << ADPS2) | (1 << ADPS0) | (1 << ADIE);
|
81 |
|
82 | set_sleep_mode(SLEEP_MODE_ADC); // ADC Sleep mode vorwählen
|
83 | //sleep_mode();
|
84 | sei(); // Global Interrupt Enabled
|
85 |
|
86 | ADMUX = mux; // Kanal waehlen
|
87 |
|
88 | // Nach Aktivieren des AD-Wandlers wird ein "Dummy-Readout" empfohlen,
|
89 | // man liest also einen Wert und verwirft diesen,
|
90 | // um den AD-Wandler "warmlaufen zu lassen".
|
91 | ADCSRA |= (1 << ADSC);
|
92 | while ( ADCSRA & (1 << ADSC))
|
93 | {
|
94 |
|
95 | }
|
96 |
|
97 | // 10Bit Ergenisregister ADCL und ADCH einlesen, und als 16Bit großen
|
98 | // Integer Wert speichern. Werte aus ADCL und ADCH rechtsbündig
|
99 | result |= ADCL + (ADCH << 8);
|
100 |
|
101 | // Messung - Arith. Mittelwert aus 64 aufeinanderfolgenden Wandlungen
|
102 | result = 0;
|
103 |
|
104 | for(i = 0; i < 64; i++)
|
105 | {
|
106 | ADCSRA |= (1 << ADSC); // eine Wandlung "single conversion"
|
107 | MyADCSRA &= ~(1 << ADIF); // ADC Interrupt Flag setzten
|
108 |
|
109 | while(!(MyADCSRA & (1 << ADIF))) // Solange Einlesen bis kein Interrupt Flag von ADC vorhanden
|
110 | {
|
111 |
|
112 | }
|
113 |
|
114 | result += ADCL + (ADCH << 8); // Nach jeder Wandlung Ergebnisse aufaddieren
|
115 | }
|
116 |
|
117 | ADCSRA &= ~(1 << ADEN); // Nach Wandlungen AD-Wandler deaktivieren
|
118 |
|
119 | result /= 64; // Summe der Wandlungen durch 64 teilen = arith. Mittelwert
|
120 | return result; // den arith. Mittelwert als Rückgabewert der Funktion ReadChannel
|
121 | }
|
122 |
|
123 | void status_LED(uint8_t color) // Zeigt den Zustand an
|
124 | {
|
125 | DDRB = 0x03; // Port (B) 1-3 als Ausgang
|
126 | PORTB = 0xFF; // Alle Led'S aus
|
127 |
|
128 | switch(color) // Variable "color" abfragen
|
129 | {
|
130 | case 1:
|
131 | // Rote LED leuchtet - Alarmzustand
|
132 | PORTB = status[0];
|
133 | break;
|
134 |
|
135 | case 2:
|
136 | // Grüne LED Leuchtet - Alles Super!
|
137 | PORTB = status[1];
|
138 | break;
|
139 | }
|
140 | }
|
141 |
|
142 | void _error(void)
|
143 | {
|
144 | //reti(); TODO: Ordentliche Fehlerroutine die Interrupts berücksichtigt
|
145 | PORTC = 0x10; // Einerstelle einschalten
|
146 | PORTD = 0x06; // E (ERROR) Ausgeben
|
147 | status_LED(1); // Status LED auf Rot (Fehler aufgetretet)
|
148 | }
|
149 |
|
150 | void PrintNumber(int number)
|
151 | {
|
152 | if(number < 10) // Einstellig
|
153 | {
|
154 | if(number < 0) // Wenn Zahl Negativ
|
155 | {
|
156 | number = -number; // Wenn Zahl negativ, durch Multiplizieren mit (-1) positiv machen
|
157 | }
|
158 |
|
159 | segment[0] = digit[ number % 10 ]; // Einerstelle bestimmen
|
160 | segment[1] = 0xFF; // Zehnerstelle leer lassen
|
161 | }
|
162 |
|
163 | if(number > 9) // Zweistellig
|
164 | {
|
165 |
|
166 | if(number > 99) // Wenn Zahl größer als 99, also dreistellig
|
167 | {
|
168 | _error(); // Ist ein Fehler aufgetretet Error-Funktion starten
|
169 | }
|
170 |
|
171 | else
|
172 | {
|
173 | segment[0] = digit[ number % 10 ]; // Einerstelle bestimmen
|
174 | segment[1] = digit[ number / 10 ]; // Zehnerstelle bestimmen
|
175 | }
|
176 | }
|
177 | }
|
178 |
|
179 | double Angle(uint16_t x, uint16_t y, uint16_t z)
|
180 | {
|
181 | double tilt; // Winkel in Degree (°)
|
182 |
|
183 | // x-Value | z-Value | Degree
|
184 | // 409.6 | 614.4 | 0°
|
185 | // 614.4 | 409.6 | 90°
|
186 |
|
187 | //tilt = (asin((x - 512.0) / 102.4)) * (180 / M_PI); // Einachsige Winkelbestimmung
|
188 | tilt = (atan((x - 512.0) / 102.4) / (((z - 512.0) / 102.4))) * (180 / M_PI); // Zweiachsige Winkelbestimmung
|
189 |
|
190 | return tilt; // Rückgabewert (Winkel) der Funktion
|
191 | }
|
192 |
|
193 | void USART_Init(unsigned int ubrr) // USART initialisieren
|
194 | {
|
195 | UBRRH = (unsigned char) (ubrr >> 8); // Baudrate in Register schreiben
|
196 | UBRRL = (unsigned char) ubrr;
|
197 |
|
198 | UCSRB = (1 << TXEN) | (1 << RXCIE); // UART TX einschalten, UART RX Complete Interrupt einschalten
|
199 | UCSRC = (1 << URSEL) | (3 << UCSZ0); // Frame Format einstellen (Asynchron 8N1)
|
200 | }
|
201 |
|
202 | void USART_puts(char *data_)
|
203 | {
|
204 | if(uart_tx_flag == 1)
|
205 | {
|
206 | strcpy(uart_tx_buffer, data_); // String Daten in den Sendepuffer kopieren
|
207 | uart_tx_flag = 0; // Flag für 'Senden ist komplett' löschen,
|
208 | UCSRB |= (1<<UDRIE); // UDRE Interrupt einschalten, los gehts
|
209 | }
|
210 | }
|
211 |
|
212 | int main(void)
|
213 | {
|
214 | char CSV[7];
|
215 | double ADC_angle;
|
216 |
|
217 | uint16_t accelx; // Messergebnisse des Kanals C0 (X) (0-1024)
|
218 | uint16_t accely; // Messergebnisse des Kanals C1 (Y) (0-1024)
|
219 | uint16_t accelz; // Messergebnisse des Kanals C2 (Z) (0-1024)
|
220 |
|
221 | // Eigenschaften der Ports C und D definieren
|
222 | DDRC = 0x18; // Nur die Ports 3-5 (C) auf Ausgänge schalten
|
223 | DDRD = 0xFD; // Port D Komplett als Ausgang für 7 Segment
|
224 |
|
225 | USART_Init(MYUBRR); // USART initialisieren
|
226 | cli(); // Global Interrupt Disabled
|
227 |
|
228 | wdt_enable(5); // Watchdog hält Wache im 500ms Zyklus
|
229 |
|
230 | TCCR0 |= (1 << CS00); // Vorteiler 1 (Takt 4Mhz)
|
231 | TIMSK |= (1 << TOIE0); // Timer 0 Overflow Interrupt
|
232 |
|
233 | sei(); // Globale Interrupts einschalten
|
234 |
|
235 | while(1)
|
236 | {
|
237 | wdt_reset(); // Watchdog Timer zurücksetzen
|
238 | status_LED(2); // Status LED auf Grün (Alles OK)
|
239 |
|
240 | accelx = ReadChannel(0); // 10Bit - Messwert der x-Achse
|
241 | accely = ReadChannel(1); // 10Bit - Messwert der y-Achse
|
242 | accelz = ReadChannel(2); // 10Bit - Messwert der z-Achse
|
243 |
|
244 | ADC_angle = Angle(accelx, accely, accelz); // Winkel in Variable verstauen
|
245 |
|
246 | PrintNumber(ADC_angle); // Wert ausgeben, ganzzahlig nicht negativ
|
247 | USART_puts(itoa(ADC_angle, CSV, 10)); // Daten auch seriell rausschicken
|
248 | }
|
249 |
|
250 | return 0; // Wird niemals erreicht
|
251 | }
|