]>
Commit | Line | Data |
---|---|---|
35edb766 L |
1 | /* |
2 | * (C) Copyright 2014 Leo C. <erbl259-lmu@yahoo.de> | |
3 | * | |
4 | * SPDX-License-Identifier: GPL-2.0+ | |
5 | */ | |
6 | ||
41d36f28 L |
7 | #include "common.h" |
8 | #include <util/atomic.h> | |
9 | #include <limits.h> | |
10 | #include "debug.h" | |
05994bd9 | 11 | #include "gpio.h" |
41d36f28 | 12 | |
41d36f28 L |
13 | /* |
14 | ||
15e476bc L |
15 | Pin Name Port Timer Mode max div max div min f [Hz] |
16 | -------------------------------------------------------------------------------- | |
17 | 0 PG5 OC0B PWM (2**8)*1024 262144 70.31 | |
18 | 1 PG4 | |
19 | 2 CLK2 PB4 OC2A Toggle (2**8)*1024*2 524288 35.16 | |
20 | 3 ZCLK PB5 OC1A PWM (2**16)*1024 67108864 0.2746 | |
21 | 4 PB6 OC1B PWM (2**16)*1024 67108864 0.2746 | |
22 | 5 PB7 OC0A Toggle (2**8)*1024*2 524288 35.16 | |
23 | 6 PG3 | |
24 | 7 PG2 | |
25 | 8 PG1 | |
26 | 9 PG0 | |
27 | 10 CLKO PE7 | |
28 | -------------------------------------------------------------------------------- | |
29 | ||
30 | ||
31 | pre Timer0 Timer1 Timer2 | |
41d36f28 | 32 | -------------------------------------------------- |
15e476bc L |
33 | 0 0 0 0 |
34 | 1 1 1 1 | |
35 | 2 8 x8 8 x8 8 x8 | |
36 | 3 64 x8 64 x8 32 x4 | |
37 | 4 256 x4 256 x4 64 x2 | |
38 | 5 1024 x4 1024 x4 128 x2 | |
39 | 6 256 x2 | |
40 | 7 1024 x4 | |
05994bd9 | 41 | -------------------------------------------------- |
41d36f28 L |
42 | */ |
43 | ||
41d36f28 L |
44 | #define PWMTOGGLE 0b01 |
45 | #define PWMPOS 0b10 | |
46 | #define PWMNEG 0b11 | |
47 | ||
48 | ||
05994bd9 | 49 | const FLASH uint8_t prescale_factors_01[] = |
41d36f28 L |
50 | { 8, 8, 4, 4, 0 }; |
51 | ||
05994bd9 | 52 | const FLASH uint8_t prescale_factors_2[] = |
41d36f28 L |
53 | { 8, 4, 2, 2, 2, 4, 0 }; |
54 | ||
55 | typedef volatile struct { | |
56 | uint8_t pin; | |
57 | uint8_t ddr; | |
58 | uint8_t pout; | |
59 | } port_t ; | |
60 | ||
61 | struct pindef_s { | |
62 | port_t * const adr; | |
63 | const uint8_t mask; | |
64 | #define NO_TIMER 0 | |
65 | #define TIMER0 (1 << 0) | |
66 | #define TIMER1 (2 << 0) | |
67 | #define TIMER2 (3 << 0) | |
68 | #define TIMER (3 << 0) | |
69 | #define T_16BIT (1 << 3) | |
70 | #define CHANA (1 << 4) | |
71 | #define CHANB (0 << 4) | |
72 | const uint8_t timer; | |
73 | }; | |
74 | ||
75 | ||
05994bd9 | 76 | const FLASH struct pindef_s pinlist[GPIO_MAX] = { |
41d36f28 L |
77 | { (port_t *) &PING, _BV(5), TIMER0 | CHANB }, |
78 | { (port_t *) &PING, _BV(4), NO_TIMER }, | |
79 | { (port_t *) &PINB, _BV(4), TIMER2 | CHANA }, | |
80 | { (port_t *) &PINB, _BV(5), TIMER1 | CHANA | T_16BIT }, | |
81 | { (port_t *) &PINB, _BV(6), TIMER1 | CHANB | T_16BIT }, | |
82 | { (port_t *) &PINB, _BV(7), TIMER0 | CHANA }, | |
83 | { (port_t *) &PING, _BV(3), NO_TIMER }, | |
84 | { (port_t *) &PING, _BV(2), NO_TIMER }, | |
85 | { (port_t *) &PING, _BV(1), NO_TIMER }, | |
86 | { (port_t *) &PING, _BV(0), NO_TIMER }, | |
87 | { (port_t *) &PINE, _BV(7), NO_TIMER }, | |
88 | }; | |
05994bd9 L |
89 | |
90 | void gpio_timer_off(uint8_t timertype) | |
41d36f28 L |
91 | { |
92 | uint8_t chan_mask; | |
05994bd9 | 93 | |
41d36f28 L |
94 | if (timertype & CHANA) |
95 | chan_mask = 0xc0; | |
96 | else | |
97 | chan_mask = 0x30; | |
98 | ||
99 | switch (timertype & TIMER) { | |
100 | case TIMER0: | |
101 | if (TCCR0A & chan_mask) { | |
102 | TCCR0B = 0; | |
103 | TCCR0A = 0; | |
104 | PRR0 |= _BV(PRTIM0); | |
105 | } | |
106 | break; | |
107 | case TIMER1: | |
108 | if (TCCR1A & chan_mask) { | |
109 | TCCR1B = 0; | |
110 | TCCR1A = 0; | |
111 | PRR0 |= _BV(PRTIM1); | |
112 | } | |
113 | break; | |
114 | case TIMER2: | |
115 | if (TCCR2A & chan_mask) { | |
116 | TCCR2B = 0; | |
117 | TCCR2A = 0; | |
118 | PRR0 |= _BV(PRTIM2); | |
119 | } | |
120 | break; | |
121 | } | |
122 | } | |
123 | ||
05994bd9 | 124 | int gpio_config(int pin, gpiomode_t mode) |
41d36f28 L |
125 | { |
126 | if ((unsigned) pin >= ARRAY_SIZE(pinlist)) { | |
127 | /* Invalid pin number */ | |
128 | return -1; | |
129 | } else { | |
130 | ||
131 | port_t *p = pinlist[pin].adr; | |
132 | uint8_t bit = pinlist[pin].mask; | |
05994bd9 | 133 | |
41d36f28 L |
134 | switch (mode) { |
135 | case INPUT: | |
05994bd9 | 136 | gpio_timer_off(pinlist[pin].timer); |
8a7decea | 137 | ATOMIC_BLOCK(ATOMIC_FORCEON) { |
41d36f28 L |
138 | p->ddr &= ~bit; |
139 | p->pout &= ~bit; | |
140 | } | |
141 | break; | |
142 | case INPUT_PULLUP: | |
05994bd9 | 143 | gpio_timer_off(pinlist[pin].timer); |
8a7decea | 144 | ATOMIC_BLOCK(ATOMIC_FORCEON) { |
41d36f28 L |
145 | p->ddr &= ~bit; |
146 | p->pout |= bit; | |
147 | } | |
148 | break; | |
149 | case OUTPUT: | |
05994bd9 | 150 | gpio_timer_off(pinlist[pin].timer); |
49475345 | 151 | /* FALL TROUGH */ |
41d36f28 | 152 | case OUTPUT_TIMER: |
8a7decea | 153 | ATOMIC_BLOCK(ATOMIC_FORCEON) { |
41d36f28 L |
154 | p->ddr |= bit; |
155 | } | |
156 | break; | |
157 | default: | |
158 | /* Invalid pin mode */ | |
159 | return -1; | |
160 | } | |
161 | } | |
162 | return 0; | |
163 | } | |
164 | ||
05994bd9 | 165 | void gpio_write(int pin, uint8_t val) |
41d36f28 L |
166 | { |
167 | port_t *p = pinlist[pin].adr; | |
168 | uint8_t bit = pinlist[pin].mask; | |
05994bd9 | 169 | |
8a7decea | 170 | ATOMIC_BLOCK(ATOMIC_FORCEON) { |
41d36f28 L |
171 | if (val) |
172 | p->pout |= bit; | |
173 | else | |
174 | p->pout &= ~bit; | |
175 | } | |
176 | } | |
177 | ||
05994bd9 | 178 | int gpio_read(int pin) |
41d36f28 L |
179 | { |
180 | port_t *p = pinlist[pin].adr; | |
181 | uint8_t bit = pinlist[pin].mask; | |
05994bd9 | 182 | |
41d36f28 L |
183 | return (p->pin & bit) != 0; |
184 | } | |
185 | ||
05994bd9 | 186 | gpiomode_t gpio_config_get(int pin) |
41d36f28 L |
187 | { |
188 | uint8_t timertype = pinlist[pin].timer; | |
189 | ||
190 | if (timertype & TIMER) { | |
05994bd9 | 191 | |
41d36f28 L |
192 | uint8_t chan_mask; |
193 | if (timertype & CHANA) | |
194 | chan_mask = 0xc0; | |
195 | else | |
196 | chan_mask = 0x30; | |
197 | ||
198 | switch (timertype & TIMER) { | |
199 | case TIMER0: | |
200 | if (TCCR0A & chan_mask) | |
201 | return OUTPUT_TIMER; | |
202 | break; | |
203 | case TIMER1: | |
204 | if (TCCR1A & chan_mask) | |
205 | return OUTPUT_TIMER; | |
206 | break; | |
207 | case TIMER2: | |
208 | if (TCCR2A & chan_mask) | |
209 | return OUTPUT_TIMER; | |
210 | break; | |
211 | } | |
212 | } | |
05994bd9 | 213 | |
41d36f28 L |
214 | port_t *p = pinlist[pin].adr; |
215 | uint8_t bit = pinlist[pin].mask; | |
216 | ||
217 | if (p->ddr & bit) | |
218 | return OUTPUT; | |
05994bd9 | 219 | |
41d36f28 L |
220 | if (p->pout & bit) |
221 | return INPUT_PULLUP; | |
05994bd9 | 222 | |
41d36f28 L |
223 | return INPUT; |
224 | } | |
05994bd9 | 225 | |
41d36f28 L |
226 | /* |
227 | * return -1: pin has no timer output | |
228 | * 0: pin is not configured for timer output | |
229 | * > 0: divider | |
230 | */ | |
05994bd9 L |
231 | |
232 | long gpio_clockdiv_get(int pin) | |
41d36f28 L |
233 | { |
234 | long divider; | |
235 | uint8_t prescale; | |
236 | const FLASH uint8_t *pstab; | |
237 | ||
238 | uint8_t timertype = pinlist[pin].timer; | |
239 | if ((timertype & TIMER) == 0) | |
240 | return -1; | |
241 | ||
05994bd9 | 242 | if (gpio_config_get(pin) != OUTPUT_TIMER) |
41d36f28 | 243 | return 0; |
05994bd9 | 244 | |
41d36f28 L |
245 | switch (timertype & TIMER) { |
246 | case TIMER0: | |
247 | prescale = TCCR0B; | |
248 | divider = OCR0A; | |
249 | break; | |
250 | ||
251 | case TIMER1: | |
252 | prescale = TCCR1B; | |
253 | divider = ICR1; | |
254 | break; | |
255 | ||
256 | case TIMER2: | |
257 | prescale = TCCR2B; | |
258 | divider = OCR2A; | |
259 | break; | |
260 | } | |
261 | ||
262 | prescale = (prescale & 0x07) - 1; | |
263 | divider += 1; | |
264 | ||
05994bd9 | 265 | pstab = (timertype & TIMER) == TIMER2 ? |
41d36f28 | 266 | prescale_factors_2 : prescale_factors_01; |
05994bd9 | 267 | |
41d36f28 L |
268 | while (prescale--) |
269 | divider *= pstab[prescale]; | |
05994bd9 | 270 | |
41d36f28 L |
271 | if ((timertype & (CHANA|T_16BIT)) == CHANA) |
272 | divider *= 2; | |
05994bd9 | 273 | |
41d36f28 L |
274 | return divider; |
275 | } | |
276 | ||
05994bd9 | 277 | int gpio_clockdiv_set(int pin, unsigned long divider) |
41d36f28 L |
278 | { |
279 | unsigned long ltop; | |
280 | uint16_t top; | |
281 | uint8_t prescale; | |
282 | const FLASH uint8_t *pstab; | |
283 | ||
284 | uint8_t timertype = pinlist[pin].timer; | |
285 | if ((timertype & TIMER) == 0) | |
286 | return 0; | |
287 | ||
288 | if (divider < 2) | |
289 | return -1; | |
05994bd9 | 290 | |
41d36f28 L |
291 | ltop = divider; |
292 | if ((timertype & (CHANA|T_16BIT)) == CHANA) | |
293 | ltop /= 2; | |
05994bd9 | 294 | |
41d36f28 L |
295 | if (ltop > 1024 * ((timertype & T_16BIT) ? (1L<<16) : (1L<<8))) |
296 | return -1; | |
297 | ||
298 | prescale = 1; | |
05994bd9 | 299 | pstab = (timertype & TIMER) == TIMER2 ? |
41d36f28 | 300 | prescale_factors_2 : prescale_factors_01; |
05994bd9 | 301 | |
41d36f28 L |
302 | // debug("** clockdiv_set: pin: %d, ltop: %lu, prescale: %d\n", |
303 | // pin, ltop, prescale); | |
304 | ||
305 | while (ltop > ((timertype & T_16BIT) ? (1L<<16) : (1L<<8))) { | |
306 | // debug("** clockdiv_set: pin: %d, ltop: %lu, prescale: %d, *pstab %d\n", | |
307 | // pin, ltop, prescale, *pstab); | |
308 | ||
309 | if (*pstab == 0) | |
310 | return -1; | |
311 | ltop /= *pstab++; | |
312 | prescale++; | |
313 | } | |
314 | ||
315 | if (ltop == 0) | |
316 | return -1; | |
317 | ||
318 | top = ltop - 1; | |
319 | ||
15e476bc | 320 | // PING |= _BV(0); /* Debug */ |
41d36f28 L |
321 | |
322 | switch (timertype & TIMER) { | |
323 | case TIMER0: | |
324 | PRR0 &= ~_BV(PRTIM0); | |
325 | TCCR0B = (1 << WGM02); | |
326 | TCNT0 = 0; | |
327 | OCR0A = top; | |
328 | if (timertype & CHANA) { | |
329 | TCCR0A = (PWMTOGGLE << COM0A0) | (0b11 << WGM00); | |
330 | } else { | |
331 | OCR0B = top/2; | |
332 | TCCR0A = (PWMPOS << COM0B0) | (0b11 << WGM10); | |
333 | } | |
334 | TCCR0B = (1 << WGM02) | (prescale << CS10); | |
335 | break; | |
336 | ||
337 | case TIMER1: | |
338 | PRR0 &= ~_BV(PRTIM1); | |
339 | TCCR1B = (0b11 << WGM12); | |
340 | TCNT1 = 0; | |
341 | ICR1 = top; | |
342 | if (timertype & CHANA) { | |
343 | OCR1A = top/2; | |
344 | TCCR1A = (PWMPOS << COM1A0) | (0b10 << WGM10); | |
345 | } else { | |
346 | OCR1B = top/2; | |
347 | TCCR1A = (PWMPOS << COM1B0) | (0b10 << WGM10); | |
348 | } | |
349 | // debug("pin: %d, top: %u," | |
350 | // " ICR1: %u, OCR1A: %u, OCR1B: %u\n", | |
351 | // pin, top, ICR1, OCR1A, OCR1B); | |
352 | ||
353 | TCCR1B = (0b11 << WGM12) | (prescale << CS10); | |
354 | break; | |
355 | ||
356 | case TIMER2: | |
357 | PRR0 &= ~_BV(PRTIM2); | |
358 | TCCR2B = (1 << WGM22); | |
359 | TCNT2 = 0; | |
360 | OCR2A = top; | |
361 | if (timertype & CHANA) { | |
362 | TCCR2A = (PWMTOGGLE << COM2A0) | (0b11 << WGM20); | |
363 | } else { | |
364 | OCR2B = top/2; | |
365 | TCCR2A = (PWMPOS << COM2B0) | (0b11 << WGM10); | |
366 | } | |
367 | TCCR2B = (1 << WGM22) | (prescale << CS10); | |
368 | break; | |
369 | } | |
370 | ||
15e476bc | 371 | // PING |= _BV(0); /* Debug */ |
41d36f28 | 372 | |
05994bd9 | 373 | gpio_config(pin, OUTPUT_TIMER); |
41d36f28 L |
374 | |
375 | return 0; | |
376 | } |