ARM: also set GPIO function and mode.

This also implements more of the FastIO infrastructure.
Unfortunately, definitions aren't exactly straightforward, so we
need lots of tabular data. For example, for the pin function I
I had to step through the user manual, pin by pin.

We also learned a lesson here: Cortex-M0 has a 4 word ( = 16 bytes)
prefetch engine. Loops not starting at such a boundary take
additional 4 clock cycles, making them slower. The tight loop used
for testing previously happened to be 16-byte aligne by accident.
Adding just one line of code in the SET_OUTPUT() macro misaligned
it, so loop repetition rate dropped from 5.3 MHz to 3.7 MHz.

There are many measures to align code to 16-byte boundaries:

 - -falign-functions=16 as gcc flag.

 - -falign-loops=16 as gcc flag, found to not work.

 - -falign-labels=16 as gcc flag, worked for aligning the loop,
   but also bloated the binary by 10%.

 - __attribute__ ((aligned(16))) attached to functions (not
   tested)

 - Adding this just before the loop worked fine and increased the
   binary by just 16 bytes:

     __ASM (".balign 16");

Take care of this when relying on exact execution times, e.g. when
implementing delay_us()!
This commit is contained in:
Markus Hitter 2015-07-23 13:48:40 +02:00
parent c66bf0a8de
commit 072e3f8ae5
2 changed files with 93 additions and 1 deletions

View File

