From 2f33fc36b3aa6b53d7e0b1827401303ae7d67c02 Mon Sep 17 00:00:00 2001 From: Nicolas Schodet Date: Fri, 24 Dec 2010 18:50:48 +0100 Subject: digital/avr/common: add bus macros --- digital/avr/common/io_bus.h | 159 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 159 insertions(+) create mode 100644 digital/avr/common/io_bus.h (limited to 'digital/avr/common/io_bus.h') diff --git a/digital/avr/common/io_bus.h b/digital/avr/common/io_bus.h new file mode 100644 index 00000000..64d3e314 --- /dev/null +++ b/digital/avr/common/io_bus.h @@ -0,0 +1,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 */ -- cgit v1.2.3