]> cloudbase.mooo.com Git - z180-stamp.git/blob - avr/gpio.c
-Wimplicit-fallthrough=1
[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 /* FALL TROUGH */
152 case OUTPUT_TIMER:
153 ATOMIC_BLOCK(ATOMIC_FORCEON) {
154 p->ddr |= bit;
155 }
156 break;
157 default:
158 /* Invalid pin mode */
159 return -1;
160 }
161 }
162 return 0;
163 }
164
165 void gpio_write(int pin, uint8_t val)
166 {
167 port_t *p = pinlist[pin].adr;
168 uint8_t bit = pinlist[pin].mask;
169
170 ATOMIC_BLOCK(ATOMIC_FORCEON) {
171 if (val)
172 p->pout |= bit;
173 else
174 p->pout &= ~bit;
175 }
176 }
177
178 int gpio_read(int pin)
179 {
180 port_t *p = pinlist[pin].adr;
181 uint8_t bit = pinlist[pin].mask;
182
183 return (p->pin & bit) != 0;
184 }
185
186 gpiomode_t gpio_config_get(int pin)
187 {
188 uint8_t timertype = pinlist[pin].timer;
189
190 if (timertype & TIMER) {
191
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 }
213
214 port_t *p = pinlist[pin].adr;
215 uint8_t bit = pinlist[pin].mask;
216
217 if (p->ddr & bit)
218 return OUTPUT;
219
220 if (p->pout & bit)
221 return INPUT_PULLUP;
222
223 return INPUT;
224 }
225
226 /*
227 * return -1: pin has no timer output
228 * 0: pin is not configured for timer output
229 * > 0: divider
230 */
231
232 long gpio_clockdiv_get(int pin)
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
242 if (gpio_config_get(pin) != OUTPUT_TIMER)
243 return 0;
244
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
265 pstab = (timertype & TIMER) == TIMER2 ?
266 prescale_factors_2 : prescale_factors_01;
267
268 while (prescale--)
269 divider *= pstab[prescale];
270
271 if ((timertype & (CHANA|T_16BIT)) == CHANA)
272 divider *= 2;
273
274 return divider;
275 }
276
277 int gpio_clockdiv_set(int pin, unsigned long divider)
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;
290
291 ltop = divider;
292 if ((timertype & (CHANA|T_16BIT)) == CHANA)
293 ltop /= 2;
294
295 if (ltop > 1024 * ((timertype & T_16BIT) ? (1L<<16) : (1L<<8)))
296 return -1;
297
298 prescale = 1;
299 pstab = (timertype & TIMER) == TIMER2 ?
300 prescale_factors_2 : prescale_factors_01;
301
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
320 // PING |= _BV(0); /* Debug */
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
371 // PING |= _BV(0); /* Debug */
372
373 gpio_config(pin, OUTPUT_TIMER);
374
375 return 0;
376 }