diff options
author | Marti Bolivar <mbolivar@mit.edu> | 2010-10-22 21:13:02 -0400 |
---|---|---|
committer | Marti Bolivar <mbolivar@mit.edu> | 2010-10-22 21:13:02 -0400 |
commit | 6c956a383834b66c29591294f0926ced22f3e3b7 (patch) | |
tree | 00214f59655cf96d747228d9ffb8f6059b63b31c | |
parent | dd7e6ecdafcb8938e23dfd18a36d70628fbc74bd (diff) | |
download | librambutan-6c956a383834b66c29591294f0926ced22f3e3b7.tar.gz librambutan-6c956a383834b66c29591294f0926ced22f3e3b7.zip |
maple mini runs blinky now.
still need usb descriptors to improve, and also nothing else is tested.
-rw-r--r-- | Makefile | 4 | ||||
-rw-r--r-- | libmaple/adc.h | 11 | ||||
-rw-r--r-- | libmaple/exti.h | 8 | ||||
-rw-r--r-- | libmaple/libmaple.h | 6 | ||||
-rw-r--r-- | libmaple/timers.c | 2 | ||||
-rw-r--r-- | libmaple/timers.h | 18 | ||||
-rw-r--r-- | support/ld/maple_mini/flash.ld | 211 | ||||
-rw-r--r-- | support/ld/maple_mini/jtag.ld | 186 | ||||
-rw-r--r-- | support/ld/maple_mini/ram.ld | 220 | ||||
-rw-r--r-- | wirish/HardwareTimer.cpp | 18 | ||||
-rw-r--r-- | wirish/HardwareTimer.h | 285 | ||||
-rw-r--r-- | wirish/boards.h | 79 | ||||
-rw-r--r-- | wirish/ext_interrupts.c | 20 | ||||
-rw-r--r-- | wirish/ext_interrupts.h | 41 | ||||
-rw-r--r-- | wirish/io.h | 149 | ||||
-rw-r--r-- | wirish/pwm.h | 11 | ||||
-rw-r--r-- | wirish/wirish_digital.c | 7 |
17 files changed, 1175 insertions, 101 deletions
@@ -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, |