aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile4
-rw-r--r--libmaple/adc.h11
-rw-r--r--libmaple/exti.h8
-rw-r--r--libmaple/libmaple.h6
-rw-r--r--libmaple/timers.c2
-rw-r--r--libmaple/timers.h18
-rw-r--r--support/ld/maple_mini/flash.ld211
-rw-r--r--support/ld/maple_mini/jtag.ld186
-rw-r--r--support/ld/maple_mini/ram.ld220
-rw-r--r--wirish/HardwareTimer.cpp18
-rw-r--r--wirish/HardwareTimer.h285
-rw-r--r--wirish/boards.h79
-rw-r--r--wirish/ext_interrupts.c20
-rw-r--r--wirish/ext_interrupts.h41
-rw-r--r--wirish/io.h149
-rw-r--r--wirish/pwm.h11
-rw-r--r--wirish/wirish_digital.c7
17 files changed, 1175 insertions, 101 deletions
diff --git a/Makefile b/Makefile
index 4fd5ade..2c61bd7 100644
--- a/Makefile
+++ b/Makefile
@@ -17,6 +17,10 @@ ifeq ($(BOARD), maple_native)
MCU := STM32F103ZE
PRODUCT_ID := 0003
endif
+ifeq ($(BOARD), maple_mini)
+ MCU := STM32F103CB
+ PRODUCT_ID := 0003
+endif
# Useful paths
ifeq ($(LIB_MAPLE_HOME),)
diff --git a/libmaple/adc.h b/libmaple/adc.h
index 9b61821..ce67116 100644
--- a/libmaple/adc.h
+++ b/libmaple/adc.h
@@ -43,7 +43,8 @@ extern "C"{
*
* Need to up the sample time if otherwise... see datasheet */
-/* TODO: We'll only use ADC1 for now... */
+/* TODO: We'll only use ADC1 for now. See page 41 of the manual for
+ ADC2 and ADC3's real addresses. */
#define ADC1_BASE 0x40012400
#define ADC2_BASE 0x40012400
#define ADC3_BASE 0x40012400
@@ -73,17 +74,17 @@ extern "C"{
void adc_init(void);
void adc_disable(void);
-/* Perform a single conversion on ADC[0-16],
+/* Perform a single conversion on ADC[0-15],
* PRECONDITIONS:
* adc initialized */
static inline int adc_read(int channel) {
- /* Set channel */
+ /* Set channel */
ADC_SQR3 = channel;
- /* Start the conversion */
+ /* Start the conversion */
CR2_SWSTART_BIT = 1;
- /* Wait for it to finish */
+ /* Wait for it to finish */
while(SR_EOC_BIT == 0)
;
diff --git a/libmaple/exti.h b/libmaple/exti.h
index 89cd986..cab2963 100644
--- a/libmaple/exti.h
+++ b/libmaple/exti.h
@@ -141,10 +141,10 @@
#define EXTI14 14
#define EXTI15 15
-#define EXTI_CONFIG_PORTA 0
-#define EXTI_CONFIG_PORTB 1
-#define EXTI_CONFIG_PORTC 2
-#define EXTI_CONFIG_PORTD 3
+#define EXTI_CONFIG_PORTA 0 // Maple, Maple Native, Maple Mini
+#define EXTI_CONFIG_PORTB 1 // Maple, Maple Native, Maple Mini
+#define EXTI_CONFIG_PORTC 2 // Maple, Maple Native, Maple Mini
+#define EXTI_CONFIG_PORTD 3 // Maple and Maple Native only
#define EXTI_CONFIG_PORTE 4 // Native only
#define EXTI_CONFIG_PORTF 5 // Native only
#define EXTI_CONFIG_PORTG 6 // Native only
diff --git a/libmaple/libmaple.h b/libmaple/libmaple.h
index 0b0494f..74488ab 100644
--- a/libmaple/libmaple.h
+++ b/libmaple/libmaple.h
@@ -168,6 +168,12 @@
#ifndef RAMSIZE
# define RAMSIZE (caddr_t)0x50000
#endif
+
+ /* Bitbanded Memory sections */
+ #define BITBAND_SRAM_REF 0x20000000
+ #define BITBAND_SRAM_BASE 0x22000000
+ #define BITBAND_PERI_REF 0x40000000
+ #define BITBAND_PERI_BASE 0x42000000
#endif
/* Make sure MCU-specific settings were defined */
diff --git a/libmaple/timers.c b/libmaple/timers.c
index 334ec0b..c369d1f 100644
--- a/libmaple/timers.c
+++ b/libmaple/timers.c
@@ -211,7 +211,7 @@ void timer_disable_all(void) {
}
/* Sets the mode of individual timer channels, including a DISABLE mode */
-void timer_set_mode(uint8 timer_num, uint8 channel, uint8 mode) {
+void timer_set_mode(uint8 timer_num, uint8 channel, TimerMode mode) {
timer_port *timer = timer_dev_table[timer_num].base;
ASSERT(channel >= 1);
diff --git a/libmaple/timers.h b/libmaple/timers.h
index ba8245c..d180bab 100644
--- a/libmaple/timers.h
+++ b/libmaple/timers.h
@@ -132,9 +132,21 @@ typedef volatile uint32* TimerCCR;
#define TIMER8_CH3_CCR TIMER8_BASE + 0x3C
#define TIMER8_CH4_CCR TIMER8_BASE + 0x40
-#define TIMER_DISABLED 0
-#define TIMER_PWM 1
-#define TIMER_OUTPUTCOMPARE 2
+/**
+ * Used to configure the behavior of a timer.
+ */
+typedef enum TimerMode {
+ TIMER_DISABLED, /**< In this mode, the timer stops counting,
+ interrupts are not called, and no state changes
+ are output. */
+ TIMER_PWM, /**< This is the default mode for pins after
+ initialization. */
+ TIMER_OUTPUTCOMPARE, /**< In this mode, the timer counts from 0 to
+ the overflow value repeatedly; every time
+ the counter value reaches one of the
+ channel compare values, the corresponding
+ interrupt is fired. */
+} TimerMode;
typedef struct {
volatile uint16 CR1;
diff --git a/support/ld/maple_mini/flash.ld b/support/ld/maple_mini/flash.ld
new file mode 100644
index 0000000..2d40100
--- /dev/null
+++ b/support/ld/maple_mini/flash.ld
@@ -0,0 +1,211 @@
+/* Linker script for STM32 (by Lanchon with Mods by LeafLabs)
+ *
+ * Version:Sourcery G++ 4.2-84
+ * BugURL:https://support.codesourcery.com/GNUToolchain/
+ *
+ * Copyright 2007 CodeSourcery.
+ *
+ * The authors hereby grant permission to use, copy, modify, distribute,
+ * and license this software and its documentation for any purpose, provided
+ * that existing copyright notices are retained in all copies and that this
+ * notice is included verbatim in any distributions. No written agreement,
+ * license, or royalty fee is required for any of the authorized uses.
+ * Modifications to this software may be copyrighted by their authors
+ * and need not follow the licensing terms described here, provided that
+ * the new terms are clearly indicated on the first page of each file where
+ * they apply. */
+
+/* Linker script for STM32 (by Lanchon),
+ * ROM and RAM relocated to their positions
+ * as placed by Maple bootloader
+ *
+ * Configure target memory and included script
+ * according to your application requirements. */
+
+/* Define memory spaces. */
+MEMORY
+{
+ ram (rwx) : ORIGIN = 0x20000C00, LENGTH = 17K
+ rom (rx) : ORIGIN = 0x08005000, LENGTH = 108K
+}
+
+OUTPUT_FORMAT ("elf32-littlearm", "elf32-bigarm", "elf32-littlearm")
+ENTRY(_start)
+SEARCH_DIR(.)
+/* GROUP(-lgcc -lc -lcs3 -lcs3unhosted -lcs3-lanchon-stm32) */
+GROUP(libgcc.a libc.a libm.a libcs3-lanchon-stm32.a)
+
+/* These force the linker to search for particular symbols from
+ * the start of the link process and thus ensure the user's
+ * overrides are picked up
+ */
+EXTERN(__cs3_reset_lanchon_stm32)
+INCLUDE names.inc
+EXTERN(__cs3_interrupt_vector_lanchon_stm32)
+EXTERN(__cs3_start_c main __cs3_stack __cs3_heap_end)
+EXTERN(_start)
+
+PROVIDE(__cs3_stack = __cs3_region_start_ram + __cs3_region_size_ram);
+PROVIDE(__cs3_heap_start = _end);
+PROVIDE(__cs3_heap_end = __cs3_region_start_ram + __cs3_region_size_ram);
+
+SECTIONS
+{
+ .text :
+ {
+ CREATE_OBJECT_SYMBOLS
+ __cs3_region_start_rom = .;
+ *(.cs3.region-head.rom)
+ __cs3_interrupt_vector = __cs3_interrupt_vector_lanchon_stm32;
+ *(.cs3.interrupt_vector)
+ /* Make sure we pulled in an interrupt vector. */
+ ASSERT (. != __cs3_interrupt_vector_lanchon_stm32, "No interrupt vector");
+ *(.rom)
+ *(.rom.b)
+
+ PROVIDE(__cs3_reset_lanchon_stm32 = _start);
+ __cs3_reset = __cs3_reset_lanchon_stm32;
+ *(.cs3.reset)
+
+ *(.text .text.* .gnu.linkonce.t.*)
+ *(.plt)
+ *(.gnu.warning)
+ *(.glue_7t) *(.glue_7) *(.vfp11_veneer)
+
+ *(.rodata .rodata.* .gnu.linkonce.r.*)
+
+ *(.ARM.extab* .gnu.linkonce.armextab.*)
+ *(.gcc_except_table)
+ *(.eh_frame_hdr)
+ *(.eh_frame)
+
+ . = ALIGN(4);
+ KEEP(*(.init))
+
+ . = ALIGN(4);
+ __preinit_array_start = .;
+ KEEP (*(.preinit_array))
+ __preinit_array_end = .;
+
+ . = ALIGN(4);
+ __init_array_start = .;
+ KEEP (*(SORT(.init_array.*)))
+ KEEP (*(.init_array))
+ __init_array_end = .;
+
+ . = ALIGN(0x4);
+ KEEP (*crtbegin.o(.ctors))
+ KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors))
+ KEEP (*(SORT(.ctors.*)))
+ KEEP (*crtend.o(.ctors))
+
+ . = ALIGN(4);
+ KEEP(*(.fini))
+
+ . = ALIGN(4);
+ __fini_array_start = .;
+ KEEP (*(.fini_array))
+ KEEP (*(SORT(.fini_array.*)))
+ __fini_array_end = .;
+
+ KEEP (*crtbegin.o(.dtors))
+ KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors))
+ KEEP (*(SORT(.dtors.*)))
+ KEEP (*crtend.o(.dtors))
+
+ . = ALIGN(4);
+ __cs3_regions = .;
+ LONG (0)
+ LONG (__cs3_region_init_ram)
+ LONG (__cs3_region_start_ram)
+ LONG (__cs3_region_init_size_ram)
+ LONG (__cs3_region_zero_size_ram)
+ } >rom
+
+ /* .ARM.exidx is sorted, so has to go in its own output section. */
+ __exidx_start = .;
+ .ARM.exidx :
+ {
+ *(.ARM.exidx* .gnu.linkonce.armexidx.*)
+ } >rom
+ __exidx_end = .;
+ .text.align :
+ {
+ . = ALIGN(8);
+ _etext = .;
+ } >rom
+
+/* expose a custom rom only section */
+ .USER_FLASH :
+ {
+ *(.USER_FLASH)
+ } >rom
+
+
+ /* __cs3_region_end_rom is deprecated */
+ __cs3_region_end_rom = __cs3_region_start_rom + LENGTH(rom);
+ __cs3_region_size_rom = LENGTH(rom);
+ __cs3_region_num = 1;
+
+ .data :
+ {
+ __cs3_region_start_ram = .;
+ *(.cs3.region-head.ram)
+ KEEP(*(.jcr))
+ *(.got.plt) *(.got)
+ *(.shdata)
+ *(.data .data.* .gnu.linkonce.d.*)
+ *(.ram)
+ . = ALIGN (8);
+ _edata = .;
+ } >ram AT>rom
+ .bss :
+ {
+ *(.shbss)
+ *(.bss .bss.* .gnu.linkonce.b.*)
+ *(COMMON)
+ *(.ram.b)
+ . = ALIGN (8);
+ _end = .;
+ __end = .;
+ } >ram AT>rom
+ /* __cs3_region_end_ram is deprecated */
+ __cs3_region_end_ram = __cs3_region_start_ram + LENGTH(ram);
+ __cs3_region_size_ram = LENGTH(ram);
+ __cs3_region_init_ram = LOADADDR (.data);
+ __cs3_region_init_size_ram = _edata - ADDR (.data);
+ __cs3_region_zero_size_ram = _end - _edata;
+ __cs3_region_num = 1;
+
+ .stab 0 (NOLOAD) : { *(.stab) }
+ .stabstr 0 (NOLOAD) : { *(.stabstr) }
+ /* DWARF debug sections.
+ * Symbols in the DWARF debugging sections are relative to the beginning
+ * of the section so we begin them at 0. */
+ /* DWARF 1 */
+ .debug 0 : { *(.debug) }
+ .line 0 : { *(.line) }
+ /* GNU DWARF 1 extensions */
+ .debug_srcinfo 0 : { *(.debug_srcinfo) }
+ .debug_sfnames 0 : { *(.debug_sfnames) }
+ /* DWARF 1.1 and DWARF 2 */
+ .debug_aranges 0 : { *(.debug_aranges) }
+ .debug_pubnames 0 : { *(.debug_pubnames) }
+ /* DWARF 2 */
+ .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) }
+ .debug_abbrev 0 : { *(.debug_abbrev) }
+ .debug_line 0 : { *(.debug_line) }
+ .debug_frame 0 : { *(.debug_frame) }
+ .debug_str 0 : { *(.debug_str) }
+ .debug_loc 0 : { *(.debug_loc) }
+ .debug_macinfo 0 : { *(.debug_macinfo) }
+ /* SGI/MIPS DWARF 2 extensions */
+ .debug_weaknames 0 : { *(.debug_weaknames) }
+ .debug_funcnames 0 : { *(.debug_funcnames) }
+ .debug_typenames 0 : { *(.debug_typenames) }
+ .debug_varnames 0 : { *(.debug_varnames) }
+
+ .note.gnu.arm.ident 0 : { KEEP (*(.note.gnu.arm.ident)) }
+ .ARM.attributes 0 : { KEEP (*(.ARM.attributes)) }
+ /DISCARD/ : { *(.note.GNU-stack) }
+}
diff --git a/support/ld/maple_mini/jtag.ld b/support/ld/maple_mini/jtag.ld
new file mode 100644
index 0000000..435e3f0
--- /dev/null
+++ b/support/ld/maple_mini/jtag.ld
@@ -0,0 +1,186 @@
+/* Linker script for STM32 (by Lanchon),
+ * ROM and RAM relocated to their positions
+ * as placed by Maple bootloader
+ *
+ * Configure target memory and included script
+ * according to your application requirements. */
+
+/* Define memory spaces. */
+MEMORY
+{
+ ram (rwx) : ORIGIN = 0x20000000, LENGTH = 20K
+ rom (rx) : ORIGIN = 0x08000000, LENGTH = 128K
+}
+
+OUTPUT_FORMAT ("elf32-littlearm", "elf32-bigarm", "elf32-littlearm")
+ENTRY(_start)
+SEARCH_DIR(.)
+/* GROUP(-lgcc -lc -lcs3 -lcs3unhosted -lcs3-lanchon-stm32) */
+GROUP(libgcc.a libc.a libm.a libcs3-lanchon-stm32.a)
+
+/* These force the linker to search for particular symbols from
+ * the start of the link process and thus ensure the user's
+ * overrides are picked up
+ */
+EXTERN(__cs3_reset_lanchon_stm32)
+INCLUDE names.inc
+EXTERN(__cs3_interrupt_vector_lanchon_stm32)
+EXTERN(__cs3_start_c main __cs3_stack __cs3_heap_end)
+EXTERN(_start)
+
+PROVIDE(__cs3_stack = __cs3_region_start_ram + __cs3_region_size_ram);
+PROVIDE(__cs3_heap_start = _end);
+PROVIDE(__cs3_heap_end = __cs3_region_start_ram + __cs3_region_size_ram);
+
+SECTIONS
+{
+ .text :
+ {
+ CREATE_OBJECT_SYMBOLS
+ __cs3_region_start_rom = .;
+ *(.cs3.region-head.rom)
+ __cs3_interrupt_vector = __cs3_interrupt_vector_lanchon_stm32;
+ *(.cs3.interrupt_vector)
+ /* Make sure we pulled in an interrupt vector. */
+ ASSERT (. != __cs3_interrupt_vector_lanchon_stm32, "No interrupt vector");
+ *(.rom)
+ *(.rom.b)
+
+ PROVIDE(__cs3_reset_lanchon_stm32 = _start);
+ __cs3_reset = __cs3_reset_lanchon_stm32;
+ *(.cs3.reset)
+
+ *(.text .text.* .gnu.linkonce.t.*)
+ *(.plt)
+ *(.gnu.warning)
+ *(.glue_7t) *(.glue_7) *(.vfp11_veneer)
+
+ *(.rodata .rodata.* .gnu.linkonce.r.*)
+
+ *(.ARM.extab* .gnu.linkonce.armextab.*)
+ *(.gcc_except_table)
+ *(.eh_frame_hdr)
+ *(.eh_frame)
+
+ . = ALIGN(4);
+ KEEP(*(.init))
+
+ . = ALIGN(4);
+ __preinit_array_start = .;
+ KEEP (*(.preinit_array))
+ __preinit_array_end = .;
+
+ . = ALIGN(4);
+ __init_array_start = .;
+ KEEP (*(SORT(.init_array.*)))
+ KEEP (*(.init_array))
+ __init_array_end = .;
+
+ . = ALIGN(0x4);
+ KEEP (*crtbegin.o(.ctors))
+ KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors))
+ KEEP (*(SORT(.ctors.*)))
+ KEEP (*crtend.o(.ctors))
+
+ . = ALIGN(4);
+ KEEP(*(.fini))
+
+ . = ALIGN(4);
+ __fini_array_start = .;
+ KEEP (*(.fini_array))
+ KEEP (*(SORT(.fini_array.*)))
+ __fini_array_end = .;
+
+ KEEP (*crtbegin.o(.dtors))
+ KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors))
+ KEEP (*(SORT(.dtors.*)))
+ KEEP (*crtend.o(.dtors))
+
+ . = ALIGN(4);
+ __cs3_regions = .;
+ LONG (0)
+ LONG (__cs3_region_init_ram)
+ LONG (__cs3_region_start_ram)
+ LONG (__cs3_region_init_size_ram)
+ LONG (__cs3_region_zero_size_ram)
+ } >rom
+
+ /* .ARM.exidx is sorted, so has to go in its own output section. */
+ __exidx_start = .;
+ .ARM.exidx :
+ {
+ *(.ARM.exidx* .gnu.linkonce.armexidx.*)
+ } >rom
+ __exidx_end = .;
+ .text.align :
+ {
+ . = ALIGN(8);
+ _etext = .;
+ } >rom
+ /* __cs3_region_end_rom is deprecated */
+ __cs3_region_end_rom = __cs3_region_start_rom + LENGTH(rom);
+ __cs3_region_size_rom = LENGTH(rom);
+ __cs3_region_num = 1;
+
+ .data :
+ {
+ __cs3_region_start_ram = .;
+ *(.cs3.region-head.ram)
+ KEEP(*(.jcr))
+ *(.got.plt) *(.got)
+ *(.shdata)
+ *(.data .data.* .gnu.linkonce.d.*)
+ *(.ram)
+ . = ALIGN (8);
+ _edata = .;
+ } >ram AT>rom
+ .bss :
+ {
+ *(.shbss)
+ *(.bss .bss.* .gnu.linkonce.b.*)
+ *(COMMON)
+ *(.ram.b)
+ . = ALIGN (8);
+ _end = .;
+ __end = .;
+ } >ram AT>rom
+ /* __cs3_region_end_ram is deprecated */
+ __cs3_region_end_ram = __cs3_region_start_ram + LENGTH(ram);
+ __cs3_region_size_ram = LENGTH(ram);
+ __cs3_region_init_ram = LOADADDR (.data);
+ __cs3_region_init_size_ram = _edata - ADDR (.data);
+ __cs3_region_zero_size_ram = _end - _edata;
+ __cs3_region_num = 1;
+
+ .stab 0 (NOLOAD) : { *(.stab) }
+ .stabstr 0 (NOLOAD) : { *(.stabstr) }
+ /* DWARF debug sections.
+ * Symbols in the DWARF debugging sections are relative to the beginning
+ * of the section so we begin them at 0. */
+ /* DWARF 1 */
+ .debug 0 : { *(.debug) }
+ .line 0 : { *(.line) }
+ /* GNU DWARF 1 extensions */
+ .debug_srcinfo 0 : { *(.debug_srcinfo) }
+ .debug_sfnames 0 : { *(.debug_sfnames) }
+ /* DWARF 1.1 and DWARF 2 */
+ .debug_aranges 0 : { *(.debug_aranges) }
+ .debug_pubnames 0 : { *(.debug_pubnames) }
+ /* DWARF 2 */
+ .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) }
+ .debug_abbrev 0 : { *(.debug_abbrev) }
+ .debug_line 0 : { *(.debug_line) }
+ .debug_frame 0 : { *(.debug_frame) }
+ .debug_str 0 : { *(.debug_str) }
+ .debug_loc 0 : { *(.debug_loc) }
+ .debug_macinfo 0 : { *(.debug_macinfo) }
+ /* SGI/MIPS DWARF 2 extensions */
+ .debug_weaknames 0 : { *(.debug_weaknames) }
+ .debug_funcnames 0 : { *(.debug_funcnames) }
+ .debug_typenames 0 : { *(.debug_typenames) }
+ .debug_varnames 0 : { *(.debug_varnames) }
+
+ .note.gnu.arm.ident 0 : { KEEP (*(.note.gnu.arm.ident)) }
+ .ARM.attributes 0 : { KEEP (*(.ARM.attributes)) }
+ /DISCARD/ : { *(.note.GNU-stack) }
+}
diff --git a/support/ld/maple_mini/ram.ld b/support/ld/maple_mini/ram.ld
new file mode 100644
index 0000000..1fbecc5
--- /dev/null
+++ b/support/ld/maple_mini/ram.ld
@@ -0,0 +1,220 @@
+/* Linker script for STM32 (by Lanchon with Mods by LeafLabs)
+ *
+ * Version:Sourcery G++ 4.2-84
+ * BugURL:https://support.codesourcery.com/GNUToolchain/
+ *
+ * Copyright 2007 CodeSourcery.
+ *
+ * The authors hereby grant permission to use, copy, modify, distribute,
+ * and license this software and its documentation for any purpose, provided
+ * that existing copyright notices are retained in all copies and that this
+ * notice is included verbatim in any distributions. No written agreement,
+ * license, or royalty fee is required for any of the authorized uses.
+ * Modifications to this software may be copyrighted by their authors
+ * and need not follow the licensing terms described here, provided that
+ * the new terms are clearly indicated on the first page of each file where
+ * they apply. */
+
+/* Linker script for STM32 (by Lanchon),
+ * ROM and RAM relocated to their positions
+ * as placed by Maple bootloader
+ *
+ * Configure target memory and included script
+ * according to your application requirements. */
+
+/* Define memory spaces. */
+MEMORY
+{
+ ram (rwx) : ORIGIN = 0x20000C00, LENGTH = 17K
+ rom (rx) : ORIGIN = 0x08005000, LENGTH = 0K
+}
+
+
+OUTPUT_FORMAT ("elf32-littlearm", "elf32-bigarm", "elf32-littlearm")
+ENTRY(_start)
+SEARCH_DIR(.)
+/* GROUP(-lgcc -lc -lcs3 -lcs3unhosted -lcs3-lanchon-stm32) */
+GROUP(libgcc.a libc.a libm.a libcs3-lanchon-stm32.a)
+
+/* These force the linker to search for particular symbols from
+ * the start of the link process and thus ensure the user's
+ * overrides are picked up
+ */
+EXTERN(__cs3_reset_lanchon_stm32)
+INCLUDE names.inc
+EXTERN(__cs3_interrupt_vector_lanchon_stm32)
+EXTERN(__cs3_start_c main __cs3_stack __cs3_heap_end)
+EXTERN(_start)
+
+PROVIDE(__cs3_stack = __cs3_region_start_ram + __cs3_region_size_ram);
+PROVIDE(__cs3_heap_start = _end);
+PROVIDE(__cs3_heap_end = __cs3_region_start_ram + __cs3_region_size_ram);
+
+SECTIONS
+{
+ .text :
+ {
+ CREATE_OBJECT_SYMBOLS
+ __cs3_region_start_ram = .;
+ *(.cs3.region-head.ram)
+ __cs3_interrupt_vector = __cs3_interrupt_vector_lanchon_stm32;
+ *(.cs3.interrupt_vector)
+ /* Make sure we pulled in an interrupt vector. */
+ ASSERT (. != __cs3_interrupt_vector_lanchon_stm32, "No interrupt vector");
+
+ PROVIDE(__cs3_reset_lanchon_stm32 = _start);
+ __cs3_reset = __cs3_reset_lanchon_stm32;
+ *(.cs3.reset)
+
+ *(.text .text.* .gnu.linkonce.t.*)
+ *(.plt)
+ *(.gnu.warning)
+ *(.glue_7t) *(.glue_7) *(.vfp11_veneer)
+
+ *(.rodata .rodata.* .gnu.linkonce.r.*)
+
+ *(.ARM.extab* .gnu.linkonce.armextab.*)
+ *(.gcc_except_table)
+ *(.eh_frame_hdr)
+ *(.eh_frame)
+
+ . = ALIGN(4);
+ KEEP(*(.init))
+
+ . = ALIGN(4);
+ __preinit_array_start = .;
+ KEEP (*(.preinit_array))
+ __preinit_array_end = .;
+
+ . = ALIGN(4);
+ __init_array_start = .;
+ KEEP (*(SORT(.init_array.*)))
+ KEEP (*(.init_array))
+ __init_array_end = .;
+
+ . = ALIGN(0x4);
+ KEEP (*crtbegin.o(.ctors))
+ KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors))
+ KEEP (*(SORT(.ctors.*)))
+ KEEP (*crtend.o(.ctors))
+
+ . = ALIGN(4);
+ KEEP(*(.fini))
+
+ . = ALIGN(4);
+ __fini_array_start = .;
+ KEEP (*(.fini_array))
+ KEEP (*(SORT(.fini_array.*)))
+ __fini_array_end = .;
+
+ KEEP (*crtbegin.o(.dtors))
+ KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors))
+ KEEP (*(SORT(.dtors.*)))
+ KEEP (*crtend.o(.dtors))
+
+ . = ALIGN(4);
+ __cs3_regions = .;
+ LONG (0)
+ LONG (__cs3_region_init_ram)
+ LONG (__cs3_region_start_ram)
+ LONG (__cs3_region_init_size_ram)
+ LONG (__cs3_region_zero_size_ram)
+ } >ram
+
+ /* .ARM.exidx is sorted, so has to go in its own output section. */
+ /* even cs3.rom is in ram since its running as user code under the Maple
+ bootloader */
+ __exidx_start = .;
+ .ARM.exidx :
+ {
+ *(.ARM.exidx* .gnu.linkonce.armexidx.*)
+ } >ram
+ __exidx_end = .;
+ .text.align :
+ {
+ . = ALIGN(8);
+ _etext = .;
+ } >ram
+
+ .cs3.rom :
+ {
+ __cs3_region_start_rom = .;
+ *(.cs3.region-head.rom)
+ *(.rom)
+ . = ALIGN (8);
+ } >ram
+
+ .cs3.rom.bss :
+ {
+ *(.rom.b)
+ . = ALIGN (8);
+ } >ram
+ /* __cs3_region_end_rom is deprecated */
+ __cs3_region_end_rom = __cs3_region_start_rom + LENGTH(ram);
+ __cs3_region_size_rom = LENGTH(ram);
+ __cs3_region_init_rom = LOADADDR (.cs3.rom);
+ __cs3_region_init_size_rom = SIZEOF(.cs3.rom);
+ __cs3_region_zero_size_rom = SIZEOF(.cs3.rom.bss);
+
+ .data :
+ {
+
+ KEEP(*(.jcr))
+ *(.got.plt) *(.got)
+ *(.shdata)
+ *(.data .data.* .gnu.linkonce.d.*)
+ *(.ram)
+ . = ALIGN (8);
+ _edata = .;
+ } >ram
+ .bss :
+ {
+ *(.shbss)
+ *(.bss .bss.* .gnu.linkonce.b.*)
+ *(COMMON)
+ *(.ram.b)
+ . = ALIGN (8);
+ _end = .;
+ __end = .;
+ } >ram
+ /* __cs3_region_end_ram is deprecated */
+ __cs3_region_end_ram = __cs3_region_start_ram + LENGTH(ram);
+ __cs3_region_size_ram = LENGTH(ram);
+ __cs3_region_init_ram = LOADADDR (.text);
+ __cs3_region_init_size_ram = _edata - ADDR (.text);
+ __cs3_region_zero_size_ram = _end - _edata;
+ __cs3_region_num = 1;
+
+ .stab 0 (NOLOAD) : { *(.stab) }
+ .stabstr 0 (NOLOAD) : { *(.stabstr) }
+ /* DWARF debug sections.
+ * Symbols in the DWARF debugging sections are relative to the beginning
+ * of the section so we begin them at 0. */
+ /* DWARF 1 */
+ .debug 0 : { *(.debug) }
+ .line 0 : { *(.line) }
+ /* GNU DWARF 1 extensions */
+ .debug_srcinfo 0 : { *(.debug_srcinfo) }
+ .debug_sfnames 0 : { *(.debug_sfnames) }
+ /* DWARF 1.1 and DWARF 2 */
+ .debug_aranges 0 : { *(.debug_aranges) }
+ .debug_pubnames 0 : { *(.debug_pubnames) }
+ /* DWARF 2 */
+ .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) }
+ .debug_abbrev 0 : { *(.debug_abbrev) }
+ .debug_line 0 : { *(.debug_line) }
+ .debug_frame 0 : { *(.debug_frame) }
+ .debug_str 0 : { *(.debug_str) }
+ .debug_loc 0 : { *(.debug_loc) }
+ .debug_macinfo 0 : { *(.debug_macinfo) }
+ /* SGI/MIPS DWARF 2 extensions */
+ .debug_weaknames 0 : { *(.debug_weaknames) }
+ .debug_funcnames 0 : { *(.debug_funcnames) }
+ .debug_typenames 0 : { *(.debug_typenames) }
+ .debug_varnames 0 : { *(.debug_varnames) }
+
+ .note.gnu.arm.ident 0 : { KEEP (*(.note.gnu.arm.ident)) }
+ .ARM.attributes 0 : { KEEP (*(.ARM.attributes)) }
+ /DISCARD/ : { *(.note.GNU-stack) }
+}
+
diff --git a/wirish/HardwareTimer.cpp b/wirish/HardwareTimer.cpp
index 6fbad8b..5b80ec1 100644
--- a/wirish/HardwareTimer.cpp
+++ b/wirish/HardwareTimer.cpp
@@ -22,10 +22,8 @@
* THE SOFTWARE.
*****************************************************************************/
-/**
- * @brief wirish timer class to manage the four 16-bit timer peripherals
- *
- * This implementation is not very efficient (lots of duplicated functions)
+/*
+ * wirish timer class to manage the four 16-bit timer peripherals
*/
#include "wirish.h"
@@ -69,10 +67,6 @@ uint16 HardwareTimer::getCount(void) {
return timer_get_count(this->timerNum);
}
-/* This function will set the prescaler and overflow to get a period
- * of the given length with the most resolution; the return value is
- * the overflow value and thus the largest value that can be set as a
- * compare. */
uint16 HardwareTimer::setPeriod(uint32 microseconds) {
// XXX: 72MHz shouldn't be hard coded in here... global define?
@@ -92,19 +86,19 @@ uint16 HardwareTimer::setPeriod(uint32 microseconds) {
return this->overflow;
}
-void HardwareTimer::setChannel1Mode(uint8 mode) {
+void HardwareTimer::setChannel1Mode(TimerMode mode) {
timer_set_mode(this->timerNum,1,mode);
}
-void HardwareTimer::setChannel2Mode(uint8 mode) {
+void HardwareTimer::setChannel2Mode(TimerMode mode) {
timer_set_mode(this->timerNum,2,mode);
}
-void HardwareTimer::setChannel3Mode(uint8 mode) {
+void HardwareTimer::setChannel3Mode(TimerMode mode) {
timer_set_mode(this->timerNum,3,mode);
}
-void HardwareTimer::setChannel4Mode(uint8 mode) {
+void HardwareTimer::setChannel4Mode(TimerMode mode) {
timer_set_mode(this->timerNum,4,mode);
}
diff --git a/wirish/HardwareTimer.h b/wirish/HardwareTimer.h
index c6e11c8..b05085f 100644
--- a/wirish/HardwareTimer.h
+++ b/wirish/HardwareTimer.h
@@ -29,6 +29,26 @@
#ifndef _TIMER_H_
#define _TIMER_H_
+/**
+ * Interface to one of the 16-bit timer peripherals.
+ *
+ * User code should not instantiate this class directly; instead, use
+ * one of the predefined Timer<n> instances (Timer1, Timer2, etc.).
+ *
+ * HardwareTimer instances can be configured to generate periodic or
+ * delayed events with minimal work done by the microcontroller. Each
+ * timer maintains a single 16-bit count that can be configured with a
+ * prescaler and overflow value.
+ *
+ * By default, a timer's counter is incremented once per clock cycle.
+ * The prescaler acts as a divider of the 72MHz Maple system clock;
+ * without prescaling, the timer's count would reach 65535 (2**16-1)
+ * and roll over over 1000 times per second.
+ *
+ * The overflow value is the maximum value the counter will reach. It
+ * defaults to 65535; smaller values will cause the counter to reset
+ * more frequently.
+ */
class HardwareTimer {
private:
uint16 overflow;
@@ -37,39 +57,282 @@ class HardwareTimer {
public:
HardwareTimer(uint8 timer_num);
+ /**
+ * Stop the counter, without affecting its configuration.
+ *
+ * The timer will no longer count or fire interrupts after this
+ * function is called, until it is resumed. This function is
+ * useful during timer setup periods, in order to prevent
+ * interrupts from firing before the timer is fully configured.
+ *
+ * Note that there is some function call overhead associated with
+ * this method, so using it in concert with
+ * HardwareTimer::resume() is not a robust way to align multiple
+ * timers to the same count value.
+ *
+ * @see HardwareTimer::resume()
+ */
void pause(void);
+
+ /**
+ * Resume a paused timer, without affecting its configuration.
+ *
+ * The timer will resume counting and firing interrupts as
+ * appropriate.
+ *
+ * Note that there is some function call overhead associated with
+ * using this method, so using it in concert with
+ * HardwareTimer::resume() is not a robust way to align multiple
+ * timers to the same count value.
+ *
+ * @see HardwareTimer::pause()
+ */
void resume(void);
+
+ /**
+ * Set the timer prescale.
+ *
+ * The prescaler acts as a clock divider to slow down the rate at
+ * which the counter increments.
+ *
+ * For example, the system clock rate is 72MHz, so the counter
+ * will reach 65535 in (13.89 nanoseconds) * (65535 counts) =
+ * (910.22 microseconds), or about a thousand times a second. If
+ * the prescaler equals 1098, then the clock rate is effectively
+ * 65.56KHz, and the counter will reach 65536 in (15.25
+ * microseconds) * (65536 counts) = (0.999 seconds), or about once
+ * per second.
+ *
+ * The HardwareTimer::setPeriod() method may also be used as a
+ * convenient alternative.
+ *
+ * @param factor The new prescale value to set.
+ * @see HardwareTimer::setPeriod()
+ */
void setPrescaleFactor(uint16 factor);
- void setOverflow(uint16 val); // truncates to overflow
- void setCount(uint16 val); // truncates to overflow
+
+ /**
+ * Sets the timer overflow (or "reload") value.
+ *
+ * When the timer's counter reaches this, value it resets to
+ * zero. Its default value is 65535 (the largest unsigned 16-bit
+ * integer); setting the overflow to anything lower will cause
+ * interrupts to be called more frequently (see the setPeriod()
+ * function below for a shortcut). This number sets the maximum
+ * value for the channel compare values.
+ *
+ * @param val The new overflow value to set
+ * @see HardwareTimer::setOverflow()
+ */
+ void setOverflow(uint16 val);
+
+ /**
+ * Set the current timer count.
+ *
+ * Note that there is some function call overhead associated with
+ * callign this method, so using it is not a robust way to get
+ * multiple timers to share a count value.
+ *
+ * @param val The new count value to set. If this value exceeds
+ * the timer's overflow value, it is truncated to the
+ * overflow value.
+ */
+ void setCount(uint16 val);
+
+ /**
+ * Retrieve the current timer count.
+ *
+ * @return The timer's current count value
+ */
uint16 getCount(void);
- // tries to set prescaler and overflow wisely; returns overflow
+ /**
+ * Configure the prescaler and overflow values to generate a timer
+ * reload with a period as close to the given number of
+ * microseconds as possible.
+ *
+ * The return value is the overflow, which may be used to set
+ * channel compare values. However, if a clock that fires an
+ * interrupt every given number of microseconds is all that is
+ * desired, and the relative "phases" are unimportant, channel
+ * compare values may all be set to 1.
+ *
+ * @param microseconds the desired period of the timer.
+ * @return the overflow value (and thus, the largest value that can be
+ * set as a compare).
+ */
uint16 setPeriod(uint32 microseconds);
- void setChannel1Mode(uint8 mode);
- void setChannel2Mode(uint8 mode);
- void setChannel3Mode(uint8 mode);
- void setChannel4Mode(uint8 mode);
- void setCompare1(uint16 val); // truncates to overflow
- void setCompare2(uint16 val); // truncates to overflow
- void setCompare3(uint16 val); // truncates to overflow
- void setCompare4(uint16 val); // truncates to overflow
+
+ /**
+ * Set channel 1 of this timer to the given mode.
+ *
+ * Note: Timer1.setChannel1Mode(TIMER_PWM) may not work as
+ * expected; if you want PWM functionality on a channel make sure
+ * you don't set it to something else!
+ *
+ * @see TimerMode
+ */
+ void setChannel1Mode(TimerMode mode);
+
+ /**
+ * Set channel 2 of this timer to the given mode.
+ * @see TimerMode
+ */
+ void setChannel2Mode(TimerMode mode);
+
+ /**
+ * Set channel 3 of this timer to the given mode.
+ * @see TimerMode
+ */
+ void setChannel3Mode(TimerMode mode);
+
+ /**
+ * Set channel 4 of this timer to the given mode.
+ * @see TimerMode
+ */
+ void setChannel4Mode(TimerMode mode);
+
+ /**
+ * Sets the compare value for channel 1.
+ *
+ * When the counter reaches this value the interrupt for this
+ * channel will fire if channel 1 mode is TIMER_OUTPUTCOMPARE and
+ * an interrupt is attached.
+ *
+ * By default, this only changes the relative offsets between
+ * events on a single timer ("phase"); they don't control the
+ * frequency with which they occur. However, a common trick is to
+ * increment the compare value manually in the interrupt handler
+ * so that the event will fire again after the increment
+ * period. There can be a different increment value for each
+ * channel, so this trick allows events to be programmed at 4
+ * different rates on a single timer. Note that function call
+ * overheads mean that the smallest increment rate is at least a
+ * few microseconds.
+ *
+ * @param val The compare value to set. If greater than this
+ * timer's overflow value, it will be truncated to the
+ * overflow value.
+ *
+ * @see TimerMode
+ * @see HardwareTimer::setChannel1Mode()
+ */
+ void setCompare1(uint16 val);
+
+ /**
+ * Sets the compare value for channel 2.
+ *
+ * @param val The compare value to set. If greater than this
+ * timer's overflow value, it will be truncated to the
+ * overflow value.
+ * @see HardwareTimer::setCompare1()
+ */
+ void setCompare2(uint16 val);
+
+ /**
+ * Sets the compare value for channel 3.
+ *
+ * @param val The compare value to set. If greater than this
+ * timer's overflow value, it will be truncated to the
+ * overflow value.
+ * @see HardwareTimer::setCompare1()
+ */
+ void setCompare3(uint16 val);
+
+ /**
+ * Sets the compare value for channel 4.
+ *
+ * @param val The compare value to set. If greater than this
+ * timer's overflow value, it will be truncated to the
+ * overflow value.
+ * @see HardwareTimer::setCompare1()
+ */
+ void setCompare4(uint16 val);
+
+ /**
+ * Attach an interrupt handler to this timer's channel 1. This
+ * interrupt handler will be called when the timer's counter
+ * reaches its channel 1 compare value.
+ *
+ * The argument should be a function which takes no arguments and
+ * has no return value; i.e. it should have signature
+ *
+ * void (*handler)(void);
+ *
+ * Note: The function (often called an interrupt service routine,
+ * or ISR) should attempt to return as quickly as possible.
+ * Blinking the LED, some logic, PWM updates, and Serial writes
+ * are fine; writing to SerialUSB or waiting for user input can
+ * take a long time and other compare interrupts won't fire. Tip:
+ * if you have a delay() in your interrupt routine, you're probably
+ * doing it wrong.
+ *
+ * @param handler The ISR to attach to channel 1.
+ * @see voidFuncPtr
+ */
void attachCompare1Interrupt(voidFuncPtr handler);
+
+ /**
+ * Like attachCompare1Interrupt(), but for channel 2.
+ * @see HardwareTimer::attachCompare1Interrupt()
+ */
void attachCompare2Interrupt(voidFuncPtr handler);
+
+ /**
+ * Like attachCompare1Interrupt(), but for channel 3.
+ * @see HardwareTimer::attachCompare1Interrupt()
+ */
void attachCompare3Interrupt(voidFuncPtr handler);
+
+ /**
+ * Like attachCompare1Interrupt(), but for channel 4.
+ * @see HardwareTimer::attachCompare1Interrupt()
+ */
void attachCompare4Interrupt(voidFuncPtr handler);
+
+ /**
+ * Remove the interrupt handler attached to channel 1, if any.
+ * The handler will no longer be called by this timer.
+ * @see HardwareTimer::attachCompare1Interrupt()
+ */
void detachCompare1Interrupt(void);
+
+ /**
+ * Remove the interrupt handler attached to channel 2, if any.
+ * The handler will no longer be called by this timer.
+ * @see HardwareTimer::attachCompare1Interrupt()
+ */
void detachCompare2Interrupt(void);
+
+ /**
+ * Remove the interrupt handler attached to channel 3, if any.
+ * The handler will no longer be called by this timer.
+ * @see HardwareTimer::attachCompare1Interrupt()
+ */
void detachCompare3Interrupt(void);
+
+ /**
+ * Remove the interrupt handler attached to channel 4, if any.
+ * The handler will no longer be called by this timer.
+ * @see HardwareTimer::attachCompare1Interrupt()
+ */
void detachCompare4Interrupt(void);
};
+/** Pre-instantiated timer for use by user code. */
extern HardwareTimer Timer1;
+/** Pre-instantiated timer for use by user code. */
extern HardwareTimer Timer2;
+/** Pre-instantiated timer for use by user code. */
extern HardwareTimer Timer3;
+/** Pre-instantiated timer for use by user code. */
extern HardwareTimer Timer4;
#if NR_TIMERS >= 8
+/** Pre-instantiated timer for use by user code, on devices with
+ more than four timers (this does not include the Maple). */
extern HardwareTimer Timer5;
+/** Pre-instantiated timer for use by user code, on devices with
+ more than four timers (this does not include the Maple). */
extern HardwareTimer Timer8;
#endif
diff --git a/wirish/boards.h b/wirish/boards.h
index fed5ead..0625d0a 100644
--- a/wirish/boards.h
+++ b/wirish/boards.h
@@ -292,6 +292,85 @@ typedef struct PinMapping {
};
#endif
+#ifdef BOARD_maple_mini
+
+ #define CYCLES_PER_MICROSECOND 72
+ #define MAPLE_RELOAD_VAL 71999 /* takes a cycle to reload */
+
+ static __attribute__ ((unused)) PinMapping PIN_MAP[NR_GPIO_PINS] = {
+ /* D0/PC15 */
+ {GPIOC_BASE, 15, ADC_INVALID, TIMER_INVALID, EXTI_CONFIG_PORTC, TIMER_INVALID, TIMER_INVALID},
+ /* D1/PA0 */
+ {GPIOA_BASE, 0, ADC0, TIMER2_CH1_CCR, EXTI_CONFIG_PORTA, TIMER2, 1},
+ /* D2/PA1 */
+ {GPIOA_BASE, 1, ADC1, TIMER2_CH2_CCR, EXTI_CONFIG_PORTA, TIMER2, 2},
+ /* D3/PA2 */
+ {GPIOA_BASE, 2, ADC2, TIMER2_CH3_CCR, EXTI_CONFIG_PORTA, TIMER2, 3},
+ /* D4/PA3 */
+ {GPIOA_BASE, 3, ADC3, TIMER2_CH4_CCR, EXTI_CONFIG_PORTA, TIMER2, 4},
+ /* D5/PA4 */
+ {GPIOA_BASE, 4, ADC4, TIMER_INVALID, EXTI_CONFIG_PORTA, TIMER_INVALID, TIMER_INVALID},
+ /* D6/PA5 */
+ {GPIOA_BASE, 5, ADC5, TIMER_INVALID, EXTI_CONFIG_PORTA, TIMER_INVALID, TIMER_INVALID},
+ /* D7/PA6 */
+ {GPIOA_BASE, 6, ADC6, TIMER3_CH1_CCR, EXTI_CONFIG_PORTA, TIMER3, 1},
+ /* D8/PA7 */
+ {GPIOA_BASE, 7, ADC7, TIMER3_CH2_CCR, EXTI_CONFIG_PORTA, TIMER3, 2},
+ /* D9/PB0 */
+ {GPIOB_BASE, 0, ADC8, TIMER3_CH3_CCR, EXTI_CONFIG_PORTB, TIMER3, 3},
+ /* D10/PB1 */
+ {GPIOB_BASE, 1, ADC9, TIMER3_CH4_CCR, EXTI_CONFIG_PORTB, TIMER3, 4},
+ /* D11/PB2 */
+ {GPIOB_BASE, 2, ADC_INVALID, TIMER_INVALID, EXTI_CONFIG_PORTB, TIMER_INVALID, TIMER_INVALID},
+ /* D12/PB10 */
+ {GPIOB_BASE, 10, ADC_INVALID, TIMER_INVALID, EXTI_CONFIG_PORTB, TIMER_INVALID, TIMER_INVALID},
+ /* D13/PB11 */
+ {GPIOB_BASE, 11, ADC_INVALID, TIMER_INVALID, EXTI_CONFIG_PORTB, TIMER_INVALID, TIMER_INVALID},
+ /* D14/PB13 */
+ {GPIOB_BASE, 13, ADC_INVALID, TIMER_INVALID, EXTI_CONFIG_PORTB, TIMER_INVALID, TIMER_INVALID},
+ /* D15/PB14 */
+ {GPIOB_BASE, 14, ADC_INVALID, TIMER_INVALID, EXTI_CONFIG_PORTB, TIMER_INVALID, TIMER_INVALID},
+ /* D16/PB15 */
+ {GPIOB_BASE, 15, ADC_INVALID, TIMER_INVALID, EXTI_CONFIG_PORTB, TIMER_INVALID, TIMER_INVALID},
+ /* D17/PA8 */
+ {GPIOA_BASE, 8, ADC_INVALID, TIMER1_CH1_CCR, EXTI_CONFIG_PORTB, TIMER1, 1},
+ /* D18/PA9 */
+ {GPIOA_BASE, 9, ADC_INVALID, TIMER1_CH2_CCR, EXTI_CONFIG_PORTA, TIMER2, 2},
+ /* D19/PA10 */
+ {GPIOA_BASE, 10, ADC_INVALID, TIMER1_CH3_CCR, EXTI_CONFIG_PORTA, TIMER1, 3},
+ /* D20/PA11 */
+ {GPIOA_BASE, 11, ADC_INVALID, TIMER1_CH4_CCR, EXTI_CONFIG_PORTA, TIMER1, 4},
+ /* D21/PA12 */
+ {GPIOA_BASE, 12, ADC_INVALID, TIMER_INVALID, EXTI_CONFIG_PORTA, TIMER_INVALID, TIMER_INVALID},
+ /* D22/PA13 */
+ {GPIOA_BASE, 13, ADC_INVALID, TIMER_INVALID, EXTI_CONFIG_PORTA, TIMER_INVALID, TIMER_INVALID},
+ /* D23/PA14 */
+ {GPIOA_BASE, 14, ADC_INVALID, TIMER_INVALID, EXTI_CONFIG_PORTA, TIMER_INVALID, TIMER_INVALID},
+ /* D24/PA15 */
+ {GPIOA_BASE, 15, ADC_INVALID, TIMER_INVALID, EXTI_CONFIG_PORTA, TIMER_INVALID, TIMER_INVALID},
+ /* D25/PB3 */
+ {GPIOB_BASE, 3, ADC_INVALID, TIMER_INVALID, EXTI_CONFIG_PORTB, TIMER_INVALID, TIMER_INVALID},
+ /* D26/PB4 */
+ {GPIOB_BASE, 4, ADC_INVALID, TIMER_INVALID, EXTI_CONFIG_PORTB, TIMER_INVALID, TIMER_INVALID},
+ /* D27/PB5 */
+ {GPIOB_BASE, 5, ADC_INVALID, TIMER_INVALID, EXTI_CONFIG_PORTB, TIMER_INVALID, TIMER_INVALID},
+ /* D28/PB6 */
+ {GPIOB_BASE, 6, ADC_INVALID, TIMER4_CH1_CCR, EXTI_CONFIG_PORTB, TIMER4, 1},
+ /* D29/PB7 */
+ {GPIOB_BASE, 7, ADC_INVALID, TIMER4_CH2_CCR, EXTI_CONFIG_PORTB, TIMER4, 1},
+ /* D30/PC13 */
+ {GPIOC_BASE, 13, ADC_INVALID, TIMER_INVALID, EXTI_CONFIG_PORTC, TIMER_INVALID, TIMER_INVALID},
+ /* D31/PC14 */
+ {GPIOC_BASE, 14, ADC_INVALID, TIMER_INVALID, EXTI_CONFIG_PORTC, TIMER_INVALID, TIMER_INVALID},
+ /* D32/PB8 */
+ {GPIOB_BASE, 8, ADC_INVALID, TIMER4_CH3_CCR, EXTI_CONFIG_PORTB, TIMER4, 3},
+ /* D33/PB12 */
+ {GPIOB_BASE, 12, ADC_INVALID, TIMER_INVALID, EXTI_CONFIG_PORTB, TIMER_INVALID, TIMER_INVALID},
+ };
+
+
+#endif
+
#ifndef CYCLES_PER_MICROSECOND
#error "Board type has not been selected correctly."
#endif
diff --git a/wirish/ext_interrupts.c b/wirish/ext_interrupts.c
index f02cdc5..dd7c1a8 100644
--- a/wirish/ext_interrupts.c
+++ b/wirish/ext_interrupts.c
@@ -32,17 +32,8 @@
#include "exti.h"
#include "ext_interrupts.h"
-/**
- * @brief Attach an interrupt handler to be triggered on a given
- * transition on the pin. Runs in interrupt context
- *
- * @param pin Maple pin number
- * @param handler Function to run upon external interrupt trigger.
- * @param mode Type of transition to trigger on, eg falling, rising, etc.
- *
- * @sideeffect Registers a handler
- */
-void attachInterrupt(uint8 pin, voidFuncPtr handler, uint32 mode) {
+/* Attach ISR handler on pin, triggering on the given mode. */
+void attachInterrupt(uint8 pin, voidFuncPtr handler, ExtIntTriggerMode mode) {
uint8 outMode;
/* Parameter checking */
@@ -65,6 +56,7 @@ void attachInterrupt(uint8 pin, voidFuncPtr handler, uint32 mode) {
outMode = EXTI_RISING_FALLING;
break;
default:
+ ASSERT(0);
return;
}
@@ -76,11 +68,7 @@ void attachInterrupt(uint8 pin, voidFuncPtr handler, uint32 mode) {
return;
}
-/**
- * @brief Disable an external interrupt
- * @param pin maple pin number
- * @sideeffect unregisters external interrupt handler
- */
+/* Disable any interrupts */
void detachInterrupt(uint8 pin) {
if (!(pin < NR_GPIO_PINS)) {
return;
diff --git a/wirish/ext_interrupts.h b/wirish/ext_interrupts.h
index fef8c8f..80e2e9e 100644
--- a/wirish/ext_interrupts.h
+++ b/wirish/ext_interrupts.h
@@ -22,6 +22,8 @@
* THE SOFTWARE.
*****************************************************************************/
+#include "libmaple_types.h"
+
/**
* @file ext_interrupts.h
*
@@ -31,17 +33,44 @@
#ifndef _EXT_INTERRUPTS_H_
#define _EXT_INTERRUPTS_H_
-enum {
- RISING,
- FALLING,
- CHANGE
-};
+/**
+ * The kind transition on an external pin which should trigger an
+ * interrupt.
+ */
+typedef enum ExtIntTriggerMode_ {
+ RISING, /**< To trigger an interrupt when the pin transitions LOW
+ to HIGH */
+ FALLING, /**< To trigger an interrupt when the pin transitions
+ HIGH to LOW */
+ CHANGE /**< To trigger an interrupt when the pin transitions from
+ LOW to HIGH or HIGH to LOW (i.e., when the pin
+ changes). */
+} ExtIntTriggerMode;
#ifdef __cplusplus
extern "C"{
#endif
-void attachInterrupt(uint8 pin, voidFuncPtr, uint32 mode);
+/**
+ * @brief Registers an interrupt handler on a pin.
+ *
+ * The interrupt will be triggered on a given transition on the pin,
+ * as specified by the mode parameter. The handler runs in interrupt
+ * context.
+ *
+ * @param pin Maple pin number
+ * @param handler Function to run upon external interrupt trigger.
+ * @param mode Type of transition to trigger on, e.g. falling, rising, etc.
+ *
+ * @sideeffect Registers a handler
+ */
+void attachInterrupt(uint8 pin, voidFuncPtr handler, ExtIntTriggerMode mode);
+
+/**
+ * @brief Disable any registered external interrupt.
+ * @param pin Maple pin number
+ * @sideeffect unregisters external interrupt handler
+ */
void detachInterrupt(uint8 pin);
#ifdef __cplusplus
diff --git a/wirish/io.h b/wirish/io.h
index 647e79c..f82e414 100644
--- a/wirish/io.h
+++ b/wirish/io.h
@@ -38,54 +38,125 @@
extern "C"{
#endif
+/**
+ * Specifies a GPIO pin behavior.
+ *
+ * Each of the GPIO pins on a Maple board may be configured using
+ * pinMode() to behave in a number of ways: as a digital output pin,
+ * or as an analog input pin, etc., depending on the particular pin.
+ *
+ * This enum specifies the complete set of possible configurations;
+ * not every pin can have all of these modes. For example, on the
+ * Maple, pin 15 may be configured as INPUT_ANALOG, but not as PWM.
+ * See your device's silkscreen and and the GPIO documentation for
+ * more information.
+ *
+ * @see pinMode()
+ */
typedef enum WiringPinMode {
- OUTPUT,
- OUTPUT_OPEN_DRAIN,
- INPUT,
- INPUT_ANALOG,
- INPUT_PULLUP,
- INPUT_PULLDOWN,
- INPUT_FLOATING,
- PWM,
- PWM_OPEN_DRAIN,
+ OUTPUT, /**< Basic digital output: when the pin is HIGH, the
+ voltage is held at +3.3v (Vcc) and when it is LOW, it
+ is pulled down to ground. */
+
+ OUTPUT_OPEN_DRAIN, /**< In open drain mode, the pin indicates
+ "low" by accepting current flow to ground
+ and "high" by providing increased
+ impedance. An example use would be to
+ connect a pin to a bus line (which is pulled
+ up to a positive voltage by a separate
+ supply through a large resistor). When the
+ pin is high, not much current flows through
+ to ground and the line stays at positive
+ voltage; when the pin is low the bus
+ "drains" to ground with a small amount of
+ current constantly flowing through the large
+ resistor from the external supply. In this
+ mode no current is ever actually /sourced/
+ from the pin. */
+
+ INPUT, /**< Basic digital input. The pin voltage is sampled; when
+ it is closer to 3.3v (Vcc) the pin status is high, and
+ when it is closer to 0v (ground) it is low. If no
+ external circuit is pulling the pin voltage to high or
+ low, it will tend to randomly oscillate and be very
+ sensitive to noise (eg a breath of air across the pin
+ will cause the state to flip). */
+
+ INPUT_ANALOG, /**< This is a special mode for when the pin will be
+ used for analog (not digital) reads. Enables ADC
+ conversion to be performed on the voltage at the
+ pin. */
+
+ INPUT_PULLUP, /**< The state of the pin in this mode is reported
+ the same way as with INPUT, but the pin voltage
+ is gently "pulled up" towards +3.3v. This means
+ the state will be high unless an external device
+ is specifically pulling the pin down to ground,
+ in which case the "gentle" pull up will not
+ affect the state of the input. */
+
+ INPUT_PULLDOWN, /**< The state of the pin in this mode is reported
+ the same way as with INPUT, but the pin voltage
+ is gently "pulled down" towards 0v. This means
+ the state will be low unless an external device
+ is specifically pulling the pin up to 3.3v, in
+ which case the "gentle" pull down will not
+ effect the state of the input. */
+
+ INPUT_FLOATING, /**< Synonym for INPUT. */
+
+ PWM, /**< This is a special mode for when the pin will be used for
+ PWM output (a special case of digital output). */
+
+ PWM_OPEN_DRAIN, /**< Like PWM, except that instead of alternating
+ cycles of LOW and HIGH, the voltage on the pin
+ consists of alternating cycles of LOW and
+ floating (disconnected). */
} WiringPinMode;
+/**
+ * Configure behavior of a GPIO pin.
+ *
+ * @param pin Pin to configure. One of: 0-38 (pin numbers as labeled
+ * on silkscreen), or D0-D38 (symbols for same)
+ * @param mode Mode corresponding to desired pin behavior.
+ * @see WiringPinMode
+ */
+void pinMode(uint8 pin, WiringPinMode mode);
-/* Set pin to mode
- * pinMode(pin, mode):
- * pin -> {0-38, D0-D39, A0-16}
- * mode -> {
- * INPUT/INPUT_DIGITAL
- * INPUT_PULLUP
- * INPUT_PULLDOWN
- * INPUT_ANALOG
- * OUTPUT/OUTPUT_PP
- * OUTPUT_OPEN_DRAIN
- * }
+/**
+ * Writes a (digital) value to a pin. The pin must have its
+ * mode set to <code>OUTPUT</code> or <code>OUTPUT_OPEN_DRAIN</code>.
+ *
+ * @param pin Pin to write to. One of: 0-38 (pin numbers as labeled
+ * on silkscreen), or D0-D38 (symbols for same)
+ * @param value Either LOW (write a 0) or HIGH (write a 1).
+ * @see pinMode()
*/
-void pinMode(uint8, uint8);
-
-/*
- * Writes VALUE to digital pin[0-38]
- * digitalWrite(pin, value):
- * pin -> {0-38, D0-D39, A0-16}
- * value -> LOW, HIGH;
-*/
-void digitalWrite(uint8, uint8);
-
-/* Read a digital value from pin, the pin mode must be set to
- * {INPUT, INPUT_PULLUP, INPUT_PULLDOWN}
- * digitalRead(pin)
- * pin -> {0-38, D0-D39, A0-16}
+void digitalWrite(uint8 pin, uint8 value);
+
+/**
+ * Read a digital value from a pin. The pin must have its mode set to
+ * one of INPUT, INPUT_PULLUP, and INPUT_PULLDOWN.
+ *
+ * @param pin Pin to read from. One of: 0-38 (pin numbers as labeled
+ * on silkscreen), or D0-D38 (symbols for same)
+ * @return LOW or HIGH.
+ * @see pinMode()
*/
uint32 digitalRead(uint8);
-/* Read an analog value from pin, the pin mode must be set
- * to INPUT_ANALOG
- * analogRead(pin)
- * pin -> {A0-A16}
+/**
+ * Read an analog value from pin. This function blocks during ADC
+ * conversion. The pin must have its mode set to INPUT_ANALOG.
+ *
+ * @param pin Pin to read from. One of: 0-38 (pin numbers as labeled
+ * on silkscreen), or D0-D38 (symbols for same)
+ * @return ADC-converted voltage, in the range 0--4095, inclusive.
+ * @see pinMode()
+ * @see analogReference()
*/
-uint32 analogRead(uint8);
+uint32 analogRead(uint8 pin);
#ifdef __cplusplus
} // extern "C"
diff --git a/wirish/pwm.h b/wirish/pwm.h
index fe170cd..6d0ddaf 100644
--- a/wirish/pwm.h
+++ b/wirish/pwm.h
@@ -36,7 +36,16 @@ extern "C"{
#endif
#define analogWrite pwmWrite
-void pwmWrite(uint8, uint16);
+
+/**
+ * Set the PWM duty.
+ *
+ * User code is expected to determine and honor the maximum value
+ * (based on the configured period). As a convenience, analogWrite is
+ * an alias of pwmWrite to ease porting Arduino code, though period
+ * and duty will have to be recalibrated
+ */
+void pwmWrite(uint8 pin, uint16 duty_cycle);
#ifdef __cplusplus
}
diff --git a/wirish/wirish_digital.c b/wirish/wirish_digital.c
index 9298b60..aa22196 100644
--- a/wirish/wirish_digital.c
+++ b/wirish/wirish_digital.c
@@ -22,8 +22,8 @@
* THE SOFTWARE.
*****************************************************************************/
-/**
- * @brief Arduino-compatible digital I/O implementation.
+/*
+ * Arduino-compatible digital I/O implementation.
*/
#include "wirish.h"
@@ -73,7 +73,8 @@ void pinMode(uint8 pin, WiringPinMode mode) {
gpio_set_mode(PIN_MAP[pin].port, PIN_MAP[pin].pin, outputMode);
if (PIN_MAP[pin].timer_num != TIMER_INVALID) {
- /* enable/disable timer channels if we're switching into or out of pwm */
+ /* enable/disable timer channels if we're switching into or
+ out of pwm */
if (pwm) {
timer_set_mode(PIN_MAP[pin].timer_num,
PIN_MAP[pin].timer_chan,