]> cloudbase.mooo.com Git - z180-stamp.git/blob - avr/gpio.c
Merge branch 'chan-fatfs' into fatfs-integration
[z180-stamp.git] / avr / gpio.c
1 /*
2 * (C) Copyright 2014 Leo C. <erbl259-lmu@yahoo.de>
3 *
4 * SPDX-License-Identifier: GPL-2.0+
5 */
6
7 #include "common.h"
8 #include <util/atomic.h>
9 #include <limits.h>
10 #include "debug.h"
11 #include "gpio.h"
12
13 /*
14
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
32 --------------------------------------------------
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
41 --------------------------------------------------
42 */
43
44 #define PWMTOGGLE 0b01
45 #define PWMPOS 0b10
46 #define PWMNEG 0b11
47
48
49 const FLASH uint8_t prescale_factors_01[] =
50 { 8, 8, 4, 4, 0 };
51
52 const FLASH uint8_t prescale_factors_2[] =
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
76 const FLASH struct pindef_s pinlist[GPIO_MAX] = {
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 };
89
90 void gpio_timer_off(uint8_t timertype)
91 {
92 uint8_t chan_mask;
93
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
124 int gpio_config(int pin, gpiomode_t mode)
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;
133
134 switch (mode) {
135 case INPUT:
136 gpio_timer_off(pinlist[pin].timer);
137 ATOMIC_BLOCK(ATOMIC_FORCEON) {
138 p->ddr &= ~bit;
139 p->pout &= ~bit;
140 }
141 break;
142 case INPUT_PULLUP:
143 gpio_timer_off(pinlist[pin].timer);
144 ATOMIC_BLOCK(ATOMIC_FORCEON) {
145 p->ddr &= ~bit;
146 p->pout |= bit;
147 }
148 break;
149 case OUTPUT:
150 gpio_timer_off(pinlist[pin].timer);
151 case OUTPUT_TIMER:
152 ATOMIC_BLOCK(ATOMIC_FORCEON) {
153 p->ddr |= bit;
154 }
155 break;
156 default:
157 /* Invalid pin mode */
158 return -1;
159 }
160 }
161 return 0;
162 }
163
164 void gpio_write(int pin, uint8_t val)
165 {
166 port_t *p = pinlist[pin].adr;
167 uint8_t bit = pinlist[pin].mask;
168
169 ATOMIC_BLOCK(ATOMIC_FORCEON) {
170 if (val)
171 p->pout |= bit;
172 else
173 p->pout &= ~bit;
174 }
175 }
176
177 int gpio_read(int pin)
178 {
179 port_t *p = pinlist[pin].adr;
180 uint8_t bit = pinlist[pin].mask;
181
182 return (p->pin & bit) != 0;
183 }
184
185 gpiomode_t gpio_config_get(int pin)
186 {
187 uint8_t timertype = pinlist[pin].timer;
188
189 if (timertype & TIMER) {
190
191 uint8_t chan_mask;
192 if (timertype & CHANA)
193 chan_mask = 0xc0;
194 else
195 chan_mask = 0x30;
196
197 switch (timertype & TIMER) {
198 case TIMER0:
199 if (TCCR0A & chan_mask)
200 return OUTPUT_TIMER;
201 break;
202 case TIMER1:
203 if (TCCR1A & chan_mask)
204 return OUTPUT_TIMER;
205 break;
206 case TIMER2:
207 if (TCCR2A & chan_mask)
208 return OUTPUT_TIMER;
209 break;
210 }
211 }
212
213 port_t *p = pinlist[pin].adr;
214 uint8_t bit = pinlist[pin].mask;
215
216 if (p->ddr & bit)
217 return OUTPUT;
218
219 if (p->pout & bit)
220 return INPUT_PULLUP;
221
222 return INPUT;
223 }
224
225 /*
226 * return -1: pin has no timer output
227 * 0: pin is not configured for timer output
228 * > 0: divider
229 */
230
231 long gpio_clockdiv_get(int pin)
232 {
233 long divider;
234 uint8_t prescale;
235 const FLASH uint8_t *pstab;
236
237 uint8_t timertype = pinlist[pin].timer;
238 if ((timertype & TIMER) == 0)
239 return -1;
240
241 if (gpio_config_get(pin) != OUTPUT_TIMER)
242 return 0;
243
244 switch (timertype & TIMER) {
245 case TIMER0:
246 prescale = TCCR0B;
247 divider = OCR0A;
248 break;
249
250 case TIMER1:
251 prescale = TCCR1B;
252 divider = ICR1;
253 break;
254
255 case TIMER2:
256 prescale = TCCR2B;
257 divider = OCR2A;
258 break;
259 }
260
261 prescale = (prescale & 0x07) - 1;
262 divider += 1;
263
264 pstab = (timertype & TIMER) == TIMER2 ?
265 prescale_factors_2 : prescale_factors_01;
266
267 while (prescale--)
268 divider *= pstab[prescale];
269
270 if ((timertype & (CHANA|T_16BIT)) == CHANA)
271 divider *= 2;
272
273 return divider;
274 }
275
276 int gpio_clockdiv_set(int pin, unsigned long divider)
277 {
278 unsigned long ltop;
279 uint16_t top;
280 uint8_t prescale;
281 const FLASH uint8_t *pstab;
282
283 uint8_t timertype = pinlist[pin].timer;
284 if ((timertype & TIMER) == 0)
285 return 0;
286
287 if (divider < 2)
288 return -1;
289
290 ltop = divider;
291 if ((timertype & (CHANA|T_16BIT)) == CHANA)
292 ltop /= 2;
293
294 if (ltop > 1024 * ((timertype & T_16BIT) ? (1L<<16) : (1L<<8)))
295 return -1;
296
297 prescale = 1;
298 pstab = (timertype & TIMER) == TIMER2 ?
299 prescale_factors_2 : prescale_factors_01;
300
301 // debug("** clockdiv_set: pin: %d, ltop: %lu, prescale: %d\n",
302 // pin, ltop, prescale);
303
304 while (ltop > ((timertype & T_16BIT) ? (1L<<16) : (1L<<8))) {
305 // debug("** clockdiv_set: pin: %d, ltop: %lu, prescale: %d, *pstab %d\n",
306 // pin, ltop, prescale, *pstab);
307
308 if (*pstab == 0)
309 return -1;
310 ltop /= *pstab++;
311 prescale++;
312 }
313
314 if (ltop == 0)
315 return -1;
316
317 top = ltop - 1;
318
319 // PING |= _BV(0); /* Debug */
320
321 switch (timertype & TIMER) {
322 case TIMER0:
323 PRR0 &= ~_BV(PRTIM0);
324 TCCR0B = (1 << WGM02);
325 TCNT0 = 0;
326 OCR0A = top;
327 if (timertype & CHANA) {
328 TCCR0A = (PWMTOGGLE << COM0A0) | (0b11 << WGM00);
329 } else {
330 OCR0B = top/2;
331 TCCR0A = (PWMPOS << COM0B0) | (0b11 << WGM10);
332 }
333 TCCR0B = (1 << WGM02) | (prescale << CS10);
334 break;
335
336 case TIMER1:
337 PRR0 &= ~_BV(PRTIM1);
338 TCCR1B = (0b11 << WGM12);
339 TCNT1 = 0;
340 ICR1 = top;
341 if (timertype & CHANA) {
342 OCR1A = top/2;
343 TCCR1A = (PWMPOS << COM1A0) | (0b10 << WGM10);
344 } else {
345 OCR1B = top/2;
346 TCCR1A = (PWMPOS << COM1B0) | (0b10 << WGM10);
347 }
348 // debug("pin: %d, top: %u,"
349 // " ICR1: %u, OCR1A: %u, OCR1B: %u\n",
350 // pin, top, ICR1, OCR1A, OCR1B);
351
352 TCCR1B = (0b11 << WGM12) | (prescale << CS10);
353 break;
354
355 case TIMER2:
356 PRR0 &= ~_BV(PRTIM2);
357 TCCR2B = (1 << WGM22);
358 TCNT2 = 0;
359 OCR2A = top;
360 if (timertype & CHANA) {
361 TCCR2A = (PWMTOGGLE << COM2A0) | (0b11 << WGM20);
362 } else {
363 OCR2B = top/2;
364 TCCR2A = (PWMPOS << COM2B0) | (0b11 << WGM10);
365 }
366 TCCR2B = (1 << WGM22) | (prescale << CS10);
367 break;
368 }
369
370 // PING |= _BV(0); /* Debug */
371
372 gpio_config(pin, OUTPUT_TIMER);
373
374 return 0;
375 }