@ -37,74 +37,152 @@
#define IO_MIS_OFFSET 0x8018 // Masked interrupt status register.
#define IO_IC_OFFSET 0x801C // Interrupt clear register.
/**
Masks to handle the pulling function. All IOCON registers for pins do this on
bits [4:3]. 'Or' them with xxx_OUTPUT (see below) to get the requested
functionality.
Note that PIO0_4 and PIO0_5 are always open drain, no pullup possible. Mask
bits are "reserved" on these two.
*/
#define IO_MODEMASK_INACTIVE (0x00 << 3)
#define IO_MODEMASK_PULLDOWN (0x01 << 3)
#define IO_MODEMASK_PULLUP (0x02 << 3)
#define IO_MODEMASK_REPEATER (0x03 << 3)
/**
We define only pins available on the DIP28/TSSOP28 package here, because
the LPC1114FN28/102 is currently the only LPC1114 known to be used in a
RepRap controller.
RepRap controller. We also use pin names as defined in the User Manual, for
(hopefully) least disambiguation.
Description:
xxx_CMSIS Stupid MBED uses an inconsistent naming scheme in
LPC_IOCON_TypeDef, so we have to map these names to the
standard naming pattern.
xxx_PIN Pin number. Can be used to calculate the port mask and also
the bitbanding address.
xxx_PORT Base address of the corresponding GPIO register. For offsets
see above.
xxx_OUTPUT Bits to set/clear to set this pin as GPIO output in the IOCON
register. See chapter 7.4 of the User Manual. FUNC as I/O pin,
MODE inactive (no pullup or pulldown), no hysteresis, no
analog function, no open drain.
To achieve appropriate pulling behaviour for inputs, 'or'
this value with one of the IO_MODEMASKs.
*/
// Reset pin. Don't use.
//#define PIO0_0_CMSIS RESET_PIO0_0
//#define PIO0_0_PIN 0
//#define PIO0_0_PORT LPC_GPIO0
//#define PIO0_0_OUTPUT (0x01 << 0)
#define PIO0_1_CMSIS PIO0_1
#define PIO0_1_PIN 1
#define PIO0_1_PORT LPC_GPIO0
#define PIO0_1_OUTPUT 0x00
#define PIO0_2_CMSIS PIO0_2
#define PIO0_2_PIN 2
#define PIO0_2_PORT LPC_GPIO0
#define PIO0_2_OUTPUT 0x00
#define PIO0_3_CMSIS PIO0_3
#define PIO0_3_PIN 3
#define PIO0_3_PORT LPC_GPIO0
#define PIO0_3_OUTPUT 0x00
#define PIO0_4_CMSIS PIO0_4
#define PIO0_4_PIN 4
#define PIO0_4_PORT LPC_GPIO0
#define PIO0_4_OUTPUT (0x01 << 8)
#define PIO0_5_CMSIS PIO0_5
#define PIO0_5_PIN 5
#define PIO0_5_PORT LPC_GPIO0
#define PIO0_5_OUTPUT (0x01 << 8)
#define PIO0_6_CMSIS PIO0_6
#define PIO0_6_PIN 6
#define PIO0_6_PORT LPC_GPIO0
#define PIO0_6_OUTPUT 0x00
#define PIO0_7_CMSIS PIO0_7
#define PIO0_7_PIN 7
#define PIO0_7_PORT LPC_GPIO0
#define PIO0_7_OUTPUT 0x00
#define PIO0_8_CMSIS PIO0_8
#define PIO0_8_PIN 8
#define PIO0_8_PORT LPC_GPIO0
#define PIO0_8_OUTPUT 0x00
#define PIO0_9_CMSIS PIO0_9
#define PIO0_9_PIN 9
#define PIO0_9_PORT LPC_GPIO0
#define PIO0_9_OUTPUT 0x00
#define PIO0_10_CMSIS SWCLK_PIO0_10
#define PIO0_10_PIN 10
#define PIO0_10_PORT LPC_GPIO0
#define PIO0_10_OUTPUT (0x01 << 0)
#define PIO0_11_CMSIS R_PIO0_11
#define PIO0_11_PIN 11
#define PIO0_11_PORT LPC_GPIO0
#define PIO0_11_OUTPUT ((0x01 << 0) | (0x01 << 7))
#define PIO1_0_CMSIS R_PIO1_0
#define PIO1_0_PIN 0
#define PIO1_0_PORT LPC_GPIO1
#define PIO1_0_OUTPUT ((0x01 << 0) | (0x01 << 7))
#define PIO1_1_CMSIS R_PIO1_1
#define PIO1_1_PIN 1
#define PIO1_1_PORT LPC_GPIO1
#define PIO1_1_OUTPUT ((0x01 << 0) | (0x01 << 7))
#define PIO1_2_CMSIS R_PIO1_2
#define PIO1_2_PIN 2
#define PIO1_2_PORT LPC_GPIO1
#define PIO1_2_OUTPUT ((0x01 << 0) | (0x01 << 7))
#define PIO1_3_CMSIS SWDIO_PIO1_3
#define PIO1_3_PIN 3
#define PIO1_3_PORT LPC_GPIO1
#define PIO1_3_OUTPUT ((0x01 << 0) | (0x01 << 7))
#define PIO1_4_CMSIS PIO1_4
#define PIO1_4_PIN 4
#define PIO1_4_PORT LPC_GPIO1
#define PIO1_4_OUTPUT (0x01 << 7)
#define PIO1_5_CMSIS PIO1_5
#define PIO1_5_PIN 5
#define PIO1_5_PORT LPC_GPIO1
#define PIO1_5_OUTPUT 0x00
#define PIO1_6_CMSIS PIO1_6
#define PIO1_6_PIN 6
#define PIO1_6_PORT LPC_GPIO1
#define PIO1_6_OUTPUT 0x00
#define PIO1_7_CMSIS PIO1_7
#define PIO1_7_PIN 7
#define PIO1_7_PORT LPC_GPIO1
#define PIO1_7_OUTPUT 0x00
#define PIO1_8_CMSIS PIO1_8
#define PIO1_8_PIN 8
#define PIO1_8_PORT LPC_GPIO1
#define PIO1_8_OUTPUT 0x00
#define PIO1_9_CMSIS PIO1_9
#define PIO1_9_PIN 9
#define PIO1_9_PORT LPC_GPIO1
#define PIO1_9_OUTPUT 0x00

14
pinio.h
View File

@ -37,6 +37,13 @@
else { IO ## _WPORT &= ~MASK(IO ## _PIN); } \
} while (0)
/**
Setting pins as input/output: other than with ARMs, function of a pin
on AVR isn't given by a dedicated function register, but solely by the
on-chip peripheral connected to it. With the peripheral (e.g. UART, SPI,
...) connected, a pin automatically serves with this function. With the
peripheral disconnected, it automatically returns to general I/O function.
*/
/// Set pin as input.
#define _SET_INPUT(IO) do { IO ## _DDR &= ~MASK(IO ## _PIN); } while (0)
/// Set pin as output.
@ -56,9 +63,16 @@
(v) ? MASK(IO ## _PIN) : 0; \
} while (0)
/**
Set pins as input/output. On ARM, each pin has its own IOCON register,
which allows to set its function and mode. We always set here standard
GPIO behavior. Peripherals using these pins may have to change this and
should do so in their own context.
*/
/// Set pin as output.
#define _SET_OUTPUT(IO) \
do { \
LPC_IOCON->IO ## _CMSIS = IO ## _OUTPUT; \
IO ## _PORT->DIR |= MASK(IO ## _PIN); \
} while (0)