1 | /***************************************************************************************************
|
2 | Identifikation *************************************************************************************
|
3 | ***************************************************************************************************/
|
4 | /*-------------------------------------------------------------------------------------------------
|
5 | Dateiname: $Workfile: test1.c $
|
6 | Version: $Revision: 1.0 $
|
7 | Copyright: maoukil
|
8 | Beschreibung: Geblinkt wird in PortB (push-pull) drei LEDs.
|
9 | -------------------------------------------------------------------------------------------------*/
|
10 |
|
11 | /***************************************************************************************************
|
12 | Includes *******************************************************************************************
|
13 | ***************************************************************************************************/
|
14 |
|
15 | #include <avr/io.h>
|
16 | #include <inttypes.h>
|
17 | #include <avr/interrupt.h>
|
18 | #include <avr/delay.h>
|
19 |
|
20 | #ifndef SIGNAL
|
21 | #include <avr/signal.h>
|
22 | #endif
|
23 |
|
24 | /*-------------------------------------------------------------------------------------------------
|
25 | Der MCU-Takt. Wird gebraucht, um Timer1 mit den richtigen
|
26 | Werten zu initialisieren. Voreinstellung ist 1MHz.
|
27 | (Werkseinstellung für AVRs mit internem Oszillator).
|
28 | Das Define wird nur gemacht, wenn F_CPU noch nicht definiert wurde.
|
29 | F_CPU kann man so auch per Kommandozeile definieren, z.B. für 8MHz:
|
30 | avr-gcc ... -DF_CPU=8000000
|
31 | Der Wert von F_CPU hat rein informativen Character für
|
32 | die korrekte Codeerzeugung im Programm!
|
33 | Um die Taktrate zu ändern müssen die Fuses des Controllers
|
34 | und/oder Quarz/Resonator/RC-Glied/Oszillator
|
35 | angepasst werden!
|
36 | -------------------------------------------------------------------------------------------------*/
|
37 | #ifndef F_CPU
|
38 | #define F_CPU 4000000
|
39 | #endif
|
40 |
|
41 | /***************************************************************************************************
|
42 | Defines und Makros *********************************************************************************
|
43 | ***************************************************************************************************/
|
44 |
|
45 | /*-------------------------------------------------------------------------------------------------
|
46 | So viele IRQs (Interrupt Request (IRQ)) werden jede Sekunde ausgelöst.
|
47 | Für optimale Genauigkeit muss
|
48 | IRQS_PER_SECOND ein Teiler von F_CPU sein
|
49 | und IRQS_PER_SECOND ein Vielfaches von 100.
|
50 | Ausserdem muss gelten F_CPU / IRQS_PER_SECOND <= 65536
|
51 | -------------------------------------------------------------------------------------------------*/
|
52 |
|
53 | #define LED_1 0
|
54 | #define LED_2 1
|
55 | #define LED_3 2
|
56 | #define PORT_LED PORTB
|
57 | #define DDR_LED DDRB // DDR: Definiere Port X als Ausgang
|
58 | #define IRQS_PER_SECOND 2000
|
59 | #define IRQS_PER_10MS (IRQS_PER_SECOND / 100) // Anzahl IRQs pro 10 Millisekunden
|
60 |
|
61 | /***************************************************************************************************
|
62 | Typdefinitionen ************************************************************************************
|
63 | ***************************************************************************************************/
|
64 |
|
65 |
|
66 |
|
67 | /***************************************************************************************************
|
68 | Konstanten *****************************************************************************************
|
69 | ***************************************************************************************************/
|
70 |
|
71 |
|
72 | /***************************************************************************************************
|
73 | Applikationsgroessen *******************************************************************************
|
74 | ***************************************************************************************************/
|
75 |
|
76 |
|
77 | /***************************************************************************************************
|
78 | Variablen ******************************************************************************************
|
79 | ***************************************************************************************************/
|
80 |
|
81 | static volatile uint8_t timer_10ms; // Zähler-Variable. Wird in der ISR (Interrupt Service Routine,)
|
82 | // erniedrigt und in wait_10ms benutzt.
|
83 |
|
84 | /***************************************************************************************************
|
85 | Funktionsprototypen für Modul-interne Funktionen ***************************************************
|
86 | ***************************************************************************************************/
|
87 |
|
88 | void wait_10ms (const uint8_t);
|
89 | void timer1_init();
|
90 |
|
91 | /***************************************************************************************************
|
92 | Hauptprogramm **************************************************************************************
|
93 | ***************************************************************************************************/
|
94 |
|
95 | int main()
|
96 | {
|
97 |
|
98 | DDR_LED |= (1 << LED_1); // LED-Port auf Ausgang
|
99 |
|
100 | timer1_init(); // Timer1 initialisieren
|
101 |
|
102 | sei(); // Interrupts aktivieren
|
103 |
|
104 | /*-------------------------------------------------------------------------------------------------
|
105 | Endlosschleife
|
106 | Die LED ist jeweils 1 Sekunde an und 1 Sekunde aus,
|
107 | blinkt also mit einer Frequenz von 0.5 Hz
|
108 | -------------------------------------------------------------------------------------------------*/
|
109 |
|
110 | while (1)
|
111 | {
|
112 |
|
113 | PORT_LED |= (1 << LED_1); // LED 1 an
|
114 |
|
115 | wait_10ms (200); // 1 Sekunde warten
|
116 |
|
117 | PORT_LED &= ~(1 << LED_1); // LED 1 aus
|
118 |
|
119 | wait_10ms (200); // 1 Sekunde warten
|
120 |
|
121 |
|
122 |
|
123 | PORT_LED |= (1 << LED_2); // LED 2 an
|
124 |
|
125 | wait_10ms (200); // 1 Sekunde warten
|
126 |
|
127 | PORT_LED &= ~(1 << LED_2); // LED 2 aus
|
128 |
|
129 | wait_10ms (200); // 1 Sekunde warten
|
130 |
|
131 |
|
132 |
|
133 | PORT_LED |= (1 << LED_3); // LED 3 an
|
134 |
|
135 | wait_10ms (200); // 1 Sekunde warten
|
136 |
|
137 | PORT_LED &= ~(1 << LED_3); // LED 3 aus
|
138 |
|
139 | wait_10ms (200); // 1 Sekunde warten
|
140 | }
|
141 |
|
142 | // main braucht keine return-Anweisung, weil wir nie hier hin kommen
|
143 | }
|
144 |
|
145 |
|
146 | /***************************************************************************************************
|
147 | Funktionen *****************************************************************************************
|
148 | ***************************************************************************************************/
|
149 |
|
150 | /*-------------------------------------------------------------------------------------------------
|
151 | Gültigkeitsprüfung.
|
152 | Bei ungeeigneten Werten gibt es einen Compilerfehler
|
153 | -------------------------------------------------------------------------------------------------*/
|
154 |
|
155 | #if (F_CPU/IRQS_PER_SECOND > 65536) || (IRQS_PER_10MS < 1) || (IRQS_PER_10MS > 255)
|
156 | # error Diese Werte fuer F_CPU und IRQS_PER_SECOND
|
157 | # error sind ausserhalb des gueltigen Bereichs!
|
158 | #endif
|
159 |
|
160 | /*-------------------------------------------------------------------------------------------------
|
161 | Compiler-Warnung falls die Genauigkeit nicht optimal ist.
|
162 | Wenn das nervt für deine Werte, einfach löschen :-)
|
163 | -------------------------------------------------------------------------------------------------*/
|
164 |
|
165 | #if (F_CPU % IRQS_PER_SECOND != 0) || (IRQS_PER_SECOND % 100 != 0)
|
166 | # warning Das Programm arbeitet nicht mit optimaler Genauigkeit.
|
167 | #endif
|
168 |
|
169 | // //////////////////////////////////////////////////////////////////////
|
170 | // Implementierungen der Funktionen
|
171 | // //////////////////////////////////////////////////////////////////////
|
172 |
|
173 | #if !defined (TCNT1H)
|
174 | #error Dieser Controller hat keinen 16-Bit Timer1!
|
175 | #endif // TCNT1H
|
176 |
|
177 |
|
178 | /***************************************************************************************************
|
179 | void timer1_init() ********************************************************************************
|
180 | ***************************************************************************************************/
|
181 |
|
182 | /*-------------------------------------------------------------------------------------------------
|
183 | Timer1 so initialisieren, daß er IRQS_PER_SECOND
|
184 | IRQs pro Sekunde erzeugt.
|
185 | -------------------------------------------------------------------------------------------------*/
|
186 |
|
187 | void timer1_init()
|
188 |
|
189 | {
|
190 | TCCR1A = 0; // Timer1: keine PWM. TCCR1A Timer/Counter 1 Control Register A
|
191 |
|
192 | /*-------------------------------------------------------------------------------------------------
|
193 | Timer1 ist Zähler: Clear Timer on Compare Match (CTC, Mode #4)
|
194 | Timer1 läuft mit vollem MCU-Takt: Prescale = 1
|
195 | -------------------------------------------------------------------------------------------------*/
|
196 |
|
197 | #if defined (CTC1) && !defined (WGM12)
|
198 | TCCR1B = (1 << CTC1) | (1 << CS10);
|
199 | #elif !defined (CTC1) && defined (WGM12)
|
200 | TCCR1B = (1 << WGM12) | (1 << CS10);
|
201 | #else
|
202 | #error Keine Ahnung, wie Timer1 fuer diesen AVR zu initialisieren ist!
|
203 | #endif
|
204 |
|
205 | /*-------------------------------------------------------------------------------------------------
|
206 | OutputCompare für gewünschte Timer1 Frequenz
|
207 | TCNT1 zählt immer 0...OCR1A, 0...OCR1A, ...
|
208 | Beim überlauf OCR1A -> OCR1A+1 wird TCNT1=0 gesetzt und im nächsten
|
209 | MCU-Takt eine IRQ erzeugt.
|
210 | -------------------------------------------------------------------------------------------------*/
|
211 |
|
212 | OCR1A = (unsigned short) ((unsigned long) F_CPU / IRQS_PER_SECOND-1);
|
213 |
|
214 | #if defined (TIMSK1) // OutputCompareA-Interrupt für Timer1 aktivieren
|
215 | TIMSK1 |= (1 << OCIE1A);
|
216 | #elif defined (TIMSK)
|
217 | TIMSK |= (1 << OCIE1A);
|
218 | #else
|
219 | #error Keine Ahnung, wie IRQs fuer diesen AVR zu initialisieren sind!
|
220 | #endif
|
221 |
|
222 | }
|
223 |
|
224 | /***************************************************************************************************
|
225 | void wait_10ms (const uint8_t t) *******************************************************************
|
226 | ***************************************************************************************************/
|
227 |
|
228 | /*-------------------------------------------------------------------------------------------------
|
229 | Wartet etwa t*10 ms.
|
230 | timer_10ms wird alle 10ms in der Timer1-ISR erniedrigt.
|
231 | Weil es bis zum nächsten IRQ nicht länger als 10ms dauert,
|
232 | wartet diese Funktion zwischen (t-1)*10 ms und t*10 ms.
|
233 | -------------------------------------------------------------------------------------------------*/
|
234 |
|
235 | void wait_10ms (const uint8_t t)
|
236 |
|
237 | {
|
238 | timer_10ms = t;
|
239 | while (timer_10ms);
|
240 | }
|
241 |
|
242 | /***************************************************************************************************
|
243 | Die Interrupt Service Routine (ISR) ****************************************************************
|
244 | ***************************************************************************************************/
|
245 | /*-------------------------------------------------------------------------------------------------
|
246 | Die Interrupt Service Routine (ISR).
|
247 | In interrupt_num_10ms werden die IRQs gezählt.
|
248 | Sind IRQS_PER_10MS Interrups geschehen,
|
249 | dann sind 10 ms vergangen.
|
250 | timer_10ms wird alle 10 ms um 1 vermindert und bleibt bei 0 stehen.
|
251 | -------------------------------------------------------------------------------------------------*/
|
252 |
|
253 | SIGNAL (SIG_OUTPUT_COMPARE1A)
|
254 | {
|
255 | static uint8_t interrupt_num_10ms;
|
256 |
|
257 | if (++interrupt_num_10ms == IRQS_PER_10MS) // interrupt_num_10ms erhöhen und mit M
|
258 | // aximalwert vergleichen
|
259 | {
|
260 |
|
261 | interrupt_num_10ms = 0; // 10 Millisekunden sind vorbei // interrupt_num_10ms zurücksetzen
|
262 |
|
263 | if (timer_10ms != 0) // Alle 10ms wird timer_10ms erniedrigt, falls es nicht schon 0 ist.
|
264 | timer_10ms--; // Wird verwendet in wait_10ms
|
265 | }
|
266 | }
|