From 9764fdc458ac8eb282d7cae10d43309e13f91fa0 Mon Sep 17 00:00:00 2001 From: Marti Bolivar Date: Mon, 11 Oct 2010 23:39:50 -0400 Subject: more sphinx docs --- source/_static/img/jtag-wiring.png | Bin 0 -> 40550 bytes source/bootloader.rst | 2 + source/gpio.rst | 168 ++++++++++++++------ source/i2c.rst | 87 ++++++++++- source/jtag.rst | 66 +++++++- source/language.rst | 10 +- source/pwm.rst | 159 ++++++++++++++++++- source/spi.rst | 66 +++++++- source/timers.rst | 310 ++++++++++++++++++++++++++++++++++++- source/usart.rst | 94 ++++++++++- source/usb.rst | 186 +++++++++++++++++++++- 11 files changed, 1089 insertions(+), 59 deletions(-) create mode 100644 source/_static/img/jtag-wiring.png diff --git a/source/_static/img/jtag-wiring.png b/source/_static/img/jtag-wiring.png new file mode 100644 index 0000000..9c63e82 Binary files /dev/null and b/source/_static/img/jtag-wiring.png differ diff --git a/source/bootloader.rst b/source/bootloader.rst index c4c5da2..fea1f35 100644 --- a/source/bootloader.rst +++ b/source/bootloader.rst @@ -88,6 +88,8 @@ bootloader was attractive, it became clear, after much toiling, we were going to have to write some custom drivers across several platforms to make everything work this way. +.. _bootloader-rev3: + Maple Rev3 ---------- diff --git a/source/gpio.rst b/source/gpio.rst index 2567221..9104fed 100644 --- a/source/gpio.rst +++ b/source/gpio.rst @@ -22,6 +22,8 @@ that pin), while some are not. .. contents:: Contents :local: +.. _pin-mapping-mega-table: + Pin Mapping Mega Table ---------------------- @@ -34,53 +36,121 @@ above). .. csv-table:: :header: "Pin", "STM32", ":ref:`ADC `", ":ref:`Timer `", ":ref:`I2C `", ":ref:`UART `", ":ref:`SPI `", "5v?" - "D0", "PA3", "ADC3", "TIM2_CH4", "-", "USART2_RX", "-", "No" - "D1", "PA2", "ADC2", "TIM2_CH3", "-", "USART2_TX", "-", "No" - "D2", "PA0", "ADC0", "TIM2_CH1_ETR", "-", "USART2_CTS", "-", "No" - "D3", "PA1", "ADC1", "TIM2_CH2", "-", "USART2_RTS", "-", "No" - "D4", "PB5", "-", "-", "ISC1_SMBA", "-", "-", "No" - "D5", "PB6", "-", "TIM4_CH1", "I2C1_SCL", "-", "-", "Yes" - "D6", "PA8", "-", "TIM1_CH1", "-", "USART1_CK", "-", "Yes" - "D7", "PA9", "-", "TIM1_CH2", "-", "USART1_TX", "-", "Yes" - "D8", "PA10", "-", "TIM1_CH3", "-", "USART1_RX", "-", "Yes" - "D9", "PB7", "-", "TIM4_CH2", "I2C1_SDA", "-", "-", "Yes" - "D10", "PA4", "ADC4", "-", "-", "USART2_CK", "SPI1_NSS", "No" - "D11", "PA7", "ADC7", "TIM3_CH2", "-", "-", "SPI1_MOSI", "No" - "D12", "PA6", "ADC6", "TIM3_CH1", - - SPI1_MISO No - D13 PA5 ADC5 - - - SPI1_SCK No - D14 PB8 - TIM4_CH3 - - - Yes - - Analog header - - D15 PC0 ADC10 - - - - No - D16 PC1 ADC11 - - - - No - D17 PC2 ADC12 - - - - No - D18 PC3 ADC13 - - - - No - D19 PC4 ADC14 - - - - No - D20 PC5 ADC15 - - - - No - - External header - - D21 PC13 - - - - - No - D22 PC14 - - - - - No - D23 PC15 - - - - - No - D24 PB9 - TIM4_CH4 - - - Yes - D25 PD2 - TIM3_ETR - - - Yes - D26 PC10 - - - - - Yes - D27 PB0 ADC8 TIM3_CH3 - - - No - D28 PB1 ADC9 TIM3_CH4 - - - No - D29 PB10 - - I2C2_SCL USART3_TX - Yes - D30 PB11 - - I2C2_SDA USART3_RX - Yes - D31 PB12 - TIM1_BKIN I2C2_SMBAL USART3_CK SPI2_NSS Yes - D32 PB13 - TIM1_CH1N - USART3_CTS SPI2_SCK Yes - D33 PB14 - TIM1_CH2N - USART3_RTS SPI2_MISO Yes - D34 PB15 - TIM1_CH3N - - SPI2_MOSI Yes - D35 PC6 - - - - - Yes - D36 PC7 - - - - - Yes - D37 PC8 - - - - - - - -Stub (unfinished). + "D0", "PA3", "ADC3", "TIM2_CH4", "-", "USART2_RX", "-", "No" + "D1", "PA2", "ADC2", "TIM2_CH3", "-", "USART2_TX", "-", "No" + "D2", "PA0", "ADC0", "TIM2_CH1_ETR", "-", "USART2_CTS", "-", "No" + "D3", "PA1", "ADC1", "TIM2_CH2", "-", "USART2_RTS", "-", "No" + "D4", "PB5", "-", "-", "ISC1_SMBA", "-", "-", "No" + "D5", "PB6", "-", "TIM4_CH1", "I2C1_SCL", "-", "-", "Yes" + "D6", "PA8", "-", "TIM1_CH1", "-", "USART1_CK", "-", "Yes" + "D7", "PA9", "-", "TIM1_CH2", "-", "USART1_TX", "-", "Yes" + "D8", "PA10", "-", "TIM1_CH3", "-", "USART1_RX", "-", "Yes" + "D9", "PB7", "-", "TIM4_CH2", "I2C1_SDA", "-", "-", "Yes" + "D10", "PA4", "ADC4", "-", "-", "USART2_CK", "SPI1_NSS", "No" + "D11", "PA7", "ADC7", "TIM3_CH2", "-", "-", "SPI1_MOSI", "No" + "D12", "PA6", "ADC6", "TIM3_CH1", "-", "-", "SPI1_MISO", "No" + "D13", "PA5", "ADC5", "-", "-", "-", "SPI1_SCK", "No" + "D14", "PB8", "-", "TIM4_CH3", "-", "-", "-", "Yes" + "D15", "PC0", "ADC10", "-", "-", "-", "-", "No" + "D16", "PC1", "ADC11", "-", "-", "-", "-", "No" + "D17", "PC2", "ADC12", "-", "-", "-", "-", "No" + "D18", "PC3", "ADC13", "-", "-", "-", "-", "No" + "D19", "PC4", "ADC14", "-", "-", "-", "-", "No" + "D20", "PC5", "ADC15", "-", "-", "-", "-", "No" + "D21", "PC13", "-", "-", "-", "-", "-", "No" + "D22", "PC14", "-", "-", "-", "-", "-", "No" + "D23", "PC15", "-", "-", "-", "-", "-", "No" + "D24", "PB9", "-", "TIM4_CH4", "-", "-", "-", "Yes" + "D25", "PD2", "-", "TIM3_ETR", "-", "-", "-", "Yes" + "D26", "PC10", "-", "-", "-", "-", "-", "Yes" + "D27", "PB0", "ADC8", "TIM3_CH3", "-", "-", "-", "No" + "D28", "PB1", "ADC9", "TIM3_CH4", "-", "-", "-", "No" + "D29", "PB10", "-", "-", "I2C2_SCL", "USART3_TX", "-", "Yes" + "D30", "PB11", "-", "-", "I2C2_SDA", "USART3_RX", "-", "Yes" + "D31", "PB12", "-", "TIM1_BKIN", "I2C2_SMBAL", "USART3_CK", "SPI2_NSS", "Yes" + "D32", "PB13", "-", "TIM1_CH1N", "-", "USART3_CTS", "SPI2_SCK", "Yes" + "D33", "PB14", "-", "TIM1_CH2N", "-", "USART3_RTS", "SPI2_MISO", "Yes" + "D34", "PB15", "-", "TIM1_CH3N", "-", "-", "SPI2_MOSI", "Yes" + "D35", "PC6", "-", "-", "-", "-", "-", "Yes" + "D36", "PC7", "-", "-", "-", "-", "-", "Yes" + "D37", "PC8", "-", "-", "-", "-", "-", "Yes" + +.. _gpio-modes: + +GPIO Modes +---------- + +``OUTPUT`` + + Basic digital output: when the pin set high the voltage is held at + +3.3V (|vcc|) and when set low it is pulled down to ground. + +``OUTPUT_OPEN_DRAIN`` + + In open drain mode, the pin indicates "low" by accepting current + flow to ground and "high" by providing increased impedance. An + example use would be to connect a pin to a bus line (which is + pulled up to a positive voltage by a separate supply through a + large resistor). When the pin is high, not much current flows + through to ground and the line stays at positive voltage; when the + pin is low the bus "drains" to ground with a small amount of + current constantly flowing through the large resistor from the + external supply. In this mode no current is ever actually + *sourced* from the pin. + +``INPUT`` (or ``INPUT_FLOATING``) + + Basic digital input. The pin voltage is sampled; when it is closer + to 3.3V (|vcc|) the pin status is high, and when it is closer to + 0V (ground) it is low. If no external circuit is pulling the pin + voltage to high or low, it will tend to randomly oscillate and be + very sensitive to noise (e.g., a breath of air across the pin will + cause the state to flip). + +``INPUT_PULLUP`` + + The state of the pin in this mode is reported the same way as with + INPUT, but the pin voltage is gently "pulled up" towards + +3.3V. This means the state will be high unless an external device + is specifically pulling the pin down to ground, in which case the + "gentle" pull up will not effect the state of the input. + +``INPUT_PULLDOWN`` + + The state of the pin in this mode is reported the same way as with + INPUT, but the pin voltage is gently "pulled down" towards + 0V. This means the state will be low unless an external device is + specifically pulling the pin up to 3.3V, in which case the + "gentle" pull down will not effect the state of the input. + +``INPUT_ANALOG`` + + This is a special mode for when the pin will be used for analog + (not digital) reads. See the :ref:`ADC ` page. + +``PWM`` + + This is a special mode for when the pin will be used for PWM + output (a special case of digital output). See the :ref:`PWM + ` page. + +.. TODO PWM_OPEN_DRAIN needs documentation + +Function Reference +------------------ + +``pinMode(pin_number, MODE)`` + + Usually called from within `setup()`_ to configure the pin. MODE + is one of the set listed :ref:`above `. + +``digitalRead(pin_number)`` + + Returns ``HIGH`` (|vcc|) or ``LOW`` (0V). + +``digitalWrite(pin_number, value)`` + + Sets the pin to ``HIGH`` or ``LOW``. .. _gpio-recommended-reading: @@ -90,5 +160,5 @@ Recommended Reading STMicro documentation for STM32F103RB microcontroller: * `All `_ - * `Datasheet (pdf) `_ - * `Reference Manual (pdf) `_ + * `Datasheet `_ (pdf) + * `Reference Manual `_ (pdf) diff --git a/source/i2c.rst b/source/i2c.rst index 34bd57c..564ca0e 100644 --- a/source/i2c.rst +++ b/source/i2c.rst @@ -4,4 +4,89 @@ |i2c| ===== -Stub. +.. note:: + + The i2c interface is currently only available from the 'i2c' branch + of the github `libmaple `_ + repository. + +|i2c| is a crude and easy-to-hack serial protocol that requires only +two wires/channels for communication between many devices. Every +message passed on the bus is between a *master* (who initiates the +message) and a *slave* device. Slaves are addressed using 7-bit +addresses (up to 127 unique devices); 10-bit addressing is possible, +but currently unimplemented. Every message consists of an arbitrary +combination of 8-bit reads and writes as requested by the master. +Higher level functionality, such as reading a particular register +value, is achieved by writing to set the memory location then reading +to pull out the data. + +Note that the master/slave designation is on a message-by-message +basis. The Maple can act as both a master (messages initiated by user +code) and slave device (responding to requests via configurable +interrupt handlers) at the same time. + +.. contents:: Contents + :local: + +Hardware/Circuit Design +----------------------- + +The Maple has two |i2c| ports. Port 1 (i2c1) has SDA on header D9 and +SCL on D5; Port 2 (i2c2) has SDA on D30 and SCL on D29. + +The Maple reliably communicates with up to a 400kHz clock speed; this +doesn't translate into a 400kbps data rate except in extreme cases +because of addressing and protocol overhead. We have tested clock +speeds up to a megahertz and have had mixed results; in theory it +could be possible to achieve even higher rates, but signal quality +degrades rapidly and the bus becomes unreliable. + +Proper wiring and pull-up resistor selection are essential when +incorporating |i2c| into a circuit, especially with data rates above +100kHz. In the lab, we usually use approximately 5kΩ resistors with +|vcc| (3.3V) as the high voltage, and try to connect the pullup +voltage as close to the SDA and SCL pins as possible. We recommend +looking at the ST reference website for |i2c| (see the +:ref:`recommended reading ` below), starting +with a slow clock rate (10kHz), and, if possible, using an +oscilloscope to debug any issues. + +Function Reference +------------------ + +The function API for |i2c| is not finished! See the `source code +`_ for +now. + +.. TODO link to libmaple I2C docs once (1) finished, (2) in master + +SMBus +----- + +The STM32 microcontroller has hardware support for SMBus; we simply +have not written software for it. The SMBAL line for i2c1 is on header +D4 and for i2c2 is on D31. + +.. TODO link to libmaple SMBus docs once (1) finished, (2) in master + +.. _i2c-recommended-reading: + +Recommended Reading +------------------- + +* `i2c-bus.org `_ +* `Wikipedia Article on i2c `_ +* `Arduino i2c/TWI reference `_ +* STMicro documentation for STM32F103RB microcontroller: + + * `All `_ + * `Datasheet + `_ + (pdf) + * `Reference Manual + `_ + (pdf) + * `Application Note on Advanced I2C Usage + `_ + (pdf) diff --git a/source/jtag.rst b/source/jtag.rst index e94229f..5301a18 100644 --- a/source/jtag.rst +++ b/source/jtag.rst @@ -4,4 +4,68 @@ JTAG ====== -Stub. +JTAG is an interface for low-level debugging of digital devices. It +gives instruction by instruction control over the microprocessor and +allows data to be read and written to arbitrary memory and register +locations. It is typically used with a debugging tool like `gdb +`_ when hacking low level routines +and hardware peripherals (we use it when working on :ref:`libmaple +`) or to flash a new bootloader. + +Note that the STM32 on the Maple has a built-in low level serial +debugger which could also be used to flash bootloaders, and that the +:ref:`ASSERT ` framework allows basic debugging over +a USART serial channel. We expect only advanced users to use this +feature. + +.. contents:: Contents + :local: + + +Wiring Diagram +-------------- + +.. figure:: /_static/img/jtag-wiring.png + :align: center + :alt: JTAG wiring diagram + + JTAG wiring diagram to connect a standard 20-pin ARM JTAG device to + the 8-pin JTAG port on the Maple. + +.. TODO jtag wiring diagram (above) looks terrible; replace it + +The Maple has holes for a 8-pin JTAG header but that header is not +soldered on by default. If you know ahead of time that you'll be +needing it, and you order `straight from LeafLabs +`_, add a comment to your order and we can +probably solder one on for no charge. Otherwise, you can simply +attach standard 0.1" pitch male header pins (either the exact 4x2 +block or two 4-pin pieces of breakaway straight header). For a one-off +usage hack, the header can be jammed in the holes and twisted to +ensure electrical contact; this is what we do to flash our bootloader. + +The above schematic assumes that the header has been soldered on to +the *bottom* of the board, not the top; most ribbon cable connectors +will interfere with the power header. If you don't want a header +coming off the bottom, you can use a slim connector and invert this +diagram appropriately. + + +Compatible Devices +------------------ + +We have had good experience with the `Olimex ARM-USB-OCD +`_ device, which costs +about 55 euro plus shipping (as of October 2010). + + +Recommended Reading +------------------- + +* `Wikipedia Article on Joint Test Action Group (JTAG) `_ +* `STM32/gdb/OpenOCD HOWTO `_ +* STMicro documentation for STM32F103RB microcontroller: + + * `All `_ + * `Datasheet (pdf) `_ + * `Reference Manual (pdf) `_ diff --git a/source/language.rst b/source/language.rst index 6eb5bc7..36cb801 100644 --- a/source/language.rst +++ b/source/language.rst @@ -484,6 +484,10 @@ Recommended Reading * STMicro documentation for STM32F103RB microcontroller: * `All documents `_ - * `Datasheet (pdf) `_ - * `Reference Manual (pdf) `_ - * `Programming Manual (pdf) `_ (assembly language and register reference) + * `Datasheet (pdf) + `_ + * `Reference Manual (pdf) + `_ + * `Programming Manual (pdf) + `_ + (assembly language and register reference) diff --git a/source/pwm.rst b/source/pwm.rst index c01e415..8d3a8d9 100644 --- a/source/pwm.rst +++ b/source/pwm.rst @@ -4,4 +4,161 @@ Pulse-Width Modulation (PWM) ============================== -Stub. +Pulse Width Modulation is a basic technique to create repeated square +waves (digital high/low voltage transitions) of user defined length +and duty cycle. It can be used as a way to encode an "analog" signal +on a single digital (high/low) line using the time between transitions +("pulse width") as the variable; this technique is commonly used to +send servo position and motor speed commands. Another use is to use to +the ratio of "high" and "low" time to approximate a voltage output; +this technique can be used to dim an LED or even (with careful +filtering) generate audio waveforms. + +.. contents:: Contents + :local: + +Overview +-------- + +The Maple has a large number of 16-bit PWM outputs, each connected to +one of 4 timers. Some configuration, such as the clock rate or +prescaling, must be common to the entire timer; see the :ref:`timer +documentation ` for more information. + +Note that unlike the Arduino, the Maple does not have PWM +functionality on pin D10; all other pins are :ref:`compatible +`. + +The following table shows which :ref:`timer ` generates which +PWM outputs. See the :ref:`pin mapping table ` +to track down exactly which timer *channel* corresponds to each pin. + +.. _pwm-timer-table: + +.. csv-table:: + :header: Timer, PWM Headers + :delim: | + + Timer1 | D6,D7,D8 + Timer2 | D0,D1,D2,D3 + Timer3 | D11,D12,D27,D28 + Timer4 | D5,D9,D14,D24 + +Background +---------- + +In its simplest form, the device is a single counter with two +variables. The counter starts at zero, and the output starts at +"high". The counter increments every clock cycle until it reaches the +first variable number, at which point the output goes "low". The +counter continues incrementing until it reaches the second variable at +which point the output goes "high" again and the counter resets to +zero. The time spent with output high is called the **pulse duration** +or **duty**; the total time before repeat is the **period**. + +This simple functionality could be approximated in software by setting +a GPIO high or low, but the beauty of PWM is that user code simply has +to configure the device and set the two variables and the device will +function on its own; no further microprocessor cycles will be +consumed, and a repeated high/low waveform will spew out. + +The Maple has 16-bit PWM resolution, which means that the counter and +variables can be as large as 65535, as opposed to 255 with 8-bit +resolution. With a 72MHz clock rate, a PWM output could have maximum +period of about one millisecond; using a :ref:`prescaler +` (clock divider) in front of the counter can increase +this maximum period. Setting the :ref:`period ` to +something other than the maximum value gives further control over the +total length of the waveform. However, this effectively limits the +resolution with which the duty can be modified: the duty must be less +than or equal to the period. + +Here are some commonly used PWM configurations (note that servos are +notoriously variable, especially the lower cost models): + ++-------------+----------+-----------+---------+---------------+------+ +|**Purpose** |**Period**|**Duty** |Prescaler|Period |Duty | +| |(ms) |(ms) | | | | ++=============+==========+===========+=========+===============+======+ +|LED throb |0.020 |0--0.020 |1 (none) |65535 (default)|0--767| +| | | | | | | ++-------------+----------+-----------+---------+---------------+------+ +|Servo control|20 |1.25 (0°) |21 |65535 (default)|4096 | +| | | | | | | +| | |1.50 (90°) |21 |65535 (default)|4915 | +| | | | | | | +| | |1.75 (180°)|21 |65535 (default)|5734 | +| | | | | | | ++-------------+----------+-----------+---------+---------------+------+ + +Function Reference +------------------ + +``pinMode(pin_num, PWM)`` + + This command is usually called from `setup()`_ to tell the + microcontroller that pin_num should be configured to PWM + output. ``PWM`` implies regular driven OUTPUT; ``PWM_OPEN_DRAIN`` is + also available (see the list of :ref:`GPIO modes ` for + more information). + +.. _pwm-pwmwrite: + +``pwmWrite(pin_num, value)`` + + This command sets the PWM duty. User code is expected to determine + and honor the maximum value (based on the configured period). As a + convenience, ``analogWrite`` is an alias for ``pwmWrite`` to ease + porting Arduino code, though period and duty will have to be + recalibrated (see :ref:`compatibility `). + +.. _pwm-overflow: + +``Timer1.setOverflow(overflow)`` + + This function sets the period ("reload" or "overflow") value for + an entire PWM timer bank. The value is 16bit (0 to 65535) and + determines the maximum value that can be written with + :ref:`pwmWrite() ` corresponding to 100% duty + cycle. This also affects the PWM frequency: the higher reload is, + the lower the PWM frequency will be. + + The PWM output pin starts HIGH, then the timer begins counting up + from zero (with frequency equal to 72MHz/:ref:`prescaler + `) until it hits the duty value, at which point it + drops to LOW. The timer then continues counting up until it hits + the total period (set with this function), at which point the + cycle starts again. + +.. _pwm-prescaler: + +``Timer[1,2,3,4].setPrescaleFactor(prescale)`` + + Find the appropriate timer for a given PWM header using the table + :ref:`above `, then set the prescaler. A + prescaler is a clock divider. The timer will normally count with + frequency equal to the STM32's normal clock (72MHz); this + corresponds to setting ``prescale`` to 1 (which is the default). + + If a longer frequency is desired, use a larger ``prescale`` value. + For instance, an 8MHz frequency can be achieved by setting + ``prescale`` to 9, since 72MHz / 9 = 8MHz. + + This function is normally called once from, `setup()`_, but the + timer can be reconfigured with a new prescaler at any time. + +Recommended Reading +------------------- + +* `Wikipedia Article on Pulse-width modulation + `_ +* `Arduino tutorial on PWM `_ +* `Secrets of Arduino PWM + `_ by Ken + Shirriff +* `So You Want To Use PWM, Eh? `_ at Non-Lexical Vocables +* STMicro documentation for STM32F103RB microcontroller: + + * `All `_ + * `Datasheet (pdf) `_ + * `Reference Manual (pdf) `_ diff --git a/source/spi.rst b/source/spi.rst index 8518f3c..3c29b79 100644 --- a/source/spi.rst +++ b/source/spi.rst @@ -4,4 +4,68 @@ SPI ===== -Stub. +The Maple has two SPI ports. The first has NSS on D10, MOSI on D11, +MISO on D12, and SCK on D13. The second has NSS on D31, SCK on D32, +MISO on D33, and MOSI on D34. + +.. _spi-speeds: + +Each port can be configured at one of the following speeds: + +* ``SPI_18MHZ``: 18 MHz +* ``SPI_9MHZ``: 9 MHz +* ``SPI_4_5MHZ``: 4.5 MHz +* ``SPI_2_25MHZ``: 2.25 MHz +* ``SPI_1_125MHZ``: 1.124 MHz +* ``SPI_562_500KHZ``: 562.500 KHz +* ``SPI_281_250KHZ``: 281.250 KHz +* ``SPI_140_625KHZ``: 140.625 KHz + +.. contents:: Contents + :local: + +Function Reference +------------------ + +``HardwareSPI Spi(number);`` + + This declaration must be included at the start of any sketch or + program that uses the SPI interface. The argument number is either + 1 or 2 and specifies which port to use. + +``Spi.begin(freq, endianness, mode)`` + + ``begin`` is usually called in `setup()`_ to configure the + baudrate of the given SPI port and to set up the header pins + appropriately. ``freq`` is one of the set listed :ref:`above + `; ``endianness`` is either ``LSBFIRST`` or + ``MSBFIRST``; mode is one of 0, 1, 2, 3, and specifies which "SPI + Mode" is used (see specification docs linked below). + +``Spi.begin()`` + + A default ``begin`` with no arguments is provided for the lazy; it + is equivalent to ``Spi.begin(SPI_1_125MHZ, MSBFIRST, 0)``. + +``Spi.send(data, size)`` + + Writes data into the port buffer to be transmitted as soon as + possible. ``data`` should be an array of type ``byte*``; ``size`` + should be the number of elements in ``data``. + +Recommended Reading +------------------- + +* `Wikipedia Article on Serial Peripheral Interface Bus (SPI) + `_ +* `Arduino reference on SPI + `_ +* `Hardcore SPI on Arduino `_ by kik64 +* STMicro documentation for STM32F103RB microcontroller: + + * `All `_ + * `Datasheet (pdf) + `_ + * `Reference Manual (pdf) + `_ + diff --git a/source/timers.rst b/source/timers.rst index c156d4f..e0e57cb 100644 --- a/source/timers.rst +++ b/source/timers.rst @@ -1,7 +1,315 @@ +.. highlight:: cpp + .. _timers: ======== Timers ======== -Stub. +There are four general purpose timers in the Maple microcontroller +that can be configured to generate periodic or delayed events with +minimal work done by the microcontroller. For example, the :ref:`PWM +` channels, once enabled, generate regular square-wave signals on +specific output pins that will continue even if the microcontroller is +busy crunching numbers or handling communications interrupts. By +attaching interrupt handlers to these channels (instead of just +changing the voltage on an external pin), more complex events like +printing to a serial port, updating a variable, or emitting a whale +mating call can be scheduled. You can even modify the configuration of +the timer itself at a regular interval; the possibilities are endless! + +The four timers each have four separate compare channels. Each timer +is a single 16-bit counter that can be configured with both a +prescaler and an overflow value. The prescaler acts as a divider of +the 72MHz system clock; without prescaling the counter would get up to +65536 (2 to the 16th power) and roll over more than a thousand times a +second; even with the full prescaler it rolls over about once a +minute. The overflow value is the maximum value the counter will go up +to. It defaults to the full 65535; smaller values will cause the +counter to reset to zero more frequently. + + +Caveats +------- + +**PWM Conflicts:** Because PWM functionality on a given pin depends on +the configuration of the timer and channel, you must chose your +channels carefully if you want to use both timer interrupts and PWM in +the same program. Refer to the full :ref:`pin mapping table +` to match up timer channels and Maple header +pin numbers. + +**Overhead:** there is some overhead associated with function and +interrupt calls (loading and unloading the stack, preparing state, +etc.) and this overhead can fudge your timing. Imperfect code +branching also means that, e.g., channel 1 interrupts may get called a +couple clock cycles sooner than a channel 4 interrupt, all other +configuration being the same. + +.. compound:: + + **Jitter:** other interrupts (USB, Serial, SysTick, or other + timers) can and will get called before or during the timer + interrupt routines, causing pseudo-random delays and other + frustrations. + + Disabling the USB port (by calling ``SerialUSB.end()``, or just + running off a battery) helps a lot, but then you lose the + auto-reset and communications functionality. This will require + that you put your Maple into :ref:`perpetual bootloader mode + ` before uploading a new + program to it. + + Disabling SysTick with ``systick_disable()`` helps as well. + However, calling this function will break the ``micros()`` and + ``millis()`` functions. + +**General:** working with timers and interrupts can be tricky and hard +to debug; they are a somewhat "advanced" topic. Start simple, test +with :ref:`ASSERT() `, and don't try to do too much +in your interrupt handlers! Make sure that what you're trying to do in +a handler isn't going to block other interrupts from firing (e.g. USB, +Serial, SysTick) if those other interrupts are important for your +program. + +.. _timers-modes: + +General Timer Modes +------------------- + +``TIMER_DISABLED`` + + Exactly what it sounds like: the timer stops counting, interrupts + are not called, and no state changes are output. + +``TIMER_PWM`` + + This is the default mode for pins after initialization. See the + :ref:`PWM docs ` for more information on this mode. + + .. note:: + + ``Timer1.setChannel1Mode(TIMER_PWM)`` may not work as expected; + if you want PWM functionality on a channel, make sure you don't + set it to something else. + +``TIMER_OUTPUTCOMPARE`` + + In this mode, the timer counts from 0 to the overflow value + repeatedly; every time the counter value reaches one of the + channel compare values, the corresponding interrupt is fired. + +SysTick Peripheral +------------------ + +The SysTick peripheral allows another, simple way to perform periodic +or delayed events. This separate timer does not conflict with any +other peripherals, but the associated 1kHz interrupt can jitter the +general purpose timer interrupts; this is clearly seen when running +VGA code, where the timing jitters are transformed into visual jags in +the image. The SysTick peripheral can be disabled by calling +``systick_disable()``, and re-enabled using ``systick_resume()``. + +Function Reference +------------------ + +For all of these functions, ``Timer1`` can be replaced with +``Timer2``, ``Timer3``, or ``Timer4``; the channel numbers also range +from 1 to 4. + +``Timer1.pause()``/\ ``Timer1.resume()`` + + These functions start and stop the counter without affecting the + rest of the configuration. These functions can be used during the + setup period to prevent interrupts from firing before they are + completely configured. Note that there is some function call + overhead with these functions, so they are not a perfect way to + align multiple timers to the same count value. + +``Timer1.setOverflow(val)`` + + Sets the overflow (or "reload") value for the whole timer; when + the counter reaches this value it resets to zero. Defaults to + 65535 (the largest unsigned 16bit integer); setting it to anything + lower will cause interrupts to be called more frequently (see the + setPeriod function below for a shortcut). This number sets the + maximum value for the channel compare values. + +``Timer1.setPrescaleFactor(val)`` + + The prescaler acts as a clock divider to slow down the rate at + which the counter increments. For example, the system clock rate + is 72MHz, so the counter will reach 65535 in (13.89 nanoseconds) * + (65535 counts) = (910.22 microseconds), or about a thousand times + a second. If you set the prescaler to 1098, then the clock rate is + effectively 65.56KHz, and the counter will reach 65536 in (15.25 + microseconds) * (65536 counts) = (0.999 seconds), or just about + once a second. Use the :ref:`setPeriod ` + function below if you are allergic to math! + +.. _timers-set-period: + +``Timer1.setPeriod(val)`` + + This tricky trick will configure the prescaler and overflow values + to generate a timer reload with a period as close to val + microseconds as possible. It returns the chosen overflow value, + which you can then use to set the channel compare values + appropriately: if you just want the interrupts to fire when the + clock rolls over and you don't care about the relative "phase", + you can always set the channel compare values to 1. + + Remember: a microsecond is 1/1,000,000th of a second, or 1/1,000 + of a millisecond. The prescaler itself is 16bit, so the longest + period that can be configured is 1/(72MHz) * (2^32) = (59.65 + seconds) or about a minute. You can get around this by creating an + interrupt that increments a 32-bit variable, by using the + ``millis()`` function, or by interfacing with an external + real-time-clock chip. + +``Timer1.setCount(val)``/\ ``Timer1.getCount()`` + + These functions let you mess with the counter's brains + directly. You can probably make it not work if you try! The timer + is 16bit, so ``val`` and the return value of ``getCount`` are + ``uint16``. + +``Timer1.setChannel1Mode(MODE)`` + + This sets the given channel (here 1) of the given timer (here 1) + to the given mode. See the :ref:`list above ` for + possible values; for interrupts you want ``TIMER_OUTPUTCOMPARE``. + +``Timer1.setCompare1(val)`` + + Sets the compare value for the given channel; when the counter + reaches this value the interrupt for this channel will fire if the + channel is in output compare mode and an interrupt is attached. + + By default this only changes the relative offsets between events + on a single timer ("phase"); they don't control the frequency with + which they occur. However, a common trick is to increment the + compare value manually in the interrupt handler so that the event + will fire again after the increment period. There can be a + different increment value for each channel, so this trick allows + events to be programmed at 4 different rates on a single timer! + Note that function call overhead means that the smallest increment + rate is a couple microseconds. + +``Timer1.attachCompare1Interrupt(function)``/\ ``Timer1.detachCompare1Interrupt()`` + + This is how to attach or disable an interrupt handlers to timer + channels; this what will get called when the counter reaches the + compare value set with ``setCompareN(val)``. ``function`` + (sometimes referred to as an ISR: "interrupt service routine") + should be of a type that does not accept or return any values + (C/C++ programmers: ``void (function*)(void)``). They are just + like any other function in your sketch/program and must be + initialized at the top of the file and defined below. + + ``function`` should try to do what it has to do as fast as + possible. Blinking the LED, some logic, PWM updates, and Serial + writes are fine; writing to SerialUSB or waiting for user input + can take a long time and other compare interrupts won't fire. Tip: + if you have a ``delay()`` in your ISR, you're probably doing it + wrong. + + Stay vigilant here... function pointers are serious business, and + once you go down that path you'll find yourself in a `forest of + parentheses `_ before you know it. + +Code Examples +------------- + +LED blink +^^^^^^^^^ + +\ :: + + #define LED_PIN 13 + #define LED_RATE 500000 // in microseconds; should give 0.5Hz toggles + + void handler_led(void); + + int toggle = 0; + + void setup() + { + // Set up the LED to blink + pinMode(LED_PIN, OUTPUT); + + // Setup Timer + Timer2.setChannel1Mode(TIMER_OUTPUTCOMPARE); + Timer2.setPeriod(LED_RATE); // in microseconds + Timer2.setCompare1(1); // overflow might be small + Timer2.attachCompare1Interrupt(handler_led); + } + + void loop() { + // Nothing! It's all in the interrupts + } + + void handler_led(void) { + toggle ^= 1; + digitalWrite(LED_PIN, toggle); + } + +Racing Counters +^^^^^^^^^^^^^^^ + +\ :: + + #define BUTTON_PIN 38 + + void handler_count1(void); + void handler_count2(void); + + int count1 = 0; + int count2 = 0; + + void setup() + { + // Set up BUT for input + pinMode(BUTTON_PIN, INPUT_PULLUP); + + // Setup Counting Timers + Timer3.setChannel1Mode(TIMER_OUTPUTCOMPARE); + Timer4.setChannel1Mode(TIMER_OUTPUTCOMPARE); + Timer3.pause(); + Timer4.pause(); + Timer3.setCount(0); + Timer4.setCount(0); + Timer3.setOverflow(30000); + Timer4.setOverflow(30000); + Timer3.setCompare1(1000); // somewhere in the middle + Timer4.setCompare1(1000); + Timer3.attachCompare1Interrupt(handler1); + Timer4.attachCompare1Interrupt(handler2); + Timer3.resume(); + Timer4.resume(); + } + + void loop() { + // Display the running counts + SerialUSB.print("Count 1: "); + SerialUSB.print(count1); + SerialUSB.print("\t\tCount 2: "); + SerialUSB.println(count2); + + // Run... while BUT is held, pause Count2 + for(int i = 0; i<1000; i++) { + if(digitalRead(BUTTON_PIN)) { + Timer4.pause(); + } else { + Timer4.resume(); + } + delay(1); + } + } + + void handler1(void) { + count1++; + } + void handler2(void) { + count2++; + } diff --git a/source/usart.rst b/source/usart.rst index 30c953a..19b054f 100644 --- a/source/usart.rst +++ b/source/usart.rst @@ -4,4 +4,96 @@ USART ======= -Stub. +.. contents:: + :local: + +Hardware/Circuit Design +----------------------- + +The Maple has 3 separate USART devices: ``Serial1``, ``Serial2``, and +``Serial3``. In the most simple use case, the RX and TX pins are used +to send data at a predetermined baudrate with the line voltage level +relative to ground. + ++-----------+--------+-----+ +|Port |Function|Pin | ++===========+========+=====+ +|``Serial1``|TX |D7 | +| | | | +| |RX |D8 | +| | | | +| |CK |D6 | ++-----------+--------+-----+ +|``Serial2``|TX |D1 | +| | | | +| |RX |D0 | +| | | | +| |CK |D10 | +| | | | +| |CTS |D2 | +| | | | +| |RTS |D3 | ++-----------+--------+-----+ +|``Serial3``|TX |D29 | +| | | | +| |RX |D30 | +| | | | +| |CK |D31 | +| | | | +| |CTS |D32 | +| | | | +| |RTS |D33 | ++-----------+--------+-----+ + +.. TODO make above less ugly + +Compatible Devices and Specifications +------------------------------------- + +We have successfully used the Maple USART ports with an FT232R-based USB-serial converter at up to 115200 baud; higher speeds should certainly be possible. + +Function Reference +------------------ + +In the following, you may replace ``SerialN`` with ``Serial1``, +``Serial2``, or ``Serial3``. + +``SerialN.begin(baudrate)`` + + ``SerialN.begin`` is usually called in `setup()`_ to configure the + baudrate of the given serial port and to set up the header pins + appropriately. It can be called at any time to reconfigure a port + or to change the baudrate. 9600 baud is the generic speed most + widely supported by other devices and terminals. + +``SerialN.print(...)``/\ ``SerialN.println(...)`` + + Writes data into the port buffer to be transmitted as soon as + possible. Accepts strings (``char*``). If a raw integer is + passed, the corresponding ASCII character will be transmitted; to + print out a number in human readable form add a second parameter + with the base system. + + For example, to print out the decimal number '1234' use + ``SerialN.print(1234, DEC)``; to print out the binary number + '1001', use ``SerialN.print(9, BIN)``. + +``SerialN.available()``/\ ``SerialN.read()`` + + ``SerialN.read()`` will return the next unread character that has + been received over the port. ``SerialN.available()`` returns how + many such bytes are available (or zero if none are). If none are + available, ``SerialN.read()`` will block/fail, so the usual + program structure is to poll with ``SerialN.available`` and only + read if a nonzero value is returned. + +Recommended Reading +------------------- + +* `Wikipedia article on Universal asynchronous receiver/transmitter (USART) `_ +* `Arduino reference on Serial `_ +* STMicro documentation for STM32F103RB microcontroller: + + * `All `_ + * `Datasheet `_ (pdf) + * `Reference Manual `_ (pdf) diff --git a/source/usb.rst b/source/usb.rst index 2460df5..1ce91f0 100644 --- a/source/usb.rst +++ b/source/usb.rst @@ -1,7 +1,191 @@ +.. highlight:: cpp + .. _usb: ===== USB ===== -Stub. +.. note:: Changes and Caveats + + The SerialUSB functionality was modified for the 0.0.6 IDE + release. It now includes a 50 millisecond timeout for writes and + does not try to detect if the USB host is "really" connected or + just enumerated and initialized. This means that if you have a + number of SerialUSB writes or prints in your code and you are not + monitoring on a computer your program will run much, much slower + than if it is being monitored or totally disconnected (battery). + + You can avoid this behavior by :ref:`deciphering the port status + ` using the DTR and RTS line status; the behavior + of these control lines is platform dependent and we no longer + interpret them by default. + +The Maple STM32 microprocessor includes a dedicated USB peripheral +which can be configured to act as a general USB slave device with +transfer rates up to 12Mbps (it unfortunately can't be configured as a +host or on-the-go device). By default, the peripheral is configured +for two uses: first, to receive sketch/program uploads from the IDE, +and second, to emulate a regular serial port for use as a terminal +(text read/write). + +The emulated terminal is relatively slow and inefficient; it is best +for transferring data at regular serial speeds (kilobaud). Users +requiring faster data transfer should consider implementing a +different communications protocol; the Maple could be reprogrammed to +appear as a mass storage device (thumb drive), human interface device +(mouse or keyboard), microphone, or any other USB device. + +The SerialUSB channel is also used as part of the auto-reset feature +of the IDE to program the board (on Maple Rev3): a :ref:`magic +sequence of control line toggles and transmitted data +` causes the Maple to reset itself and enter +bootloader mode. As an unfortunate consequence, the auto-reset will +not work if the IDE can not access the serial port, either due to a +conflict with another program (serial monitor) or because the +interface has been disabled from the Maple side (through +``SerialUSB.end()``). + +Function Reference +------------------ + +``SerialUSB.print(...)``/\ ``SerialUSB.println(...)`` + + Writes data into the port buffer to be transmitted as soon as + possible. Accepts strings (``char*``). If a raw integer is passed + the corresponding ASCII character will be transmitted; to print + out a number in human readable form add a second parameter with + the base system. For example, to print out the decimal number + "1234", use ``SerialUSB.print(1234, DEC)``; to print out the + binary number "1001", use ``SerialUSB.print(9, BIN)``. + +.. _usb-write: + +``SerialUSB.write(bytes)`` + + ``write`` is a lower-level function that writes bytes directly + into the buffer. :ref:`print() ` often calls this + function dozens of times to write out a single formatted number; + user code can optimize raw data speed by calling this function + with 64-byte chunks instead. + +``SerialUSB.available()``/\ ``SerialUSB.read()`` + + ``SerialUSB.read()`` will return the next available character + (``byte``) that has been received over the port. + ``SerialUSB.available()`` returns how many such bytes are actually + available. If there is no data, ``read`` will block/fail, so the + usual program structure is to poll with ``available`` and only + ``read`` if there are data to read. + +``SerialUSB.read(buffer, length)`` + + An alternative version of ``SerialUSB.read``; will write the next + ``length`` available characters into the array ``buffer``. + +``SerialUSB.pending()`` + + Returns the number of bytes waiting in the transmit + buffer. Usercode can use this to prevent any blocking/waiting when + using the direct :ref:`write ` functions, or to check + if data was actually requested/received by the host. + +``SerialUSB.getRTS()`` + + Returns the state (1 or 0) of the virtual RTS ("ready to send") + line. This can be used to guess if the USB host is actively + waiting for data (e.g., if a serial monitor program is running) or + just "configured" (i.e., the virtual serial port is configured, + but no program is reading data). + +``SerialUSB.getDTR()`` + + Returns the state (1 or 0) of the virtual DTR ("data terminal + ready") line. This can be used to guess if the USB host is + actively waiting for data (e.g., if a serial monitor program is + running) or just "configured" (i.e., the virtual serial port is + configured, but no program is reading data). + +.. TODO deprecate crap out of above two functions; write a sane API + +``SerialUSB.isConnected()`` + + Returns 1 if the USB host is connected and the virtual serial + interface is initialized (though not necessarily active). + Otherwise, returns 0. + +``SerialUSB.end()``/\ ``SerialUSB.begin()`` + + The USB peripheral is enabled by default so that the auto-reset + feature will work, but it can be disabled/restarted at any time + with the ``SerialUSB.end()`` and ``SerialUSB.begin()`` + functions. + + ``SerialUSB.end()`` is a relatively hard shutdown, similar to + unplugging the board; this may crash or confuse any programs + running host-side. Note that calling this function will require + the Maple to be put into :ref:`perpetual bootloader mode + ` before another program can + be uploaded onto it, unless ``SerialUSB.begin()`` is called before + the upload is attempted. + + It's probably wise to wait a few seconds between calls to + ``SerialUSB.end()`` and ``SerialUSB.begin()`` (or to + ``SerialUSB.begin()`` and ``SerialUSB.print()``) to let the + computer reconfigure. + +Code Examples +------------- + +.. _usb-safe-print: + +Safe Print +^^^^^^^^^^ + +This function should run smoothly and not block; the LED should blink +at roughly the same speed whether being monitored, running from +battery, or connected but not monitored. You may need to experiment +with the DTR/RTS logic for your platform and device configuration. :: + + #define LED_PIN 13 + + void setup() { + /* Set up the LED to blink */ + pinMode(LED_PIN, OUTPUT); + } + + void loop() { + // LED will stay off if we are disconnected; + // will blink quickly if USB is unplugged (battery etc) + if(SerialUSB.isConnected()) { + digitalWrite(LED_PIN, 1); + } + delay(100); + + // If this logic fails to detect if bytes are going to + // be read by the USB host, then the println() will fully + // many times, causing a very slow LED blink. + // If the characters are printed and read, the blink will + // only slow a small amount when "really" connected, and fast + // when the virtual port is only configured. + if(SerialUSB.isConnected() && (SerialUSB.getDTR() || SerialUSB.getRTS())) { + for(int i=0; i<10; i++) { + SerialUSB.println(123456,BIN); + } + } + digitalWrite(LED_PIN, 0); + delay(100); + } + +Recommended Reading +------------------- + +* `USB in a NutShell `_ overview from Beyond Logic +* `Wikipedia article on Universal Serial Bus (USB) `_ +* Linux Kernel documentation for `USB ACM `_ and `USB Serial `_ +* STMicro documentation for STM32F103RB microcontroller: + + * `All documents `_ + * `Datasheet `_ (pdf) + * `Reference Manual `_ (pdf) + * `Programming Manual `_ (pdf; assembly language and register reference) -- cgit v1.2.3