diff options
Diffstat (limited to 'notes')
-rw-r--r-- | notes/dac.txt | 67 | ||||
-rw-r--r-- | notes/dma-stm32f1.txt | 99 | ||||
-rw-r--r-- | notes/exti.txt | 41 | ||||
-rw-r--r-- | notes/fsmc.txt | 64 | ||||
-rw-r--r-- | notes/interrupts.txt | 346 | ||||
-rw-r--r-- | notes/pin-definitions.txt | 226 | ||||
-rw-r--r-- | notes/portable.txt | 33 | ||||
-rw-r--r-- | notes/stm32.txt | 63 | ||||
-rw-r--r-- | notes/timers.txt | 96 | ||||
-rw-r--r-- | notes/usb.txt | 72 | ||||
-rw-r--r-- | notes/vga.txt | 35 |
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 |