summaryrefslogtreecommitdiffhomepage
path: root/digital/avr/common/io_bus.h
blob: 64d3e3144dcd3ee159e147ac624521212aaf2d1e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
#ifndef io_bus_h
#define io_bus_h
/* io_bus.h */
/* avr.modules - AVR modules. {{{
 *
 * Copyright (C) 2010 Nicolas Schodet
 *
 * APBTeam:
 *        Web: http://apbteam.org/
 *      Email: team AT apbteam DOT org
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 * }}} */
#include "preproc.h"
#include "io.h"

/** Macro to work with buses of several AVR IO.
 *
 * A bus is defined by one or several IO blocks.  Each IO block is defined by
 * its port, width and shift.  Here is an example:
 *
 *  A, 4, 0, B, 4, 2
 *
 * This defines a bus composed of two blocks.  The first block is composed of
 * the first four IO from port A, and the second one is composed of the IO 2,
 * 3, 4 and 5.  The bus have the following mapping:
 *
 *  0 => A0 (LSB)
 *  1 => A1
 *  2 => A2
 *  3 => A3
 *  4 => B2
 *  5 => B3
 *  6 => B4
 *  7 => B5 (MSB)
 *
 * Once a bus is defined, it can be accessed as a whole using macros from this
 * file:
 *
 *  IO_BUS_OUPUT (MY_BUS);
 *  IO_BUS_SET (MY_BUS, 42);
 *  IO_BUS_SET (MY_BUS, 0);
 *  IO_BUS_INPUT (MY_BUS)
 *  v = IO_BUS_GET (MY_BUS);
 *
 * Width and shift must expand to a single integer.  Currently limited to 3
 * blocks, that can be easily changed.  Total bus width is limited to 8 bit
 * (because of uint8_t temporary storage needed for right shift). */

/* Warning: C preprocessor tricks inside. */

/* Internal: Return a bit mask for the given width. */
#define IO_BUS_ONES(w) ((1u << (w)) - 1)

/* Internal: Return a bit mask for the given width and shift. */
#define IO_BUS_MASK(w, s) (IO_BUS_ONES ((w)) << (s))

/* Internal: Replace bits in old 8 bit register with value from new at the
 * given width and shift.  This is done to optimize the case when the whole
 * register is replaced and does not have to be read (the compiler can not
 * optimize that because registers are volatile).  Width must expand to a
 * single integer. */
#define IO_BUS_REPLACE(old, new, w, s) \
    PREPROC_PASTE (IO_BUS_REPLACE_, w) ((old), (new), (s))
#define IO_BUS_REPLACE_n(old, new, w, s) \
	(((old) & ~IO_BUS_MASK ((w), (s))) \
	 | (((new) & IO_BUS_ONES ((w))) << (s)))
#define IO_BUS_REPLACE_1(old, new, s) IO_BUS_REPLACE_n ((old), (new), 1, (s))
#define IO_BUS_REPLACE_2(old, new, s) IO_BUS_REPLACE_n ((old), (new), 2, (s))
#define IO_BUS_REPLACE_3(old, new, s) IO_BUS_REPLACE_n ((old), (new), 3, (s))
#define IO_BUS_REPLACE_4(old, new, s) IO_BUS_REPLACE_n ((old), (new), 4, (s))
#define IO_BUS_REPLACE_5(old, new, s) IO_BUS_REPLACE_n ((old), (new), 5, (s))
#define IO_BUS_REPLACE_6(old, new, s) IO_BUS_REPLACE_n ((old), (new), 6, (s))
#define IO_BUS_REPLACE_7(old, new, s) IO_BUS_REPLACE_n ((old), (new), 7, (s))
#define IO_BUS_REPLACE_8(old, new, s) (new)

/** Read value from bus. */
#define IO_BUS_GET(bus) \
    PREPROC_NARG_CALL (IO_BUS_GET_, bus)
#define IO_BUS_GET_3(p0, w0, s0) \
    ((PIN ## p0 >> (s0)) & IO_BUS_ONES ((w0)))
#define IO_BUS_GET_6(p0, w0, s0, p1, w1, s1) \
    (IO_BUS_GET_3 (p0, (w0), (s0)) \
     | (IO_BUS_GET_3 (p1, (w1), (s1)) << (w0)))
#define IO_BUS_GET_9(p0, w0, s0, p1, w1, s1, p2, w2, s2) \
    (IO_BUS_GET_6 (p0, (w0), (s0), p1, (w1), (s1)) \
     | (IO_BUS_GET_3 (p2, (w2), (s2)) << ((w0) + (w1))))

/** Write value to bus. */
#define IO_BUS_SET(bus, value) \
    PREPROC_NARG_CALL (IO_BUS_SET_, bus, (value))
#define IO_BUS_SET_4(p0, w0, s0, value) \
    do { \
	uint8_t _value1 = (value); \
	PORT ## p0 = IO_BUS_REPLACE (PORT ## p0, _value1, w0, (s0)); \
    } while (0)
#define IO_BUS_SET_7(p0, w0, s0, p1, w1, s1, value) \
    do { \
	uint8_t _value2 = (value); \
	IO_BUS_SET_4 (p0, w0, (s0), _value2); \
	IO_BUS_SET_4 (p1, w1, (s1), _value2 >> (w0)); \
    } while (0)
#define IO_BUS_SET_10(p0, w0, s0, p1, w1, s1, p2, w2, s2, value) \
    do { \
	uint8_t _value3 = (value); \
	IO_BUS_SET_7 (p0, w0, (s0), p1, w1, (s1), _value3); \
	IO_BUS_SET_4 (p2, w2, (s2), _value3 >> ((w0) + (w1))); \
    } while (0)

/** Set bus as input. */
#define IO_BUS_INPUT(bus) \
    PREPROC_NARG_CALL (IO_BUS_INPUT_, bus)
#define IO_BUS_INPUT_3(p0, w0, s0) \
    do { \
	DDR ## p0 = IO_BUS_REPLACE (DDR ## p0, 0, w0, (s0)); \
    } while (0)
#define IO_BUS_INPUT_6(p0, w0, s0, p1, w1, s1) \
    do { \
	IO_BUS_INPUT_3 (p0, w0, (s0)); \
	IO_BUS_INPUT_3 (p1, w1, (s1)); \
    } while (0)
#define IO_BUS_INPUT_9(p0, w0, s0, p1, w1, s1, p2, w2, s2) \
    do { \
	IO_BUS_INPUT_6 (p0, w0, (s0), p1, w1, (s1)); \
	IO_BUS_INPUT_3 (p2, w2, (s2)); \
    } while (0)

/** Set bus as output. */
#define IO_BUS_OUTPUT(bus) \
    PREPROC_NARG_CALL (IO_BUS_OUTPUT_, bus)
#define IO_BUS_OUTPUT_3(p0, w0, s0) \
    do { \
	DDR ## p0 = IO_BUS_REPLACE (DDR ## p0, IO_BUS_ONES (w0), w0, (s0)); \
    } while (0)
#define IO_BUS_OUTPUT_6(p0, w0, s0, p1, w1, s1) \
    do { \
	IO_BUS_OUTPUT_3 (p0, w0, (s0)); \
	IO_BUS_OUTPUT_3 (p1, w1, (s1)); \
    } while (0)
#define IO_BUS_OUTPUT_9(p0, w0, s0, p1, w1, s1, p2, w2, s2) \
    do { \
	IO_BUS_OUTPUT_6 (p0, w0, (s0), p1, w1, (s1)); \
	IO_BUS_OUTPUT_3 (p2, w2, (s2)); \
    } while (0)

#endif /* io_bus_h */