aboutsummaryrefslogtreecommitdiffstats
path: root/notes
diff options
context:
space:
mode:
Diffstat (limited to 'notes')
-rw-r--r--notes/dac.txt67
-rw-r--r--notes/dma-stm32f1.txt99
-rw-r--r--notes/exti.txt41
-rw-r--r--notes/fsmc.txt64
-rw-r--r--notes/interrupts.txt346
-rw-r--r--notes/pin-definitions.txt226
-rw-r--r--notes/portable.txt33
-rw-r--r--notes/stm32.txt63
-rw-r--r--notes/timers.txt96
-rw-r--r--notes/usb.txt72
-rw-r--r--notes/vga.txt35
11 files changed, 1142 insertions, 0 deletions
diff --git a/notes/dac.txt b/notes/dac.txt
new file mode 100644
index 0000000..3292936
--- /dev/null
+++ b/notes/dac.txt
@@ -0,0 +1,67 @@
+DAC
+-------------------------------------------------------------------------------
+
+There is an ST application note for the DACs; it provides a lot of
+context but doesn't help setup the peripheral very much.
+
+For the first code iteration we'll just use 12-bit right-aligned
+single writes, or DAC_DHR12Rx.
+
+Once data is loaded into the digital registers, there are a number of
+possible triggers to start conversion to analog output: external
+interrupts, software control, and timer events. We'll just use
+software triggering for now.
+
+There is (obviously) DMA support for DAC output.
+
+There are noise (via LFSR) output and triangle wave output features
+with variable amplitude.
+
+There are eleven modes to trigger output to both channels at the same
+time, as follows:
+
+ - Independent trigger:
+ (1) No wave generation
+ (2) Same LFSR
+ (3) Different LFSR
+ (4) Same triangle
+ (5) Different triangle
+ - (6) Simultaneous software start
+ - Simultaneous trigger:
+ (7) Without wave generation
+ (8) Same LFSR
+ (9) Different LFSR
+ (10) Same triangle
+ (11) Different triangle
+
+Buffering is enabled by default.
+
+Triangle Wave HOWTO
+-------------------------------------------------------------------------------
+
+In order to generate a full-amplitude triangle wave:
+
+ - Make the following settings in DAC_BASE->CR, for the DAC channel you
+ want: set MAMP to 1011 (amplitude 4095), WAVE to 10 (triangle),
+ TSEL to 111 (software trigger), TEN to 1 (trigger enabled), and
+ EN to 1 (chanel enabled).
+
+ - Set dac->DHR12Rx to 0 (where x is your channel). This gets added
+ to the triangle wave value at each trigger step.
+
+ - Now, forever: set DAC_SWTRIGR_SWTRIGx in dac->SWTRIGR, and wait
+ for it to get cleared by hardware.
+
+You can do something similar for noise (by setting WAVE to 01
+instead). You can also cause the waves to advance due to timer events
+or external line 9, by making appropriate settings to TSEL.
+
+TODO
+-------------------------------------------------------------------------------
+
+- Sine wave demo (using timer interrupts?)
+- Wirish implementation
+- Official docs
+- Higher performance modes?
+- Signal quality testing
+- DMA output
diff --git a/notes/dma-stm32f1.txt b/notes/dma-stm32f1.txt
new file mode 100644
index 0000000..97b23a0
--- /dev/null
+++ b/notes/dma-stm32f1.txt
@@ -0,0 +1,99 @@
+DMA Notes
+=========
+
+Medium-density devices have one DMA controller, DMA1. High-density
+devices and up also have DMA2. DMA1 has 7 channels; DMA2 has 5. Each
+channel multiplexes DMA requests from various peripherals, like so:
+
+Channel Capabilities
+--------------------
+
+DMA1:
+
+ * Channel 1: ADC1, TIM2_CH3, TIM4_CH1
+ * Channel 2: USART3_TX, TIM1_CH1, TIM2_UP, TIM3_CH3, SPI1_RX
+ * Channel 3: USART3_RX, TIM1_CH2, TIM3_CH4, TIM3_UP, SPI1_TX
+ * Channel 4: USART1_TX, TIM1_CH4, TIM1_TRIG, TIM1_COM, TIM4_CH2,
+ SPI2/I2S2_RX, I2C2_TX
+ * Channel 5: USART1_RX, TIM1_UP, TIM2_CH1, TIM4_CH3,
+ SPI2/I2S2_TX, I2C2_RX
+ * Channel 6: USART2_RX, TIM1_CH3, TIM3_CH1, TIM3_TRIG, I2C1_TX
+ * Channel 7: USART2_TX, TIM2_CH2, TIM2_CH4, TIM4_UP, I2C1_RX
+
+DMA2:
+
+ * Channel 1: TIM5_CH4, TIM5_TRIG, TIM8_CH3, TIM8_UP, SPI/I2S3_RX
+ * Channel 2: TIM8_CH4, TIM8_TRIG, TIM8_COM, TIM5_CH3, TIM5_UP, SPI/I2S3_TX
+ * Channel 3: TIM8_CH1, UART4_RX, TIM6_UP/DAC_CH1
+ * Channel 4: TIM5_CH2, SDIO, TIM7_UP/DAC_CH2
+ * Channel 5: ADC3, TIM8_CH2, TIM5_CH1, UART4_TX
+
+An example usage: via DMA1, channel 1, you can have ADC1 periodically
+dump converted data into an array in memory. The DMA controller can
+then interrupt you when the array is half-full and full, and if any
+error occurred.
+
+Since channels are multiplexed in hardware, you can't simultaneously
+use the same DMA channel to serve requests from two of its peripherals
+at the same time. For example, if you are using DMA 1 channel 1 to
+serve DMA requests from ADC1, you can't also serve requests from Timer
+2 channel 3.
+
+Channel Priority
+----------------
+
+An arbiter prioritizes simultaneous channel DMA requests. Channel
+priority levels are configurable (4 levels of priority). Ties within
+a DMA controller are broken by choosing the lower channel number;
+between the controllers, DMA1 has higher priority than DMA2.
+
+Interrupts
+----------
+
+You can cause an interrupt to fire once half the transfers are
+complete, when all the transfers are complete, if an error occurs
+during transfer, or any combination of the three.
+
+If an error occurs, the transfer is automatically disabled.
+
+Configuration
+-------------
+
+In order to configure a DMA transfer for DMA controller n, channel x,
+ST RM0008 says you should do the following:
+
+ A. Set the peripheral register address in DMAn_BASE->CPARx.
+ B. Set the memory address in DMAn_BASE->CMARx.
+ C. Set the number of data to be transferred in DMAn_BASE->CNDTRx.
+ D. Set the channel priority via the PL bits in DMAn_BASE->CCRx.
+ E. Configure various other things (e.g. data transfer sizes, what
+ events cause channel interrupts) in DMAn_BASE->CCRx as desired.
+ F. Activate the channel by setting ENABLE bit in DMAn_BASE->CCRx.
+
+The channel will start serving DMA requests as soon as it's activated.
+
+The DMA library lets you accomplish these tasks as follows:
+
+ **Setup transfer**
+
+ Do (A), (B), and (E) using dma_setup_transfer().
+
+ This also does (D), but chooses the lowest priority by default.
+
+ **Perform any other desired configuration**
+
+ You can do (C) using dma_set_num_transfers().
+
+ You can do (D) using dma_set_priority().
+
+ You can attach interrupt handlers with dma_attach_interrupt().
+
+ **Activate the channel**
+
+ Do (F) with dma_enable().
+
+Once you're all done, you can dma_disable() the channel. If you
+dma_detach_interrupt() an interrupt handler, the channel interrupts
+will stop firing, but the transfer itself won't stop until it's done
+(which never happens if you set the DMA_CIRC_MODE flag when you called
+dma_setup_transfer()).
diff --git a/notes/exti.txt b/notes/exti.txt
new file mode 100644
index 0000000..df930e5
--- /dev/null
+++ b/notes/exti.txt
@@ -0,0 +1,41 @@
+External interrupt notes.
+
+To generate the interrupt, the interrupt line should be configured
+and enabled. This is done by programming the two trigger registers
+with the desired edge detection and by enabling the interrupt
+request by writing a '1' to the corresponding bit in the interrupt
+mask register. When the selected edge occurs on the external
+interrupt line, an interrupt request is generated. The pending bit
+corresponding to the interrupt line is also set. This request is
+reset by writing a '1' in the pending register.
+
+Hardware interrupt selection:
+
+To configure the 20 lines as interrupt sources, use the following
+procedure:
+
+1) Configure AFIO_EXTICR[y] to select the source input for EXTIx
+ external interrupt
+2) Configure the mask bits of the 20 interrupt lines (EXTI_IMR)
+3) Configure the trigger selection bits of the interrupt lines
+ (EXTI_RTSR and EXTI_FTSR)
+4) Configure the enable and mask bits that control the NVIC_IRQ
+ channel mapped to the External Interrupt Controller (EXTI) so
+ that an inerrupt coming from one of the 20 lines can be
+ correctly acknowledged.
+
+AFIO clock must be on.
+
+RM0008, page 107: "PD0, PD1 cannot be used for external
+interrupt/event generation on 36, 48, 64-bin packages."
+
+The 16 EXTI interrupts are mapped to 7 interrupt handlers.
+
+EXTI Lines to Interrupt Mapping:
+EXTI0 -> EXTI0
+EXTI1 -> EXTI1
+EXTI2 -> EXTI2
+EXTI3 -> EXTI3
+EXTI4 -> EXTI4
+EXTI[5-9] -> EXT9_5
+EXTI[10-15] -> EXT15_10
diff --git a/notes/fsmc.txt b/notes/fsmc.txt
new file mode 100644
index 0000000..1f70760
--- /dev/null
+++ b/notes/fsmc.txt
@@ -0,0 +1,64 @@
+
+FSMC notes (for maple native and other "high density" STM32 devices)
+-------------------------------------------------------------------------------
+
+There is an application note for all this which is helpful; see the ST website.
+
+SRAM chip details
+ IS62WV51216BLL
+ 512k x 16
+ 19 address input
+ 16 data inputs
+ t_wc (write cycle) = 55ns
+ t_rc (write cycle) = 55ns
+ t_pwe1 (write enable low pulse) = 40ns
+ t_aa (address access) = 55ns
+
+
+The FSMC nomenclature is very confusing. There are three separate
+"banks" (which I will call "peripheral banks") each specialized for
+different types of external memory (NOR flash, NAND flash, SRAM,
+etc). We use the one for "PSRAM" with our SRAM chip; it's bank #1. The
+SRAM peripheral bank is further split into 4 "banks" (which I will
+call "channels") to support multiple external devices with chip select
+pins. I think what's going on is that there are 4 hardware peripherals
+and many sections of RAM; the docs are confusing about what's a "block
+of memeory" and what's an "FSMC block".
+
+Anyways, this all takes place on the AHB memory bus.
+
+I'm going to use not-extended mode 1 for read/write.
+
+Steps from application note:
+
+- enable bank3: BCR3_MBKEN = '1'
+- memory type is SRAM: BCR3_MTYP = '00'
+- databuse weidth is 16bits: BCR3_MWID = '01'
+- memory is nonmultiplexed: BCR3_MEXEN is reset (= '0')
+- everything else is cleared
+
+But not true! Actually write enable needs to be set.
+
+Using the application note, which is based around a very similar chip (with
+faster timing), I calculated an ADDSET (address setup) value of 0x0 and a
+DATAST (data setup) value of 0x3.
+
+Using channel1, NOR/PSRAM1 memory starts at 0x60000000.
+
+Have to turn on the RCC clock for all those GPIO pins, but don't need to use
+any interrupts.
+
+Not-super-helpful-link:
+http://www.keil.com/support/man/docs/mcbstm32e/mcbstm32e_to_xmemory.htm
+
+Note the possible confusion with address spaces, bitwidths, rollovers, etc.
+
+
+TODO
+-------------------------------------------------------------------------------
+- more rigorous testing: throughput, latency, bounds checking, bitwidth, data
+ resiliance, etc.
+- update .ld scripts to transparently make use of this external memory
+- test/demo using a seperate external SRAM chip or screen
+- write up documentation
+
diff --git a/notes/interrupts.txt b/notes/interrupts.txt
new file mode 100644
index 0000000..7434513
--- /dev/null
+++ b/notes/interrupts.txt
@@ -0,0 +1,346 @@
+Interrupt (IRQ) Handling in libmaple
+====================================
+
+There have been various threads asking about interrupt handling in
+libmaple. This file explains the libmaple interrupt handling
+interfaces, how libmaple organizes its interrupt handlers, and how to
+write new interrupt handlers.
+
+If you know the Cortex M3 and the libmaple sources pretty well, you
+can skip to the end to read how to add a new interrupt
+handler. Otherwise, read on.
+
+1. Interrupts in Wirish
+-----------------------
+
+There are very few Wirish-level convenience functions for handling
+interrupts. The most obvious one is attachInterrupt(), which is used
+for external interrupt handlers:
+
+http://leaflabs.com/docs/lang/api/attachinterrupt.html
+
+Another example is HardwareTimer::attachInterrupt(); a usage example is here:
+
+http://leaflabs.com/docs/lang/api/hardwaretimer.html#using-timer-interrupts
+
+What these have in common is that they take a pointer to the function
+the user wants to use as an interrupt handler, and pass it down to the
+libmaple proper interface for the subsystem. For example,
+attachInterrupt() calls exti_attach_interrupt(), and
+HardwareTimer::attachInterrupt() calls timer_attach_interrupt().
+
+So, as usual, the Wirish functions are just thin wrappers around the
+libmaple proper interfaces.
+
+2. Interrupts in libmaple proper
+--------------------------------
+
+The libmaple proper interfaces all use functions named
+foo_attach_interrupt(). So there's the exti_attach_interrupt() and
+timer_attach_interrupt() routines that have already been mentioned,
+but there are also some others which (at time of writing) don't have
+Wirish equivalents, like dma_attach_interrupt().
+
+These functions all behave the same way: they take a particular
+peripheral interrupt and a pointer to a user function, and they do
+whatever is necessary to turn on the interrupt line and ensure that
+the user's function gets called exactly when that interrupt occurs.
+
+This in itself is a useful abstraction above the hardware. To
+understand why, here's a bullet-point primer on how interrupts work on
+STM32/Cortex M3 (read about the NVIC in a Cortex M3 book to understand
+all the details; these are just the basics):
+
+- Each series of STM32 microcontroller (STM32F1, STM32F2, etc.)
+ specifies a certain number of IRQs (the libmaple type which
+ enumerates the IRQs is nvic_irq_num; see the libmaple/nvic.h
+ documentation for all the details).
+
+- Each IRQ has a number, which corresponds to a real, physical
+ interrupt line inside the processor. When you talk about an "IRQ",
+ you usually mean one of these interrupt lines.
+
+- The interrupt hardware can be configured to call a single function
+ per IRQ line when an interrupt associated with the IRQ has happened
+ (e.g. when a pin changes from low to high for an external
+ interrupt).
+
+- However, sometimes, various interrupts share an IRQ line. For
+ example, on Maple, external interrupts 5 through 9 all share a
+ single IRQ line (which has nvic_irq_num NVIC_EXTI_9_5). That means
+ that when any one (or any subset!) of those interrupts occurs, the
+ _same_ function (the IRQ handler for NVIC_EXTI_9_5) gets called.
+
+ When that happens, your IRQ handler has to figure out which
+ interrupt(s) it needs to handle (usually by looking at bitfields in
+ some sort of status register), do the right thing to handle them,
+ and then sometimes perform cleanup actions after finishing
+ (e.g. external interrupts need to clear pending masks, or the
+ interrupts will fire over and over again).
+
+So now it should make sense why libmaple's foo_attach_interrupt()
+handlers are convenient: they let you pretend that each interrupt has
+its own IRQ line, even though that's often not true. They also take
+care of set-up and clean-up tasks for you. This means a performance
+hit, but the convenience is usually worth it.
+
+3. Where libmaple keeps its IRQ Handlers
+----------------------------------------
+
+As noted above, for each nvic_irq_num, there's an IRQ line, and for
+each IRQ line, you can set up a single function to call. This section
+explains where libmaple keeps these functions and what they're called.
+
+You typically will only need the information in this section if
+there's no foo_attach_interrupt() routine for the kind of interrupt
+you're interested in. The discussion is at the hardware level, and
+assumes you know how the NVIC works. You can try looking in the
+(freely available) Cortex M3 Technical Reference Manual for the
+details, but Joseph Yiu's book, "The Definitive Guide to the Cortex
+M3" is a much more beginner-friendly resource, and covers everything
+you need to know.
+
+3.1: The vector table files (vector_table.S)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+While they don't contain interrupt handlers themselves, vector table
+files are where to look for what they're named.
+
+You can find the names libmaple expects for IRQ handlers by looking in
+the vector table file for the microcontroller you're interested
+in. This file is always named vector_table.S, but there are multiple
+such files throughout the libmaple source tree. This is because the
+different STM32 series and even lines and densities within a series
+(like the value and performance lines and low/medium/high/XL-densities
+for STM32F1) each have different sets of IRQs.
+
+For portability, then, the vector table files must live somewhere
+where nonportable code goes, namely, under libmaple/stm32f1/,
+libmaple/stm32f2/, etc. as appropriate. The libmaple build system
+knows which one to use for each board.
+
+For example, the vector table file for the microcontroller on the
+Maple (STM32F103RB, a medium-density performance line F1 -- whew!) is
+libmaple/stm32f1/performance/vector_table.S. Here's a snippet:
+
+ .globl __stm32_vector_table
+ .type __stm32_vector_table, %object
+
+ __stm32_vector_table:
+ /* CM3 core interrupts */
+ .long __msp_init
+ .long __exc_reset
+ .long __exc_nmi
+ .long __exc_hardfault
+ .long __exc_memmanage
+ .long __exc_busfault
+ .long __exc_usagefault
+[...]
+ .long __irq_exti0
+ .long __irq_exti1
+ .long __irq_exti2
+ .long __irq_exti3
+ .long __irq_exti4
+
+The names of the interrupt handlers appear one per line, after the
+.long. The names are chosen to make it pretty obvious what IRQ line is
+associated with the function. Additionally, since this is the actual
+vector table for the chip, the names appear in NVIC order, so you can
+check the interrupts and events chapter in the chip reference manual
+to make sure which IRQ line a function is associated with.
+
+3.2: Interrupts handled by libmaple
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The vector table file is just an assembly stub which defines the
+actual vector table (i.e., the initial stack pointer and table of
+function pointers that go at address 0x0), but it doesn't define the
+interrupts themselves. It leaves that up to the rest of libmaple.
+
+Though it doesn't handle them all, libmaple does provide many
+interrupt handlers when it can provide some useful default
+behavior. For example, it defines USART interrupt handlers that store
+received bytes in a ring buffer. It defines EXTI interrupt handlers
+that figure out which external interrupt actually fired, and call the
+corresponding user interrupt handler (which was set either with
+attachInterrupt() or exti_attach_interrupt()).
+
+When there is a default IRQ handler, it lives in a .c file for the
+peripheral the interrupt is related to. Again, usually for reasons of
+portability, these usually live somewhere series-specific. For
+instance, the USART IRQ handlers for Maple live in
+libmaple/stm32f1/usart.c. More rarely, they'll be in some top-level
+file under libmaple/ if the same interrupt is available on all
+supported series (e.g. at time of writing, the EXTI interrupts in
+libmaple/exti.c).
+
+Use the vector table file and grep to find IRQ handlers for the MCU
+you're interested in.
+
+3.3: Interrupts not handled by libmaple (isrs.S)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Though libmaple does provide some IRQ handlers, it doesn't define one
+for every available interrupt. This is true for various reasons: maybe
+the peripheral or interrupt isn't supported yet, maybe there's no
+useful default behavior, etc.
+
+In this case, it would be wasteful to have a separate function for
+each unhandled interrupt. To handle this, there's a single file that
+deals with all unhandled interrupts. Its name is isrs.S, and it lives
+in the same directory as the corresponding vector_table.S. For
+example, for Maple, the file is libmaple/stm32f1/performance/isrs.S.
+
+These aren't complicated; read the source to see how they work.
+
+4. Adding your own interrupt handlers
+-------------------------------------
+
+When adding an interrupt handler (or overriding a default one), you
+need to decide whether you want it for a particular program, or if
+what you're writing is general-purpose enough that it should live in
+libmaple itself.
+
+4.1 Adding a special-purpose interrupt handler
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+If you're just writing a one-off IRQ handler for your own use, your
+job isn't too complicated, provided you know the peripheral you're
+interested in well enough.
+
+You need to:
+
+1. Define an IRQ handler with the right name
+2. Turn on the IRQ line with nvic_irq_enable()
+3. Set any relevant interrupt enable bits in peripheral registers
+
+You first need to define a function with the right name. Look up the
+name in the vector table file for your board (see above). For example,
+to define your own SDIO interrupt handler for Maple, define a function
+named __irq_sdio():
+
+ void __irq_sdio(void) {
+ // Your handler goes here.
+ }
+
+The libmaple linker scripts are smart enough to notice that you've
+done this and put a pointer to this function in the appropriate place
+in the vector table.
+
+IMPORTANT: the function you define MUST HAVE C LINKAGE. C++ name
+mangling will confuse the linker, and it won't find your function. So
+if you're writing your IRQ handler in a C++ file, you need to define
+it like this:
+
+ extern "C" void __irq_sdio(void) {
+ // etc.
+ }
+
+To enable the interrupt, you need to call nvic_irq_enable() with the
+nvic_irq_num you want to enable. For SDIO, that looks like this:
+
+ nvic_irq_enable(NVIC_SDIO);
+
+This line typically goes in your setup code. Check the docs for
+<libmaple/nvic.h> to find the nvic_irq_num you need.
+
+Beyond that, you also sometimes need to set some interrupt enable bits
+in a register associated with the peripheral. These bits vary by
+peripheral; consult the reference manual for your chip for the
+details. For example, SDIO interupts are enabled using bits in the
+SDIO_MASK register.
+
+4.2 Adding a general-purpose interrupt handler
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Take this route only when you're sure your handler will be generally
+useful enough to ship with every copy of libmaple. Since the vector
+table is always present, your interrupt handler will consume every
+user's Flash. Normally, this is only worth it when defining some sort
+of foo_attach_interrupt() routine for a commonly used interrupt,
+though there are exceptions (e.g. the USART handlers).
+
+To add an interrupt handler, you need to define interrupt handlers
+with the appropriate names as described in the previous section. These
+will live under the series directory for the microcontroller you're
+using. For example, for Maple, they'd live under libmaple/stm32f1.
+
+DO NOT PUT THEM IN THE TOP-LEVEL LIBMAPLE DIRECTORY UNLESS THE
+INTERRUPT IS AVAILABLE ON ALL SUPPORTED SERIES, AND YOU CAN TEST IT ON
+MCUs FROM DIFFERENT SERIES.
+
+Just because an IRQ is available and purports to work the same way on
+multiple STM32 series doesn't mean that it in fact does. For example,
+there are silicon bugs related to I2C interrupt handling on STM32F1
+that require special-purpose workarounds. When in doubt, leave your
+handler in the series directory you can test. It can always be moved
+later.
+
+After you've added the handler, you need to add IRQ enable and disable
+routines for the peripheral. At the very least, this needs to take a
+pointer to the peripheral's device and an argument specifying which
+IRQ or IRQs to enable. For example, here are some timer IRQ
+enable/disable routines present in <libmaple/timer.h>:
+
+ /**
+ * @brief Enable a timer interrupt.
+ * @param dev Timer device.
+ * @param interrupt Interrupt number to enable; this may be any
+ * timer_interrupt_id value appropriate for the timer.
+ * @see timer_interrupt_id
+ * @see timer_channel
+ */
+ void timer_enable_irq(timer_dev *dev, uint8 interrupt);
+
+ /**
+ * @brief Disable a timer interrupt.
+ * @param dev Timer device.
+ * @param interrupt Interrupt number to disable; this may be any
+ * timer_interrupt_id value appropriate for the timer.
+ * @see timer_interrupt_id
+ * @see timer_channel
+ */
+ void timer_disable_irq(timer_dev *dev, uint8 interrupt);
+
+It's OK to take a flags argument for enabling/disabling multiple IRQs
+at once.
+
+If you're adding a foo_attach_interrupt(), it needs to work similarly,
+except it will also take a pointer to the user function to call when
+the interrupt occurs. When called, it must enable the correct NVIC
+line (which is usually available via the device pointer), as well as
+set any interrupt-enable bits in the appropriate peripheral register
+necessary to turn the interrupt on. Here's a timer example:
+
+ /**
+ * @brief Attach a timer interrupt.
+ * @param dev Timer device
+ * @param interrupt Interrupt number to attach to; this may be any
+ * timer_interrupt_id or timer_channel value appropriate
+ * for the timer.
+ * @param handler Handler to attach to the given interrupt.
+ * @see timer_interrupt_id
+ * @see timer_channel
+ */
+ void timer_attach_interrupt(timer_dev *dev,
+ uint8 interrupt,
+ voidFuncPtr handler) {
+ dev->handlers[interrupt] = handler;
+ timer_enable_irq(dev, interrupt);
+ enable_irq(dev, interrupt);
+ }
+
+You also need a corresponding foo_detach_interrupt() routine.
+
+In the case of IRQs for which a foo_attach_interrupt() routine is
+available, the IRQ handler needs to do any register inspection
+necessary to ensure the user handler is called only when the
+corresponding interrupt has occurred (for example, don't call timer
+capture/compare interrupt handlers due to an update event). How this
+works will depend on the peripheral.
+
+The IRQ handler must also perform any cleanup actions that are
+necessary. For example, various interrupts will cause the IRQ to fire
+until you clear some bits in a peripheral register. Users get confused
+and annoyed when their handlers get called forever. Clean up after
+them, so they don't need to worry about the details.
diff --git a/notes/pin-definitions.txt b/notes/pin-definitions.txt
new file mode 100644
index 0000000..2f462ea
--- /dev/null
+++ b/notes/pin-definitions.txt
@@ -0,0 +1,226 @@
+Pin definitions by GPIO bank.
+
+Source: ST DOC ID 14611, Datasheet for STM32F103xC, STM32F103xD,
+STM32F103xE, Table 5, pp. 30--35.
+
+Some additional peripheral GPIO information is given in the "Other"
+section following each bank's main table.
+
+This document was prepared carefully and is believed to be correct,
+but the final arbiter of truth is the ST datasheet.
+
+*** NB: UART 4 and 5 are NOT USART (columns are labeled appropriately).
+
+---------------------------------------------------------------------------
+GPIO ADC Timer FSMC I2S I2C USART SPI DAC 5v?
+---------------------------------------------------------------------------
+PA0 123in0 2ch1etr - - - 2cts - - -
+ 5ch1
+ 8etr
+PA1 123in1 5ch2 - - - 2rts - - -
+ 2ch2
+PA2 123in2 5ch3 - - - 2tx - - -
+ 2ch3
+PA3 123in3 5ch4 - - - 2rx - - -
+ 2ch4
+---------------------------------------------------------------------------
+PA4 12in4 - - - - 2ck 1nss out1 -
+PA5 12in5 - - - - - 1sck out2 -
+PA6 12in6 8bkin - - - - 1miso - -
+ 3ch1
+PA7 12in7 8ch1n - - - - 1mosi - -
+ 3ch2
+---------------------------------------------------------------------------
+PA8 - 1ch1 - - - 1ck - - Y
+PA9 - 1ch2 - - - 1tx - - Y
+PA10 - 1ch3 - - - 1rx - - Y
+PA11 - 1ch4 - - - 1cts - - Y
+---------------------------------------------------------------------------
+PA12 - 1etr - - - 1rts - - Y
+PA13 - - - - - - - - Y
+PA14 - - - - - - - - Y
+PA15 - - - 3ws - - 3nss - Y
+---------------------------------------------------------------------------
+
+Other:
+
+PA0: WKUP
+PA8: MCO
+PA11: USBDM, CAN_RX
+PA12: USBDP, CAN_TX
+PA13: JTMS-SWDIO (default)
+PA14: JTCK-SWCLK (default)
+PA15: JTDI (default)
+
+-------------------------------------------------------------------------------
+GPIO ADC Timer FSMC I2S I2C USART SPI DAC 5v? SDIO
+-------------------------------------------------------------------------------
+PB0 12in8 3ch3 - - - - - - - -
+ 8ch2n
+PB1 12in9 3ch4 - - - - - - - -
+ 8ch3n
+PB2 - - - - - - - - Y -
+PB3 - - - 3ck - - 3sck - Y -
+-------------------------------------------------------------------------------
+PB4 - - - - - - 3miso - Y -
+PB5 - - - 3sd 1smba - 3mosi - - -
+PB6 - 4ch1 - - 1scl - - - Y -
+PB7 - 4ch2 NADV - 1sda - - - Y -
+-------------------------------------------------------------------------------
+PB8 - 4ch3 - - - - - - Y D4
+PB9 - 4ch4 - - - - - - Y D5
+PB10 - - - - 2scl 3tx - - Y -
+PB11 - - - - 2sda 3rx - - Y -
+-------------------------------------------------------------------------------
+PB12 - 1bkin - 2ws 2smba 3ck 2nss - Y -
+PB13 - 1ch1n - 2ck - 3cts 2sck - Y -
+PB14 - 1ch2n - - - 3rts 2miso - Y -
+PB15 - 1ch3n - 2sd - - 2mosi - Y -
+-------------------------------------------------------------------------------
+
+Other:
+
+PB2: BOOT1
+PB3: JTDO (default)
+PB4: NJTRST (default)
+
+-------------------------------------------------------------------------------
+GPIO ADC Timer FSMC I2S I2C UART SPI DAC 5v? SDIO
+-------------------------------------------------------------------------------
+PC0 123in10 - - - - - - - - -
+PC1 123in11 - - - - - - - - -
+PC2 123in12 - - - - - - - - -
+PC3 123in13 - - - - - - - - -
+-------------------------------------------------------------------------------
+PC4 12in14 - - - - - - - - -
+PC5 12in15 - - - - - - - - -
+PC6 - 8ch1 - 2mck - - - - Y D6
+PC7 - 8ch2 - 3mck - - - - Y D7
+-------------------------------------------------------------------------------
+PC8 - 8ch3 - - - - - - Y D0
+PC9 - 8ch4 - - - - - - Y D1
+PC10 - - - - - 4tx - - Y D2
+PC11 - - - - - 4rx - - Y D3
+-------------------------------------------------------------------------------
+PC12 - - - - - 5tx - - Y CK
+PC13 - - - - - - - - - -
+PC14 - - - - - - - - - -
+PC15 - - - - - - - - - -
+-------------------------------------------------------------------------------
+
+Other:
+
+PC13: TAMPER_RTC
+PC14: OSC32_IN
+PC15: OSC32_OUT
+
+-------------------------------------------------------------------------------
+GPIO ADC Timer FSMC I2S I2C UART SPI DAC 5v? SDIO
+-------------------------------------------------------------------------------
+PD0 - - D2 - - - - - Y -
+PD1 - - D3 - - - - - Y -
+PD2 - 3etr - - - 5rx - - Y CMD
+PD3 - - CLK - - - - - Y -
+-------------------------------------------------------------------------------
+PD4 - - NOE - - - - - Y -
+PD5 - - NWE - - - - - Y -
+PD6 - - NWAIT - - - - - Y -
+PD7 - - NE1 - - - - - Y -
+ NCE2
+-------------------------------------------------------------------------------
+PD8 - - D13 - - - - - Y -
+PD9 - - D14 - - - - - Y -
+PD10 - - D15 - - - - - Y -
+PD11 - - A16 - - - - - Y -
+-------------------------------------------------------------------------------
+PD12 - - A17 - - - - - Y -
+PD13 - - A18 - - - - - Y -
+PD14 - - D0 - - - - - Y -
+PD15 - - D1 - - - - - Y -
+-------------------------------------------------------------------------------
+
+Other:
+
+PD0: OSC_IN (default)
+PD1: OSC_OUT (default)
+
+---------------------------------------------------------------------------
+GPIO ADC Timer FSMC I2S I2C USART SPI DAC 5v?
+---------------------------------------------------------------------------
+PE0 - 4etr NBL0 - - - - - Y
+PE1 - - NBL1 - - - - - Y
+PE2 - - A23 - - - - - Y
+PE3 - - A19 - - - - - Y
+---------------------------------------------------------------------------
+PE4 - - A20 - - - - - Y
+PE5 - - A21 - - - - - Y
+PE6 - - A22 - - - - - Y
+PE7 - - D4 - - - - - Y
+---------------------------------------------------------------------------
+PE8 - - D5 - - - - - Y
+PE9 - - D6 - - - - - Y
+PE10 - - D7 - - - - - Y
+PE11 - - D8 - - - - - Y
+---------------------------------------------------------------------------
+PE12 - - D9 - - - - - Y
+PE13 - - D10 - - - - - Y
+PE14 - - D11 - - - - - Y
+PE15 - - D12 - - - - - Y
+---------------------------------------------------------------------------
+
+Other:
+PE2: TRACECK
+PE3: TRACED0
+PE4: TRACED1
+PE5: TRACED2
+PE6: TRACED3
+
+---------------------------------------------------------------------------
+GPIO ADC Timer FSMC I2S I2C USART SPI DAC 5v?
+---------------------------------------------------------------------------
+PF0 - - A0 - - - - - Y
+PF1 - - A1 - - - - - Y
+PF2 - - A2 - - - - - Y
+PF3 - - A3 - - - - - Y
+---------------------------------------------------------------------------
+PF4 - - A4 - - - - - Y
+PF5 - - A5 - - - - - Y
+PF6 3in4 - NIORD - - - - - -
+PF7 3in5 - NREG - - - - - -
+---------------------------------------------------------------------------
+PF8 3in6 - NIOWR - - - - - -
+PF9 3in7 - CD - - - - - -
+PF10 3in8 - INTR - - - - - -
+PF11 - - NIOS16 - - - - - Y
+---------------------------------------------------------------------------
+PF12 - - A6 - - - - - Y
+PF13 - - A7 - - - - - Y
+PF14 - - A8 - - - - - Y
+PF15 - - A9 - - - - - Y
+---------------------------------------------------------------------------
+
+---------------------------------------------------------------------------
+GPIO ADC Timer FSMC I2S I2C USART SPI DAC 5v?
+---------------------------------------------------------------------------
+PG0 - - A10 - - - - - Y
+PG1 - - A11 - - - - - Y
+PG2 - - A12 - - - - - Y
+PG3 - - A13 - - - - - Y
+---------------------------------------------------------------------------
+PG4 - - A14 - - - - - Y
+PG5 - - A15 - - - - - Y
+PG6 - - INT2 - - - - - Y
+PG7 - - INT3 - - - - - Y
+---------------------------------------------------------------------------
+PG8 - - - - - - - - Y
+PG9 - - NE2 - - - - - Y
+ NCE3
+PG10 - - NCE4_1 - - - - - Y
+ NE3
+PG11 - - NCE4_2 - - - - - Y
+---------------------------------------------------------------------------
+PG12 - - NE4 - - - - - Y
+PG13 - - A24 - - - - - Y
+PG14 - - A25 - - - - - Y
+PG15 - - - - - - - - Y
+---------------------------------------------------------------------------
diff --git a/notes/portable.txt b/notes/portable.txt
new file mode 100644
index 0000000..549f4fb
--- /dev/null
+++ b/notes/portable.txt
@@ -0,0 +1,33 @@
+libmaple was previously very restricted to LeafLabs boards. However,
+the contents of libmaple proper are now fairly portable across medium-
+and high-density STM32F1xx chips (though there are some caveats). The
+current design is expected to accomodate new chips straightforwardly
+and well into the future.
+
+The library's configuration is based around the files wirish/boards.h
+(and .cpp), wirish/boards/*, and libmaple/stm32.h, as well as some
+#defines it expects the environment to handle during compilation.
+
+If you want to use libmaple proper, you must define one of
+STM32_MEDIUM_DENSITY or STM32_HIGH_DENSITY during compilation.
+Defining one of these allows libmaple to decide what processor
+features to expose to you (e.g., definitions related to ADC3 aren't
+compiled in when STM32_MEDIUM_DENSITY is defined). There's no support
+for low-density chips. XL-density is planned but not done (we don't
+have one to test on); patches (and samples) are welcome! See:
+
+ http://leaflabs.com/docs/libmaple/contributing.html
+
+There are some other useful #defines the environment can provide when
+compiling libmaple. They aren't as crucial, though. See the Makefile
+for more information.
+
+If you want to use Wirish, you'll need to define a BOARD_foo
+(e.g. BOARD_maple, BOARD_maple_mini, etc.). This determines which
+board files get loaded from wirish/boards/. See /wirish/boards.h and
+/wirish/boards.cpp for more details. See /wirish/boards/maple.h and
+/wirish/boards/maple.cpp for well-commented examples on how to add a
+new board configuration.
+
+The code in libmaple/usb/ is not very portable at all right now;
+expect this to change in the future.
diff --git a/notes/stm32.txt b/notes/stm32.txt
new file mode 100644
index 0000000..106b369
--- /dev/null
+++ b/notes/stm32.txt
@@ -0,0 +1,63 @@
+STM32 platform notes.
+
+Most of this information comes from ST AN3427 (Migrating a
+microcontroller application from STM32F1 to STM32F2 series) and ST
+AN3364 (How to migrate across STM32 series).
+
+The STM32 series of MCUs is divided into series. At time of writing,
+the available series are:
+
+- F1 series (STM32F1)
+- L1 series (STM32L1)
+- F2 series (STM32F2)
+- F4 series (STM32F4)
+
+Some notes on the characteristics of, and differences between, the
+series follow.
+
+F1 Series
+---------
+
+The STM32F1 series is further subdivided into a variety of somewhat
+compatible "lines". Performance, value, and connectivity line MCUs are
+available. (There's also an access line, which is ignored in these
+notes).
+
+At time of writing, libmaple supports medium- and high-density
+performance line MCUs. work to port it to other series is ongoing.
+
+Performance line MCU part numbers begin with STM32F101 or
+STM32F103. The performance line is further subdivided into
+"densities": low, medium, high, and XL.
+
+Value line MCU part numbers begin with STM32F100. Similarly to the
+performance line, the value line is subdivided into medium and high
+densities.
+
+Connectivity line MCU part numbers begin with STM32F105 or
+STM32F107. Mercifully, these are not further subdivided by density.
+
+F2 Series
+---------
+
+A revamp of the F1 series, The F2 series address a number of the
+STM32F1's deficiencies (both silicon bugs and unfortunate design
+decisions), while maintaining a fair amount of software and pin
+compatibility.
+
+The F2 series is most similar to the F1 connectivity line. Like the
+connectivity line, STM32F2s come with a USB on-the-go full speed
+peripheral (like the connectivity line), instead of USB full speed
+device (like the performance line).
+
+F4 Series
+---------
+
+The F4 series MCUs are essentially equivalent to those in the F2
+series, except they have an ARM Cortex M4 core, an FPU, and support a
+higher clock frequencies (168 MHz instead of 120 MHz).
+
+L1 Series
+---------
+
+This series is intended for low-power applications.
diff --git a/notes/timers.txt b/notes/timers.txt
new file mode 100644
index 0000000..647e92e
--- /dev/null
+++ b/notes/timers.txt
@@ -0,0 +1,96 @@
+Timers
+======
+
+Medium-density chips have timers 1 through 4. High- and XL-density
+chips additionally have timers 5 through 8. XL-density chips
+additionally have timers 9--14, which we don't support yet.
+
+Timer Capabilities
+------------------
+
+Each of timers 1--4 has 4 capture/compare (C/C) channels (also numbered
+1--4). These are directly used by PWM, but may serve other purposes as
+well (including handling user-specified periodic interrupts). The
+STM32 implementation is particularly featureful, with, e.g., the
+ability to chain together timers.
+
+Timers 1 and 8 are an advanced timers, with many more features.
+Wirish just uses just their capture/compare interrupts and enables MOE
+during initialization, essentially treating them as general purpose
+timers (like timers 2--5). Advanced timers also have separate break,
+update, and trigger interrupts that we only provide low-level
+(i.e. libmaple proper) support for.
+
+Timers 6 and 7 are basic timers, without C/C channels. They are still
+useful for interrupts (via NVIC_TIMER6, NVIC_TIMER7 IRQs, which can
+fire upon an update event), but they're most useful for controlling
+periodic DAC output.
+
+Known Issues and Other Caveats
+------------------------------
+
+There are some conflicts between timer C/C outputs and USART 1 and 2
+TX/RX. Wirish tries to handle this gracefully, but (as of 7 April
+2011) not all the bugs are sorted yet. In particular, if you call
+HardwareSerial::disable(), then try to use PWM, the USART TX pins
+don't cooperate.
+
+Resetting the prescaler or reload value only takes effect at the next
+update event. You can use timer_generate_update() to generate an
+update event via software.
+
+Other interrupts (SysTick, USB, Serial, etc.) can interfere with
+timing-critical applications. If your program requires precise
+timing, you should probably at least disable USB and SysTick. Note
+that this also disables the bootloader and stops millis()/micros()
+from counting.
+
+Getting really good timing is a bit of an art. If things don't work
+at first, you need to fiddle with an oscilloscope and the exact
+overflow/compare numbers to get precise behavior.
+
+TODO
+----
+
+- Document more carefully (e.g., determine clock-wise and
+ overflow-wise behavior for each function).
+
+- Track down and handle pin conflicts.
+
+- Input capture interface. DON'T WRITE pulseIn() IN TERMS OF THIS.
+ Do that as a simple, Arduino style implementation that just
+ busy-waits and uses micros(), to allow a pulseIn() on arbitrary
+ pins. Eventually, expose the more precise/harder to use timer-based
+ API via a convenience library.
+
+- Complementary outputs, with convenient break/dead time interface.
+
+- Additional modes (center-aligned PWM, one pulse mode, etc.) and
+ count configuration (down, up/down).
+
+Alternative Wirish Implementations
+----------------------------------
+
+The current Wirish API is big and clunky. Its inclusion by default
+also threatens making everyone's sketches bigger unnecessarily. We
+need to deprecate the parts of it that are bad for 0.0.10, and remove
+them when 0.1.0 comes out.
+
+Current implementation was inspired by Timer1 Library for Arduino:
+
+http://arduino.cc/pipermail/developers_arduino.cc/2010-June/002845.html
+
+Here's one of the more standard libaries out there:
+
+http://www.arduino.cc/playground/Code/Timer1
+
+ void initialize(long microseconds=1000000);
+ void start();
+ void stop();
+ void restart();
+ void setPeriod(long microseconds);
+ void pwm(char pin, int duty, long microseconds=-1);
+ void setPwmDuty(char pin, int duty);
+ void disablePwm(char pin);
+ void attachInterrupt(void (*isr)(), long microseconds=-1);
+ void detachInterrupt();
diff --git a/notes/usb.txt b/notes/usb.txt
new file mode 100644
index 0000000..9552b9f
--- /dev/null
+++ b/notes/usb.txt
@@ -0,0 +1,72 @@
+XXX
+XXX This file may be out of date!
+XXX
+
+[NOTE: this is a long term proposal. The current implementation just does a
+2ms TIMEOUT]
+
+SerialUSB Implementation
+-------------------------------------------------------------------------------
+The low-level serial USB implementation (in libmaple, written in C) is always
+non-blocking. A blocking implementation which polls with an optional timeout
+is in wirish (written in C++).
+
+begin() sets mode (and timeout if appropriate)
+end() disables the endpoint and hardware peripheral
+flush() clears the RX and TX buffers
+available() gives # of bytes in RX buffer
+pending() gives # of bytes in TX buffer
+read() gets one byte from RX buffer (or ??? if empty)
+getRTS()/getDTR() return control line status
+write(), print(), println(), see below
+
+there is nothing preventing the implementation of setTimeout(),
+flushTX/flushRX, etc, except for code size.
+
+NONBLOCKING (-1)
+ print() returns immediately with information about how much data was
+ transmitted. 64 bytes is the maximum that can be sent at a time, and
+ possibly less if buffer isn't empty. it's up to usercode to chunk up
+ larger datablocks, see if the buffer is full, etc
+
+ returns pending (max 64) if bytes got put in the TX buffer
+ returns 0 if buffer was full
+
+BLOCKING (0)
+ print() will block INDEFINATELY waiting for an open connection to send
+ an arbitrarily long array of bytes through with up to 64 bytes per packet.
+
+ returns sent (# of bytes added to the TX buffer successfully; all but the
+ last 64 or so will have been fully transmitted)
+
+TIMEOUT (the default, with 10ms. timeout period in ms)
+ print() will behave as in BLOCKING mode, except that it will timeout after
+ a given number of milliseconds. the timeout is not reset after every packet
+ is sent, so the device should be set with a large timeout if many packets
+ are going to be sent in one go, or the transmission will get cut off.
+
+ returns sent (# of bytes added to the TX buffer successfully; all but the
+ last 64 or so will have been fully transmitted)
+ returns 0 if buffer was full
+
+SerialUSB Design Decisions
+-------------------------------------------------------------------------------
+The USB port behaves differently from other serial interfaces, making a clean
+and simple "println()" implementation difficult. Data to be sent to the host is
+written into a small 64byte endpoint buffer, which the host may (or may not!)
+read from at any time. The RTS and DTR control lines /should/ indicate whether
+the host will empty out the endpoint buffer in a reasonable amount of time,
+but this is not reliable.
+
+From the usercode side, we want the println() function to accept strings up to
+hundreds of characters long (longer than the buffer) and get them sent out as
+quickly as possible, returning to code execution as quickly as possible. At the
+same time we don't want want to generate a large buffer because this will
+quickly eat up RAM. When the host device is not connected or not recieving
+bytes, the behavior of println can be undefined; it should return quickly and
+usercode should be able to determine if bytes were queued or not, but it isn't
+important what happens to the bytes. On the other hand, when the device /is/
+connected, we want to guarentee that bytes get sent in the appropriate order
+and none are missed.
+
+
diff --git a/notes/vga.txt b/notes/vga.txt
new file mode 100644
index 0000000..43b6830
--- /dev/null
+++ b/notes/vga.txt
@@ -0,0 +1,35 @@
+
+Notes on GPIO Writing
+------------------------------------------------------------------------------
+Classic digitalWrite() gives ~500ns pulse time (2MHz)
+
+gpio_write_bit() is about 360ns (2.78MHz)
+
+Writing to GPIO?_BASE is about 60ns (16.6MHz -> 18MHz)
+
+PWM write 0x0001 is about 14ns (72MHz) with prescaler as 0 (!)
+
+VGA Timing
+------------------------------------------------------------------------------
+1/25.125MHz = 39.72ns (640x480 pixel clock)
+
+Crude 640x480 directions:
+ From www.epanorama.net/documents/pc/vga_timing.html
+ 480 lines
+ 31.77 us horizontal line length -> 2287.44 clock cycles -> 2287
+ 3.77 us sync period -> 271 clocks -> 271
+ 1.89 us front porch? -> 136 clocks -> 136
+ 25.17 us video -> 1812.24 clocks -> 1812
+
+ So...
+ 2287 reload
+ 271 1: Hsync high
+ 407 2: Video on
+ 2219 3: Video off
+ 2287 4: Hsync low
+
+ Vertically, it's
+ 480 lines active video
+ 11 lines front porch
+ 2 lines Vsync (low)
+ 31 lines back porch