diff options
328 files changed, 18654 insertions, 10934 deletions
diff --git a/.gdbinit b/.gdbinit new file mode 100644 index 0000000..9e88d09 --- /dev/null +++ b/.gdbinit @@ -0,0 +1,9 @@ +target remote localhost:3333 +symbol-file build/maple.elf +source test.gdb +delete breakpoints +##break main.cpp:setup() +##monitor reset halt +#display/i $pc +# display/x *0xe000ed2c +# display/x *0xE000ED28 @@ -8,3 +8,4 @@ TAGS *.swp docs/doxygen/ arm +cscope* @@ -9,17 +9,48 @@ VENDOR_ID := 1EAF PRODUCT_ID := 0003 # Guess the MCU based on the BOARD (can be overridden ) +# FIXME the error LED config needs to be in wirish/ instead ifeq ($(BOARD), maple) MCU := STM32F103RB PRODUCT_ID := 0003 + ERROR_LED_PORT := GPIOA + ERROR_LED_PIN := 5 + DENSITY := STM32_MEDIUM_DENSITY endif ifeq ($(BOARD), maple_native) MCU := STM32F103ZE PRODUCT_ID := 0003 + ERROR_LED_PORT := GPIOC + ERROR_LED_PIN := 15 + DENSITY := STM32_HIGH_DENSITY endif ifeq ($(BOARD), maple_mini) MCU := STM32F103CB PRODUCT_ID := 0003 + ERROR_LED_PORT := GPIOB + ERROR_LED_PIN := 1 + DENSITY := STM32_MEDIUM_DENSITY +endif +ifeq ($(BOARD), maple_RET6) + MCU := STM32F103RE + PRODUCT_ID := 0003 + ERROR_LED_PORT := GPIOA + ERROR_LED_PIN := 5 + DENSITY := STM32_HIGH_DENSITY +endif + +# Some target specific things +ifeq ($(MEMORY_TARGET), ram) + LDSCRIPT := $(BOARD)/ram.ld + VECT_BASE_ADDR := VECT_TAB_RAM +endif +ifeq ($(MEMORY_TARGET), flash) + LDSCRIPT := $(BOARD)/flash.ld + VECT_BASE_ADDR := VECT_TAB_FLASH +endif +ifeq ($(MEMORY_TARGET), jtag) + LDSCRIPT := $(BOARD)/jtag.ld + VECT_BASE_ADDR := VECT_TAB_BASE endif # Useful paths @@ -32,13 +63,19 @@ BUILD_PATH = build LIBMAPLE_PATH := $(SRCROOT)/libmaple SUPPORT_PATH := $(SRCROOT)/support -# Useful variables -GLOBAL_CFLAGS := -Os -g3 -gdwarf-2 -mcpu=cortex-m3 -mthumb -march=armv7-m -nostdlib \ - -ffunction-sections -fdata-sections -Wl,--gc-sections \ - -DBOARD_$(BOARD) -DMCU_$(MCU) -GLOBAL_CXXFLAGS := -fno-rtti -fno-exceptions -Wall -DBOARD_$(BOARD) -DMCU_$(MCU) -GLOBAL_ASFLAGS := -mcpu=cortex-m3 -march=armv7-m -mthumb -DBOARD_$(BOARD) \ - -DMCU_$(MCU) -x assembler-with-cpp +# Compilation flags. +# FIXME remove the ERROR_LED config +GLOBAL_FLAGS := -D$(VECT_BASE_ADDR) \ + -DBOARD_$(BOARD) -DMCU_$(MCU) \ + -DERROR_LED_PORT=$(ERROR_LED_PORT) \ + -DERROR_LED_PIN=$(ERROR_LED_PIN) \ + -D$(DENSITY) +GLOBAL_CFLAGS := -Os -g3 -gdwarf-2 -mcpu=cortex-m3 -mthumb -march=armv7-m \ + -nostdlib -ffunction-sections -fdata-sections \ + -Wl,--gc-sections $(GLOBAL_FLAGS) +GLOBAL_CXXFLAGS := -fno-rtti -fno-exceptions -Wall $(GLOBAL_FLAGS) +GLOBAL_ASFLAGS := -mcpu=cortex-m3 -march=armv7-m -mthumb \ + -x assembler-with-cpp $(GLOBAL_FLAGS) LDDIR := $(SUPPORT_PATH)/ld LDFLAGS = -T$(LDDIR)/$(LDSCRIPT) -L$(LDDIR) \ @@ -49,20 +86,6 @@ LDFLAGS = -T$(LDDIR)/$(LDSCRIPT) -L$(LDDIR) \ include $(SUPPORT_PATH)/make/build-rules.mk include $(SUPPORT_PATH)/make/build-templates.mk -# Some target specific things -ifeq ($(MEMORY_TARGET), ram) - LDSCRIPT := $(BOARD)/ram.ld - VECT_BASE_ADDR := VECT_TAB_RAM -endif -ifeq ($(MEMORY_TARGET), flash) - LDSCRIPT := $(BOARD)/flash.ld - VECT_BASE_ADDR := VECT_TAB_FLASH -endif -ifeq ($(MEMORY_TARGET), jtag) - LDSCRIPT := $(BOARD)/jtag.ld - VECT_BASE_ADDR := VECT_TAB_BASE -endif - # Set all submodules here LIBMAPLE_MODULES := $(SRCROOT)/libmaple LIBMAPLE_MODULES += $(SRCROOT)/wirish @@ -130,7 +153,7 @@ debug: cscope: rm -rf *.cscope - find . -name '*.[hcs]' -o -name '*.cpp' | xargs cscope -b + find . -name '*.[hcS]' -o -name '*.cpp' | xargs cscope -b tags: etags `find . -name "*.c" -o -name "*.cpp" -o -name "*.h"` @@ -1,80 +1,106 @@ - _ _ _ _ - | (_) |__ _ __ ___ __ _ _ __ | | ___ + _ _ _ _ + | (_) |__ _ __ ___ __ _ _ __ | | ___ | | | '_ \| '_ ` _ \ / _` | '_ \| |/ _ \ | | | |_) | | | | | | (_| | |_) | | __/ |_|_|_.__/|_| |_| |_|\__,_| .__/|_|\___| - |_| by leaflabs! + |_| by LeafLabs! -The latest version of this repository can be found at: -http://github.com/leaflabs/libmaple +The latest version of this repository can be found here: -libmaple Repo Layout + http://github.com/leaflabs/libmaple + +General information ------------------------------------------------------------------------------ -/LICENSE - Licensing and copyright information -/main.cpp.example - main.cpp is required for a successful build but is non-existant by default; - use this file as a template for building your program. By default just - blinks an LED. +A HOWTO on setting up this library for use in a Unix environment is +available in our online HTML documentation: + + http://leaflabs.com/docs/unix-toolchain.html + +The HTML docs for this repository are automatically generated from the +source code and the files under the top-level /docs/ directory. For +more information, see /docs/README. + +Our bugtracker is available at: + + http://code.google.com/p/leaflabs/issues/list + +For changes that block official releases, see our wiki: + + http://wiki.leaflabs.com/index.php?title=Blocking_Changes + +Repository Layout +------------------------------------------------------------------------------ /build/ - Binary output -/libmaple/ - Lowest level definitions, routines, macros, and functions. This is the meat - of the library. + Compiler output -/wirish/ - Extra wrappers and functionality around the lower level code which is - useful for programming in the IDE. Files in here implement the "Wirish" - language, an Arduino "Wiring"-like language. +/contrib/ + + Community-contributed resources. LeafLabs doesn't maintain the + contents of this directory, so it may get stale. + +/docs/ + + Source code and build scripts for Doxygen and Sphinx + documentation. The libmaple HTML documentation is generated from + these files. It is available under http://leaflabs.com/docs/. /examples/ - What it sounds like. Copy these to /main.cpp to compile them. - -/support/ld/ - Linker scripts -/support/notes/ - Unstructured text notes that may be useful. The 45-maple.rules udev file - can be placed in /etc/udev/rules.d/ on compatible linux machines to allow - non-root access to the Maple USB device for uploading. + Example code and test programs. Copy these to /main.cpp to compile them. -Instructions to Compile for ARM Targets ------------------------------------------------------------------------------- -The best HOWTO for developing with this code is the "libmaple Unix Toolchain -Quickstart" guide at http://leaflabs.com/docs/libmaple/unix-toolchain/. +/libmaple/ -The Codesourcery g++ compiler for arm platforms is required. It is based on gcc -(they push changes into gcc a couple times a year), get the latest EABI version -from: + Lowest level definitions, routines, macros, and functions. This is + the meat of the library. The Arduino-like compatibility layer is + in /wirish/. - http://www.codesourcery.com/sgpp/lite/arm +/libraries/ -Note: grab the linux binaries for targeting the EABI platform (not to be -confused with the linux binaries /tageting/ the linux platform). + Special-purpose libraries that don't merit inclusion in the + /libmaple/ and /wirish/ directories, which are intended for + general use. Arduino-compatible libraries go here. -I unzip the archive ("TAR") version into a directory such as -~/bin/arm-gcc-codesourcery and then add the bin/ directory within that to my -$PATH in ~/.profile, resource that, then check that arm-none-eabi-gcc and -others are in my path. +/LICENSE -You will also need to have dfu-util installed and on your path (on Linux) or -compiled and placed in a folder "dfu-util/" at the same level as the -maple-library folder (macosx and windows). + Licensing and copyright information. -Write your program using /main.cpp as the entry point. Then just 'make help' -and follow the directions! +/main.cpp.example -Instructions to create a library ------------------------------------------------------------------------------- -If you have a complicated project with its own Makefile and multiple .c files, -or you're using an IDE that creates its own Makefile, you'll probably want to -load libmaple from an archive (a build-time library, not a DLL). Use the -"make library" target, and install build/libmaple.a and the appropriate headers -in your project's directory. At a minimum, the header set will include -libmaple.h, util.h, and libmaple_types.h + main.cpp is required for a successful build but is non-existent by + default; use this file as a template for building your program. By + default, just blinks an LED. + +/Makefile + + libmaple build instructions for GNU Make. + +/notes/ + + Unstructured text notes that may be useful. + +/README + + This file. + +/support/ + + Support files and scripts for various purposes. + + gdb/ GDB scripts. + ld/ Linker scripts. + make/ Additional scripts used by the top-level Makefile. + openocd/ OpenOCD scripts for JTAG debugging. + scripts/ Miscellany. + stm32loader.py Script for uploading via the built-in USART bootloader. + +/wirish/ + + Extra wrappers and functionality around the lower level code in + /libmaple/. These files implement an Arduino "Wiring"-like + library. @@ -1,55 +1,7 @@ -Quick TODO -------------------------------------------------------------------- +Bugtracker: -- write language unit test -- more maple-specific example programs -- maple mini serial bootloader HOWTO -- maple and mini jtag HOWTO +http://code.google.com/p/leaflabs/issues/list -Peripherals ------------------------------------------------------------------- - -- I2C wrap up -- SPI wrap up - -Arduino Library functions to port (these are LGPL) ---------------------------- - -EEPROM: - functions: - uint8 read(int) - void write(int, uint8) - -Firmata: - edit Firmata.h for capabilities - -Matrix: - soft spi port stuff - -SoftwareSerial: - clockCyclesToMicroseconds(int) - -Sprite: - all good? - -LiquidCrystal: - timing on soft serial send() commands - -Stepper: - requires millis() - -Wire: - reimplementation of the utility/twi.{c/h} code: - void twi_init(void); - void twi_setAddress(uint8_t); - uint8_t twi_readFrom(uint8_t, uint8_t*, uint8_t); - uint8_t twi_writeTo(uint8_t, uint8_t*, uint8_t, uint8_t); - uint8_t twi_transmit(uint8_t*, uint8_t); - void twi_attachSlaveRxEvent( void (*)(uint8_t*, int) ); - void twi_attachSlaveTxEvent( void (*)(void) ); - void twi_reply(uint8_t); - void twi_stop(void); - void twi_releaseBus(void); - -Ethernet: - ho baby... - uses avr/interrupt.h - reimplement utility/spi stuff +Blocking changes for official releases: +http://wiki.leaflabs.com/index.php?title=Blocking_Changes diff --git a/build-targets.mk b/build-targets.mk index 0718b15..207d324 100644 --- a/build-targets.mk +++ b/build-targets.mk @@ -11,7 +11,7 @@ library: $(BUILD_PATH)/libmaple.a .PHONY: library $(BUILD_PATH)/$(BOARD).elf: $(BUILDDIRS) $(TGT_BIN) $(BUILD_PATH)/main.o - $(SILENT_LD) $(CXX) $(LDFLAGS) -o $@ $(TGT_BIN) $(BUILD_PATH)/main.o + $(SILENT_LD) $(CXX) $(LDFLAGS) -o $@ $(TGT_BIN) $(BUILD_PATH)/main.o -Wl,-Map,$(BUILD_PATH)/$(BOARD).map $(BUILD_PATH)/$(BOARD).bin: $(BUILD_PATH)/$(BOARD).elf $(SILENT_OBJCOPY) $(OBJCOPY) -v -Obinary $(BUILD_PATH)/$(BOARD).elf $@ 1>/dev/null diff --git a/docs/Doxyfile b/docs/Doxyfile index 3290843..5fbb079 100644 --- a/docs/Doxyfile +++ b/docs/Doxyfile @@ -608,7 +608,7 @@ INPUT_ENCODING = UTF-8 # *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx # *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90 -FILE_PATTERNS = *.h +FILE_PATTERNS = *.h *.c # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. @@ -620,7 +620,9 @@ RECURSIVE = YES # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. -EXCLUDE = +# FIXME The USB thing needs to get redone (ST code stripped out, +# etc.). Until then, just ignore it. +EXCLUDE = ../libmaple/usb/ # The EXCLUDE_SYMLINKS tag can be used select whether or not files or # directories that are symbolic links (a Unix filesystem feature) are excluded @@ -1369,8 +1371,11 @@ INCLUDE_FILE_PATTERNS = # undefined via #undef or recursively expanded use the := operator # instead of the = operator. -PREDEFINED = ALWAYS_INLINE= \ - __cplusplus +PREDEFINED = __attribute__()= \ + __cplusplus \ + STM32_MEDIUM_DENSITY + STM32_HIGH_DENSITY + STM32_XL_DENSITY # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. diff --git a/docs/README b/docs/README index 326d278..7a50a54 100644 --- a/docs/README +++ b/docs/README @@ -1,16 +1,42 @@ -This directory contains the Sphinx documentation for libmaple, as well -as a Doxygen configuration file; we turn Doxygen XML output into -Sphinx documentation. You can generate HTML documentation using the -Makefile if you have make, or using make.bat from Windows. +This directory contains source files used to generate libmaple's +documentation. + +The generated documentation for the latest libmaple release is +available online in HTML form at http://leaflabs.com/docs/. The web +interface is the recommended way for users to read the documentation. + +This file contains instructions for generating the HTML files. + +About the Documentation +----------------------- + +The docs are written in Sphinx's extensions to reStructuredText +(reST). You can read more about Sphinx here: + + http://sphinx.pocoo.org/tutorial.html + +Much of the documentation is pulled out of the libmaple source code; +we use Doxygen XML output into Sphinx documentation. You can read +more about Doxygen here: + + http://doxygen.org + +We use a Sphinx plugin called Breathe to parse Doxygen's XML output +into a form usable by Sphinx. You can read more about Breathe here: + + http://michaeljones.github.com/breathe/ Documentation Build Steps ------------------------- +You first need to produce Doxygen XML output, then you can generate +the HTML documentation. + 1. You need a recent-ish version of Doxygen in your PATH: http://www.stack.nl/~dimitri/doxygen/download.html#latestsrc -2. Install breathe, which does Doxygen-to-Sphinx conversion: +2. Install Breathe, which does Doxygen-to-Sphinx conversion: Read/write version (for LeafLabs developers): @@ -37,9 +63,11 @@ Documentation Build Steps $ sudo easy_install -U Sphinx + You need Sphinx version >= 1.0.6. + 4. Before the first time you run Sphinx (and any time the Doxygen comments in the libmaple source code are changed), you'll need to - rebuild the Doxygen XML output: + build the Doxygen XML output: $ cd libmaple/docs/source $ doxygen @@ -48,36 +76,23 @@ Documentation Build Steps $ make html - (Read the Makefile for more targets). + On Windows, use the batch file make.bat instead. Reading and Modifying the Documentation --------------------------------------- -The net effect of the above is to produce Doxygen XML output (ignore -this) in libmaple/docs/doxygen/xml, and HTML documentation (this is -what you want) in libmaple/docs/build/html. - Just point your web browser at the file - libmaple/docs/build/html/index.html + /docs/build/html/index.html -it corresponds to the Sphinx file +It corresponds to the Sphinx file - libmaple/docs/source/index.rst - -All of the documentation itself lives in libmaple/docs/source/. The -directory source/_static/ is for static content (like style sheets); -source/_templates/ contains Sphinx templates (or, it would, if we had -any). - -The docs are written in Sphinx's version of reStructuredText (reST); -it's a Python thing that they use to produce the docs at -http://python.org. You can read more about Sphinx here: - - http://sphinx.pocoo.org/tutorial.html + /docs/source/index.rst -You can view the source for any generated page of documentation by -clicking the "Show Source" link in the sidebar. +The file /docs/source/conf.py is the Sphinx configuration file; you +can go read it for more information about our setup. -The file libmaple/docs/source/conf.py is the Sphinx configuration -file; you can go read it for more information about our setup. +All of the documentation which isn't pulled out of source code +comments lives in /docs/source/. The directory /docs/source/_static/ +is for static content (like style sheets); /docs/source/_templates/ +contains Sphinx templates. diff --git a/docs/source/_static/img/libmaple-screenshot-small.png b/docs/source/_static/img/libmaple-screenshot-small.png Binary files differdeleted file mode 100644 index f2be783..0000000 --- a/docs/source/_static/img/libmaple-screenshot-small.png +++ /dev/null diff --git a/docs/source/adc.rst b/docs/source/adc.rst index 6bbbac2..af613cc 100644 --- a/docs/source/adc.rst +++ b/docs/source/adc.rst @@ -6,23 +6,12 @@ Analog-Digital Conversion is the process of reading a physical voltage as a number. The Maple has a large number of pins which are capable of -taking 12-bit ADC measurements, which means that voltages from ground -to +3.3v are read as numbers from 0 to 4095; this corresponds to a +taking 12-bit ADC measurements, which means that voltages from 0 to +3.3V are read as numbers from 0 to 4095. This corresponds to a theoretical sensitivity of just under 1 millivolt. In reality, a -number of factors introduce noise and bias into this reading and a +number of factors introduce noise and bias into this reading, and a number of techniques must be used to get good precision and accuracy. -.. compound:: - - The header pins with ADC functionality (marked as "AIN" on the - silkscreen) are: - - D0, D1, D2, D3, D10, D11, D12, D13, D15, D16, D17, D18, D19, D20, D27, D28 - - Note that pins 3, 27, and 28 are not marked AIN on the silkscreen - for Maple revisions through Rev 5, however, they **do work** as - analog input pins. - .. contents:: Contents :local: @@ -31,13 +20,15 @@ number of techniques must be used to get good precision and accuracy. Noise and Bias -------------- +.. FIXME [0.0.10, Maple-specific] + The biggest issues with analog-digital conversion are noise and bias. With the Maple, we have tried to isolate the ADC pins and traces from -strong noise sources but there are always trade--offs between noise, +strong noise sources, but there are always trade--offs between noise, additional functionality, cost, and package size. The 6 ADC pins in a bank (D15--D20) generally have the least -noise and should be used for fine measurements. If the input voltage +noise, and should be used for fine measurements. If the input voltage changes relatively slowly, a number of samples can be taken in succession and averaged together, or the same voltage can even be sampled by multiple ADC pins at the same time. @@ -47,35 +38,40 @@ voltages that the sample is being compared against. In the case of the Maple, the high reference is |vcc| and the low reference is ground. This means that noise or fluctuations on either |vcc| or ground will affect the measurement. It also means that the voltage you are trying -to sample must be between ground and 3.3V. In the case of a variable -reading, it is best if the voltage varies over the entire range of -0--3.3V; otherwise, only a fraction of the sensitivity is being -leveraged. Resistor dividers and constant voltage diodes are basic -tools which can help bring a given voltage signal into the appropriate -range; opamps and other powered components can also be used. +to sample must be between ground and 3.3V. + +.. _adc-range: + +In the case of a variable reading, it is best if the voltage varies +over the entire range of 0--3.3V; otherwise, only a fraction of the +sensitivity is being leveraged. Some basic tools to accomplish this +are `resistor dividers +<http://en.wikipedia.org/wiki/Voltage_divider>`_ and `Zener diodes +<http://en.wikipedia.org/wiki/Voltage_source#Zener_voltage_source>`_\ +. However, `operational amplifiers +<http://en.wikipedia.org/wiki/Operational_amplifier>`_ and other +powered components can also be used if greater precision is required. .. _adc-function-reference: Function Reference ------------------ -.. doxygenfunction:: analogRead - -.. doxygenfunction:: pinMode - -.. doxygenenum:: WiringPinMode +* :ref:`lang-analogread` +* :ref:`lang-pinmode` .. _adc-recommended-reading: Recommended Reading ------------------- -* `Wikipedia article on Analog-to-digital converter <http://en.wikipedia.org/wiki/Analog-to-digital_converter>`_ -* `Arduino Analog Input Tutorial <http://arduino.cc/en/Tutorial/AnalogInputPins>`_ -* STMicro documentation for STM32F103RB microcontroller: +* `Wikipedia: Analog-to-Digital Converter + <http://en.wikipedia.org/wiki/Analog-to-digital_converter>`_ +* `Arduino Analog Input Tutorial + <http://arduino.cc/en/Tutorial/AnalogInputPins>`_ +* ST documentation: - * `All <http://www.st.com/mcu/devicedocs-STM32F103RB-110.html>`_ - * `Datasheet <http://www.st.com/stonline/products/literature/ds/13587.pdf>`_ (pdf) - * `Reference Manual <http://www.st.com/stonline/products/literature/rm/13902.pdf>`_ (pdf) - * `Application Note on ADC Modes (pdf) <http://www.st.com/stonline/products/literature/an/16840.pdf>`_ - * `Application Note on ADC Oversampling (pdf) <http://www.st.com/stonline/products/literature/an/14183.pdf>`_ + * `Application Note on ADC Modes + <http://www.st.com/stonline/products/literature/an/16840.pdf>`_ (PDF) + * `Application Note on ADC Oversampling + <http://www.st.com/stonline/products/literature/an/14183.pdf>`_ (PDF) diff --git a/docs/source/lang/api/cc-attribution.txt b/docs/source/arduino-cc-attribution.txt index e100140..ad1c1e0 100644 --- a/docs/source/lang/api/cc-attribution.txt +++ b/docs/source/arduino-cc-attribution.txt @@ -1,9 +1,9 @@ -.. Included in all this directory's files in order to satisfy the -.. Arduino CC Attribution-ShareAlike 3.0 License +.. Included in all relevant files in order to satisfy the Arduino +.. CC Attribution-ShareAlike 3.0 License .. admonition:: License and Attribution - This documentation page was adapted from the `Arduino Reference + Portions of this page were adapted from the `Arduino Reference Documentation <http://arduino.cc/en/Reference/HomePage>`_\ , which is released under a `Creative Commons Attribution-ShareAlike 3.0 License <http://creativecommons.org/licenses/by-sa/3.0/>`_. diff --git a/docs/source/compatibility.rst b/docs/source/arduino-compatibility.rst index 0d6319f..e6852a0 100644 --- a/docs/source/compatibility.rst +++ b/docs/source/arduino-compatibility.rst @@ -1,6 +1,6 @@ .. highlight:: cpp -.. _compatibility: +.. _arduino-compatibility: ============================= Maple-Arduino Compatibility @@ -41,16 +41,19 @@ means that programs aren't much larger (or are even smaller). Header Numbering and Incompatibilities -------------------------------------- +.. FIXME [Maple-specific values] + The numbering of headers is different; on the Maple each GPIO has a -unique number: D0, D1, D2, all the way up to D37 (actually, there are +unique number: 0, 1, 2, all the way up to 37 (actually, there are :ref:`a few more <jtag>`...). On the Arduino, the analog pins are numbered separately (A0-A5) from the digital pins (D0\ -D13). The incompatible hardware differences are: -* **I2C port**: on most Arduinos, the |i2c| port is Analog Input 4 - (SDA) and Analog Input 5 (SCL); on the Maple, |i2c| port 1 is D5 - (SCL) and D9 (SDA), and |i2c| port 2 is D29 (SCL) and D30 (SDA). +* :ref:`I2C <i2c>` **port**: on most Arduinos, the |i2c| port is Analog + Input 4 (SDA) and Analog Input 5 (SCL); on the Maple, |i2c| port 1 + is D5 (SCL) and D9 (SDA), and |i2c| port 2 is D29 (SCL) and D30 + (SDA). It should be possible to skywire, sacrificing signal quality (due to increased capacitance). Alternatively, |i2c| can be bit-banged @@ -59,9 +62,7 @@ The incompatible hardware differences are: * :ref:`PWM <pwm>` **on D10**: all the other standard Arduino PWM headers have PWM functionality on the Maple (D2,D3,D6,D9,D11), but - not D10. We did our best! It may be possible to reroute this - peripheral internally using low level configuration, but we haven't - looked in to it. + not D10. * **No External Voltage Reference**: The Arduino has an AREF pin which allows the use of an external ADC voltage reference; the Maple has @@ -96,15 +97,15 @@ differences, most of which are improvements: modes with a single call to ``pinMode()``: ``OUTPUT``, ``OUTPUT_OPEN_DRAIN``, ``INPUT_FLOATING``, ``INPUT_PULLUP``, ``INPUT_PULLDOWN``. Additionally, the PWM pins (labeled "PWM" on the - Maple's silkscreen) can be configured in ``PWM`` and - ``PWM_OPEN_DRAIN`` modes, and the analog input pins (labeled "AIN") - can be configured in ``INPUT_ANALOG`` mode. See the :ref:`GPIO - documentation <gpio>` for more information. + silkscreen) can be configured in ``PWM`` and ``PWM_OPEN_DRAIN`` + modes, and the analog input pins (labeled "AIN") can be configured + in ``INPUT_ANALOG`` mode. See the :ref:`GPIO documentation <gpio>` + for more information. * :ref:`Serial port <lang-serial>` **syntax**: like the `Arduino Mega <http://arduino.cc/en/Main/ArduinoBoardMega>`_, the Maple has multiple :ref:`USART ports <lang-serial>`. By default, ``Serial`` - is not mapped to any of them, use ``Serial1`` through ``Serial3`` + is not mapped to any of them. Use ``Serial1`` through ``Serial3`` instead. * **16-bit** :ref:`PWM <pwm>`: Arduino boards support 8-bit PWM, which @@ -227,7 +228,8 @@ function. - Check for ported library functionality. We intend to port all of the core and popular libraries (like Wire, Ethernet, and the LCD screen - driver), but this task is not yet finished. (Patches are welcome!) + driver), but this task is not yet finished. (:ref:`Patches are + welcome! <libmaple-contributing>`). - Check for peripheral conflicts; changing the configuration of timers and bus speeds for a feature on one header may impact all the diff --git a/docs/source/arm-gcc.rst b/docs/source/arm-gcc.rst index ef745f5..e97bb2f 100644 --- a/docs/source/arm-gcc.rst +++ b/docs/source/arm-gcc.rst @@ -4,12 +4,11 @@ GCC for Maple ============= -This document provides notes on the current usage of -``arm-none-eabi-gcc``, the `CodeSourcery <http://codesourcery.com>`_ -version of the GNU `GCC <http://gcc.gnu.org/>`_ compilers used to -compile programs for the Maple. It is not intended as a reference -manual for GCC; such manuals are available `elsewhere -<http://gcc.gnu.org/>`_. +This document provides notes on using of ``arm-none-eabi-gcc``, the +`CodeSourcery <http://www.codesourcery.com/>`_ version of the GNU `GCC +<http://gcc.gnu.org/>`_ compilers used for the Maple boards. It is +not intended as a reference manual for GCC; such manuals are available +`elsewhere <http://gcc.gnu.org/>`_. Obtaining ``arm-none-eabi-gcc`` ------------------------------- @@ -17,21 +16,24 @@ Obtaining ``arm-none-eabi-gcc`` Recent versions of ``arm-none-eabi-gcc`` and associated tools are included with the :ref:`Maple IDE <ide>`. -Users who wish to use ``arm-none-eabi-gcc`` in concert with a standard -Unix toolchain are referred to our :ref:`unix-toolchain` reference, -which describes how to set up such an environment. +Users who wish to use ``arm-none-eabi-gcc`` directly, along with a +standard Unix Make-based toolchain, should read the +:ref:`unix-toolchain`, which describes how to set up such an +environment. -LeafLabs mirrors some of the more recent versions of the compiler -under http://static.leaflabs.com/pub/codesourcery/\ , including -versions for OS X, win32, and 32-bit Linux. +LeafLabs maintains `mirrors +<http://static.leaflabs.com/pub/codesourcery/>`_ for some of the more +recent versions of the compiler, including versions for OS X, Win32, +and 32-bit Linux. Compiler Flags Used by libmaple ------------------------------- This section documents the flags passed to ``arm-none-eabi-gcc`` by -the :ref:`Maple IDE <ide>` and the default Makefile provided with -:ref:`libmaple <unix-toolchain>`. The information in this section is -subject to change without notice. +the :ref:`Maple IDE <ide>` and the default Makefile provided with the +:ref:`Unix toolchain <unix-toolchain>`. The information in this +section is subject to change between :ref:`libmaple <libmaple>` +releases. .. highlight:: sh @@ -40,7 +42,8 @@ The following flags are among those passed to the C compiler:: -Os -g -mcpu=cortex-m3 -mthumb -march=armv7-m -nostdlib -ffunction-sections -fdata-sections -Wl,--gc-sections -The following flags are among those passed to the C++ compiler:: +In addition to those flags just given for the C compiler, the +following flags are among those passed to the C++ compiler:: -fno-rtti -fno-exceptions -Wall @@ -52,6 +55,15 @@ The following flags are among those passed to the assembler:: .. _arm-gcc-avr-gcc: +Using the C Standard Library +---------------------------- + +By default (under both the :ref:`Maple IDE <ide>` and the :ref:`Unix +toolchain <unix-toolchain>`), ``arm-none-eabi-gcc`` is configured to +link against `newlib <http://sourceware.org/newlib/>`_, a C standard +library intended for use with embedded applications. You are free to +include of any of its headers. + Switching from AVR-GCC ---------------------- @@ -63,8 +75,11 @@ including Arduino) for use on the Maple. .. _arm-gcc-attribute-flash: - Replacing ``PROGMEM``: You can direct the linker script provided - with libmaple to store a variable in flash by using - ``__attribute__((section (".USER_FLASH")))``, like so:: + with libmaple to store a variable in Flash (instead of RAM) by using + the libmaple macro ``__FLASH__``, like so:: - uint32 arr[] __attribute__((section (".USER_FLASH"))) = {...}; + uint32 array[] __FLASH__ = {0, 1, 2}; + Be aware, however, that if you do that, you can only store values + which are compile-time constants, and that if you attempt to change + a variable stored in Flash, your program will crash. diff --git a/docs/source/bootloader.rst b/docs/source/bootloader.rst index 57833ed..ec4fe73 100644 --- a/docs/source/bootloader.rst +++ b/docs/source/bootloader.rst @@ -1,10 +1,9 @@ .. highlight:: sh -===================== - Maple Bootloader(s) -===================== +.. _bootloader: -.. TODO: add a section on flashing your own bootloader +Maple Bootloader(s) +=================== The firmware which allows the Maple to be reprogrammed via a USB connection. Every Maple board comes programmed with this by default, @@ -15,7 +14,7 @@ Flash memory and only runs when the chip is reset). git clone git://github.com/leaflabs/maple-bootloader.git -**Visit the github development project**: http://github.com/leaflabs/maple-bootloader +**Visit the github development project**: https://github.com/leaflabs/maple-bootloader .. contents:: Contents :local: @@ -36,7 +35,7 @@ likely only interesting to a few. If you just want to get the rough idea, skim this article. If you want to start hacking on the bootloader, get in touch with us to get even more info on how this all works. And finally, you can always `check out the code at github -<http://github.com/leaflabs/libmaple>`_! +<https://github.com/leaflabs/libmaple>`_! Arduino ------- @@ -66,7 +65,7 @@ have embedded USB support. Thus, Maple doesn’t need the extra FTDI chip. Firmware is uploaded via the standard DFU protocol (also used by iPhone and openMoko). Since DFU is a standard, there is no need for custom software running on the host to upload the firmware. Any DFU -compliant program will work. The maple ide is based around +compliant program will work. The Maple IDE is based around :command:`dfu-util`, openMoko’s DFU utility. Using DFU came at a cost, however. The USB port must additionally implement a separate serial port at the same time (we use the CDC ACM class for serial @@ -87,11 +86,11 @@ important what this means, except for two things. 1. Four drivers were necessary to make everything work. 2. IAD is not supported by OS X. -Mac, on the other hand, only supported Compound USB, a different trick -that is not supported by Windows. While a perpetual background +Mac OS X, on the other hand, only supported Compound USB, a different +trick that is not supported by Windows. While a perpetual background 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. +were going to have to write custom drivers across several platforms to +make everything work this way. .. _bootloader-rev3: @@ -103,22 +102,21 @@ Arduino. In Rev 3, the device resets into bootloader mode, which stays alive for a few moments to receive commands, and then jumps to user code. The bootloader is implemented as a DFU device -- just a DFU device, no serial port. This requires one driver for Windows -(:file:`drivers/mapleDrv/dfu` in the Windows IDE directory). As part -of the :ref:`libmaple <libmaple>` library, user code is automatically -supplied with serial support via some behind the scenes work that -happens automatically when you compile (``setupUSB()`` is appended to -``setup()``). This user mode code only implements a CDC ACM class USB -device, giving you functions like ``Usb.print()``. Separating these -two modes fixed the driver issue, required no complicated compound USB -device nonsense, and works well across platforms, requiring only two -drivers (serial and DFU) on Windows. +(:file:`drivers/mapleDrv/dfu` in the Windows IDE directory). + +As part of the :ref:`libmaple <libmaple>` library, user code is +automatically supplied with serial support via some behind the scenes +work (``setupUSB()`` is called from ``init()``). This user mode code +only implements a CDC ACM class USB device, giving you functions like +:ref:`SerialUSB.read() <lang-serialusb-read>`. Separating these two +modes fixed the driver issues and works well across platforms, +requiring only two drivers (serial and DFU) on Windows. However, it is no longer possible to upload code at will, since there -is no bootloader quietly listening in the background. Instead you have -to reset the board, then initiate a DFU transaction. This reset is -performed automatically by the IDE by sending a command over the USB -serial port. You can generate this reset on your own using a Python -script or some other scheme. All you need do is: +is no bootloader quietly listening in the background. Instead, you +must reset the board, then initiate a DFU transaction. The IDE +performs this reset automatically by performing a special sequence of +changes on the USB serial port: 1. Pulse DTR (high and then low, so that you've created a negative edge) @@ -128,15 +126,16 @@ script or some other scheme. All you need do is: negative edge, rather than just ensuring DTR is low. After the reset, the host OS takes a few moments (.5-2 seconds) to -re-enumerate the device as DFU. This delay is unpredictable, and its -the reason the bootloader on Maple Rev3 stays alive for so -long. Sometimes the bootloader was exiting before the OS had even -enumerated the device! Once in bootloader mode, however, -:command:`dfu-util` uploads your sketch into either flash or RAM (DFU -alternate setting 0 or 1, respectively) and resets the board again. -This time, however, no DFU transaction is initiated, and the -bootloader gives way to user code, closing down the DFU pipe and -bringing up the USB serial. +re-enumerate the device as DFU. This delay is unpredictable, and is +the reason the bootloader on Maple Rev 3/Rev 5 stays alive for so +long. (Sometimes, the bootloader was exiting before the OS had even +enumerated the device.) + +Once in bootloader mode, :command:`dfu-util` uploads your sketch into +either flash or RAM (DFU alternate setting 0 or 1, respectively) and +resets the board again. This time, however, no DFU transaction is +initiated, and the bootloader gives way to user code, closing down the +DFU pipe and bringing up the USB serial port. .. .. _bootloader-rev6: @@ -585,20 +584,25 @@ bringing up the USB serial. Flashing A Custom Bootloader ---------------------------- -The STM32 microprocessor on the Maple comes with a built-in hardware +.. warning:: This section is for users who want to put a fresh or + custom bootloader on their board. It's possible to make a mistake + in this process and e.g. render your Maple unable to communicate + with the IDE. Know what you're doing, and proceed with caution. + +The STM32 microprocessor on the Maple comes with a built-in serial bootloader that can be used to flash a new (software) bootloader onto -the chip. This section describes how to go about this, using a Maple -Rev 3 or higher (if you have a Maple Rev 1; you don't have a BUT -button, and won't be able to follow these directions. A workaround is -detailed in `this forum posting -<http://forums.leaflabs.com/topic.php?id=32#post-126>`_). - -.. warning:: This section is directed at users wishing to write a - custom bootloader for the Maple, or update their bootloader to a - more recent version. It's generally not necessary to do so, and it - is possible to make a mistake and e.g. render your Maple unable to - communicate with the IDE. Know what you're doing, and proceed with - caution. +the chip. While the Maple bootloader is just a program, the built-in +serial bootloader is part of the STM32 hardware, so it's always +available. + +This means that you can **always** follow these instructions to put a +new bootloader program on your board; it **doesn't matter** if there's +already a copy of the Maple bootloader on it or not. + +This section applies to Maple Rev 3 or higher. If you have a Maple +Rev 1; you don't have a BUT button, and won't be able to follow these +directions. A workaround is detailed in `this forum posting +<http://forums.leaflabs.com/topic.php?id=32#post-126>`_. .. highlight:: sh @@ -615,10 +619,20 @@ In order to follow these instructions, you will need: **Step 1: Obtain a bootloader binary**. The first thing you'll need to do is to compile your bootloader binary. Note that an ASCII -representation of the binary, such as the Intel .hex format, will not -suffice. For example, you can run (on a :ref:`suitably configured -system <unix-toolchain>`) the following to obtain a binary of the -bootloader currently used on the Maple:: +representation of the binary, such as the Intel .hex format, won't +work. + +.. FIXME [Mini, Native] links to precompiled bootloaders + +If you just want to flash the default Maple bootloader (the one that +was installed on your Maple when it arrived), we host a `pre-compiled +copy +<http://static.leaflabs.com/pub/leaflabs/maple-bootloader/maple_boot-rev3-9c5f8e.bin>`_, +which works on all Maple Revs. + +To obtain the latest development version, you can run (on a +:ref:`suitably configured system <unix-toolchain>`) the following to +obtain a binary of the bootloader currently used on the Maple:: $ git checkout git://github.com/leaflabs/maple-bootloader.git $ cd maple-bootloader @@ -632,15 +646,20 @@ could use another Maple, an Arduino, etc. -- anything that allows your computer to communicate with the Maple you want to reprogram over a serial interface. +.. FIXME [Maple-specific values] + If you do use an FTDI breakout board, first make sure your Maple is disconnected from an external power source, be it battery, USB, or barrel jack. Then, connect the FTDI board's TX pin to ``Serial1``\ 's RX pin (pin 8), FTDI RX to ``Serial1`` TX (pin 7), FTDI ground to Maple's GND, and its 3.3V pin to Maple's Vin (use the Maple's -silkscreen for help locating these pins). At this point, you're ready -to plug the FTDI board into your computer (via USB). +silkscreen for help locating these pins). + +More information on ``Serial1`` is available :ref:`here +<lang-serial>`. -The ``Serial1`` pins are documented :ref:`here <lang-serial>`. +At this point, you're ready to plug the FTDI board into your computer +(via USB). **Step 3: Put your Maple into serial bootloader mode**. Do this by pressing the RESET button, then *while RESET is held down*, pressing @@ -648,35 +667,50 @@ and holding the BUT button. Next, *making sure to keep BUT held down*, release the RESET button and wait for a few seconds before releasing BUT. -**Step 4: Obtain stm32loader.py**. The -script ``stm32loader.py`` is provided with libmaple. If you have set -up the :ref:`Unix toolchain <unix-toolchain>`, it is available in -libmaple/support/stm32loader.py. Otherwise, you can download it -directly from `github +**Step 4: Get stm32loader.py**. You can download it directly from +`libmaple's github page <https://github.com/leaflabs/libmaple/raw/master/support/stm32loader.py>`_ -(click the link, then save the file somewhere on your system). +(click the link, then save the file somewhere on your system). If you +have set up the :ref:`Unix toolchain <unix-toolchain>`, it's the file +libmaple/support/stm32loader.py. Flashing the new Bootloader ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -We'll use ``new-boot.bin``, ``ser-port``, and ``stm32loader.py`` to -respectively refer to the absolute paths to the bootloader binary -(from Step 1), the serial port device file or COMM port (from Steps 2 -and 3), and the stm32loader.py script. +We'll use ``maple_boot.bin`` as the path to the bootloader binary from +Step 1, and ``ser-port`` as the Maple's serial port device file or COM +port. + +* On **Linux**, ``ser-port`` will probably be something like + ``/dev/ttyUSB0``, although the exact number could be different (it + could be ``/dev/ttyUSB1``, ``/dev/ttyUSB2``, etc.). + +* On **OS X**, ``ser-port`` will probably look like + ``/dev/tty.usbserialXXXX``, where ``XXXX`` is some random string of + characters. + +* On **Windows**, ``ser-port`` will be something like ``COM1``, ``COM2``, etc. .. highlight:: sh -You can run :: +To upload a bootloader binary, run this command from the Unix shell:: + + python stm32loader.py -p ser-port -evw maple_boot.bin + +Or this command from the Windows command prompt:: + + python.exe stm32loader.py -p ser-port -evw maple_boot.bin - $ python stm32loader.py -h +You can also run the following to get usage information:: -to obtain usage information. The incantation for uploading a -bootloader binary ``new-bootloader.bin`` is :: + # Unix: + python stm32loader.py -h - $ python stm32loader.py -p ser-port -evw new-boot.bin + # Windows: + python.exe stm32loader.py -h If all goes well, you'll see a bunch of output, then "Verification OK". If something goes wrong, the `forum`_ is probably your best bet -for obtaining help, with IRC (irc.freenode.net, #leafblowers) being -another option. If all else fails, you can always `contact us -directly`_! +for obtaining help, with IRC (server irc.freenode.net, channel +#leafblowers) being another option. If all else fails, you can always +`contact us directly`_! diff --git a/docs/source/conf.py b/docs/source/conf.py index 1ad4e57..baadccb 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -59,7 +59,7 @@ copyright = u'2010, LeafLabs, LLC' # The short X.Y version. version = '0.0' # The full version, including alpha/beta/rc tags. -release = '0.0.9' +release = '0.0.10' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. @@ -117,25 +117,25 @@ html_theme = 'default' # further. For a list of options available for each theme, see the # documentation. html_theme_options = { - ## Sidebar placement options - #'stickysidebar' : 'true', - 'rightsidebar' : 'true', - #'collapsiblesidebar' : 'true', - - ## Color - 'sidebarbgcolor' : '#C8C8C8', - 'sidebarlinkcolor' : 'green', - 'sidebartextcolor' : 'black', + ## Sidebar placement options + #'stickysidebar' : 'true', + 'rightsidebar' : 'true', + #'collapsiblesidebar' : 'true', + + ## Color + 'sidebarbgcolor' : '#C8C8C8', + 'sidebarlinkcolor' : 'green', + 'sidebartextcolor' : 'black', #'sidebarbtncolor' : 'black', - 'footerbgcolor' : 'green', - 'relbarbgcolor' : 'green', - 'headlinkcolor' : '#000000', - 'linkcolor' : 'green', - 'visitedlinkcolor' : 'green', - - ## Font - 'headfont' : 'Georgia', - 'bodyfont' : 'Lucidia' + 'footerbgcolor' : 'green', + 'relbarbgcolor' : 'green', + 'headlinkcolor' : '#000000', + 'linkcolor' : 'green', + 'visitedlinkcolor' : 'green', + + ## Font + 'headfont' : 'Georgia', + 'bodyfont' : 'Lucidia' } # Add any paths that contain custom themes here, relative to this directory. @@ -174,7 +174,7 @@ html_last_updated_fmt = '%b %d, %Y' # re-add commented line when custom template for api finished html_sidebars = { '**': ['globaltoc.html', 'searchbox.html'], - #'lang/api**':['searchbox.html', 'apilist.html'], + #'lang/api**':['searchbox.html', 'apilist.html'], } diff --git a/docs/source/errata.rst b/docs/source/errata.rst deleted file mode 100644 index 657abd9..0000000 --- a/docs/source/errata.rst +++ /dev/null @@ -1,140 +0,0 @@ -.. _errata: - -======== - Errata -======== - -This page is a collection of known issues and warnings for each -revision of the Maple board. The failure modes aren't design errors, -but are easy ways to break or damage your board permanently. For a -list of differences between the Maple and Arduinos, see the -:ref:`compatibility reference <compatibility>`. - -The errata are grouped by Maple version ("Rev"). Rev 5 is currently -on sale. If you are unsure which version you own, the :ref:`Maple -hardware page <maple-hardware-identify-rev>` has pictures and -descriptions of each version. - -.. contents:: Contents - :local: - -Maple Rev 5 ------------ - -Known issues: - -* **Pin 3 AIN missing**: Pin 3 is capable of analog input, but the - corresponding "AIN" is missing from its silkscreen. - -* **GPIO 39-43 not configured**: this is really more of a software - "TODO" item. Some of the JTAG header pins are numbered 39-43. These - STM32 pins are indeed fully functional :ref:`GPIO <gpio>` when a - :ref:`JTAG <jtag>` device is not connected, but we have not enabled - them in software and thus they can not be accessed with the regular - :ref:`lang-pinmode` or :ref:`lang-digitalwrite` functions. - -Potential failure modes: - -* **High voltage on non-tolerant pins**: not all header pins are 5V - compatible; so e.g. connecting certain serial devices in the wrong - way could over-voltage the pins. The :ref:`Pin-Mapping Mega Table - <pin-mapping-mega-table>` details which pins are 5V-tolerant. - -Maple Rev 3 ------------ - -Known issues: - -* **Bad/Sticky Buttons**: a number of Rev 3 boards sold in May-June 2010 - have questionable RESET and BUT buttons. - - What seems to have happened is that the flux remover we used to - clean the boards before shipping eroded the plastic internals, which - resulted in intermittent functionality. All buttons on all shipped - boards did function in testing, but some may have been unreliable in - regular use. - - If you have this problem, we will be happy to ship you new buttons - if you think you can re-solder them yourself, or you can ship us - your board and we will swap out that part. - - For reference, the button part number is KMR211GLFS and the flux - remover we used is "Precision Electronics Cleaner" from RadioShack, - which is "Safe on most plastics" and contains Dipropylene glycol - monomethyl ether, hydrotreated heavy naphtha, dipropylene glycol - methyl ether acetate (really?), and carbon dioxide. - -* **Resistors on pins 0 and 1**: these header pins, which are RX/TX on - USART2 (:ref:`Serial2 <lang-serial>`), have resistors in-line - between the STM32 and the headers. These resistors increase the - impedance of the lines for ADC reads and affect the open drain GPIO - functionality of the pins. - - These resistors were accidentally copied over from older Arduino USB - designs, where they appear to protect the USB-Serial converter from - TTL voltage on the headers. - -* **GPIO 39-43 not configured**: this is really more of a software - "TODO" item. Some of the JTAG header pins are numbered 39-43. These - STM32 pins are indeed fully functional :ref:`GPIO <gpio>` when the a - :ref:`JTAG <jtag>` device is not connected, but we have not enabled - them in software and thus they can not be accessed with the regular - :ref:`lang-pinmode` or :ref:`lang-digitalwrite` functions. - -* **Silkscreen Errors**: the silkscreen on the bottom indicated PWM - functionality on pin 25 and listen the external header GND pin as - number 38 (actually 38 is connected to the BUT button). We manually - sharpied over both of these mistakes. - -* **PWM Marketing Mistake**: We originally sold the Maple advertising - 22 channels of 16-bit hardware PWM; actually the Maple only has 15. - -Potential failure modes: - -* **TTL voltage on non-tolerant pins**: not all header pins are 5V - compatible; connecting certain serial devices in the wrong way could - over voltage the pins. The :ref:`Pin-Mapping Mega Table - <pin-mapping-mega-table>` details which pins are 5V-tolerant. - -Maple Rev 1 ------------ - -Known issues: - -* **ADC noise**: generally very high, in particular when the USB port - is being used for communications (including keep-alive pings when - connected to a computer). - - This issue was resolved in Rev 3 with a 4-layer design and a - geometrically isolated ADC V\ :sub:`ref` plane. - -* **Resistors on pins 0 and 1**: these header pins, which are RX/TX on - USART2 (:ref:`Serial2 <lang-serial>`), have resistors in-line - between the STM32 and the headers. These resistors increase the - impedance of the lines for ADC reads and affect the open drain GPIO - functionality of the pins. - - These resistors were accidentally copied over from older Arduino USB - designs, where they appear to protect the USB-Serial converter from - TTL voltage on the headers. - -* **Silkscreen Differences**: the pin numbering scheme on Rev 1 is - different from Rev 3, and thus Rev 3 software is difficult to use - with Rev 1 boards. Notably, the analog input bank is labeled A0-A4 - on Rev 1 but 15-20 on Rev 3, and the extra header bank does not have - a pinout table on the bottom. - -* **No BUT Button**: the BUT button, useful for serial bootloading, - was only added in Rev 3. As a workaround, you can directly short the - appropriate MCU pin to Vcc; see `this forum posting - <http://forums.leaflabs.com/topic.php?id=32#post-126>`_. - -* **PWM Marketing Mistake**: We originally sold the Maple advertising - 22 channels of 16-bit hardware PWM; actually the Maple only has 15. - -Potential failure modes: - -* **TTL voltage on non-tolerant pins**: not all header pins are 5v - compatible; connecting certain serial devices in the wrong way could - over voltage the pins. The :ref:`Pin-Mapping Mega Table - <pin-mapping-mega-table>` details which pins are 5V-tolerant. diff --git a/docs/source/external-interrupts.rst b/docs/source/external-interrupts.rst index b2cbbb1..ac065a4 100644 --- a/docs/source/external-interrupts.rst +++ b/docs/source/external-interrupts.rst @@ -6,73 +6,42 @@ External Interrupts =================== External interrupts can be used to trigger routines to run in response -to changes in voltage on a pin. Each GPIO pin on the Maple can be used -to detect transitions such as when the voltage goes from low to high, -or from high to low. This technique can be used to avoid unnecessary -polling of the state of a pin. +to changes in voltage on a pin. Each :ref:`GPIO pin <gpio>` can be +used to detect transitions, such as when the voltage goes from +:ref:`LOW <lang-constants-low>` to :ref:`HIGH <lang-constants-high>`, +or from ``HIGH`` to ``LOW``. This can be used to avoid checking for +changes on a pin "manually" by waiting in a loop until the pin +changes. .. _contents: Contents :local: - Overview -------- External interrupts are often used to detect when events happen -outside of the microcontroller. These can be used to tell the Maple -when events happen, such as when a sensor has data ready to be read, -or when a button has been pushed. When such an event happens, an -interrupt is raised and the Maple can react to it with a preset -*interrupt handler*. - -Every GPIO pin on the Maple can be used as an external interrupt, -subject to certain constraints; there can be a maximum of 16 different -external interrupts set up at a time on the Maple. This is because the -external interrupt lines on the STM32 are multiplexed between GPIO -ports. In effect, this means that every pin on the Maple maps to a -certain EXTI line, and within that EXTI line, only one of the pins -that maps to it can be used as an external interrupt at a time. - -The following table shows which pins can be used on which lines. - -.. list-table:: - :widths: 1 1 - :header-rows: 1 - - * - EXTI Line - - Maple pins - * - EXTI0 - - 2, 15, 27 - * - EXTI1 - - 3, 16, 28 - * - EXTI2 - - 1, 17, 25 - * - EXTI3 - - 0, 18 - * - EXTI4 - - 10, 19 - * - EXTI5 - - 4, 13, 20 - * - EXTI6 - - 5, 12, 35 - * - EXTI7 - - 9, 11, 36 - * - EXTI8 - - 6, 14, 37 - * - EXTI9 - - 7, 25, 28 - * - EXTI10 - - 8, 26, 29 - * - EXTI11 - - 30 - * - EXTI12 - - 31 - * - EXTI13 - - 21, 32 - * - EXTI14 - - 22, 33 - * - EXTI15 - - 23, 34 +outside of the microcontroller. These can be used to tell Maple when +events happen, such as when a sensor has data ready to be read, or +when a button has been pushed. When such an event happens, an +interrupt is raised, and the Maple can react to it with a preset +*interrupt handler*, which is a function that gets called whenever the +event occurs. + +.. _external-interrupts-exti-line: + +Every GPIO pin can generate an external interrupt, subject to certain +constraints. There can be a maximum of 16 different external +interrupts set up at a time. This is because the external interrupt +lines on the STM32 are shared between GPIO ports. In effect, this +means that every pin on the Maple connects to what is called an *EXTI +line*, and within an EXTI line, only one of the pins that connects to +it can be used to detect external interrupts at a time. + +The EXTI Line Pin Map for your board lists which pins connect to which +EXTI lines: + +* :ref:`Maple <maple-exti-map>` +* :ref:`Maple RET6 Edition <maple-ret6-exti-map>` .. note:: @@ -80,40 +49,16 @@ The following table shows which pins can be used on which lines. desired pin to an input mode (e.g ``INPUT`` or ``INPUT_FLOATING``, ``INPUT_PULLUP``, ``INPUT_PULLDOWN``). - Function Reference ------------------ - :ref:`attachInterrupt() <lang-attachinterrupt>` - :ref:`detachInterrupt() <lang-detachinterrupt>` -Code example ------------- - -Blink the LED on every transition:: - - int pin = 13; - volatile int state = LOW; - - void setup() { - pinMode(pin, OUTPUT); - pinMode(0, INPUT_FLOATING); - attachInterrupt(0, blink, CHANGE); - } - - void loop() { - digitalWrite(pin, state); - } - - void blink() { - state = !state; - } - - Recommended Reading ------------------- -* STMicro documentation for STM32F103RB microcontroller: - - * `Datasheet <http://www.st.com/stonline/products/literature/ds/13587.pdf>`_ (pdf) - * `Reference Manual <http://www.st.com/stonline/products/literature/rm/13902.pdf>`_ (pdf) +* ST manual `RM0008 + <http://www.st.com/stonline/products/literature/rm/13902.pdf>`_ + (PDF), Chapter 9, "General-purpose and alternate-function I/Os", and + Chapter 10, "Interrupts and Events". diff --git a/docs/source/gpio.rst b/docs/source/gpio.rst index cebb402..74be3d4 100644 --- a/docs/source/gpio.rst +++ b/docs/source/gpio.rst @@ -3,84 +3,54 @@ GPIO ==== -The Maple features 38 ready-to-use general purpose input/output (GPIO) -pins for digital input/output, numbered D0 through D37. These numbers -correspond to the numeric values next to each header on the Maple -silkscreen. - -Many of these pins may additionally be used for special features or -peripheral functions. This page documents those capabilities, by pin. - -The current and voltage limitations have been copied over from the -STM32 datasheet (see the :ref:`Recommended Reading -<gpio-recommended-reading>` for a link). In particular, a number of -GPIO pins are 5V tolerant (which means that applying 5 volts to a pin -and reading it as input or allowing it to drain to ground will not -damage that pin), while some are not. +Each LeafLabs board comes with ready-to-use General Purpose +Input/Output (GPIO) pins, which are numbered starting from zero. +These numbers are listed on your board's silkscreen, next to where the +pin is broken out to a header. Many pins may additionally be used for +special features or peripheral functions. .. contents:: Contents :local: -.. _pin-mapping-mega-table: - -Pin Mapping Mega Table ----------------------- - -This table shows the available functionality on every GPIO pin, by -peripheral type. The "STM32" column refers to the port and number that -the header is connected to on the microcontroller. The "5V?" column -documents whether or not the pin is 5 volt tolerant (see above). - -.. TODO silkscreen pictures which let you know what each abbreviation -.. means, with links to the relevant documentation. - -.. csv-table:: - :header: "Pin", "STM32", ":ref:`ADC <adc>`", ":ref:`Timer <timers>`", ":ref:`I2C <i2c>`", ":ref:`UART <usart>`", ":ref:`SPI <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" - "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-pin-maps: + +Pin Maps +-------- + +The hardware documentation for your board lists each pin's +capabilities, by pin number: + +.. TODO [0.1.0] Uncomment Mini and Native GPIO links + +* :ref:`Maple <maple-gpios>` +* :ref:`Maple RET6 Edition <maple-ret6-gpios>` + +.. * :ref:`Maple Mini <maple-mini-gpios>` +.. * :ref:`Maple Native <maple-native-gpios>` + +The current and voltage limitations were determined using the STM32 +datasheets. In particular, only some GPIO pins are **5V tolerant**, +which means that applying 5 volts to a pin and reading it as input or +allowing it to drain to ground will not damage that pin. Connecting a +voltage higher than 3.3V to a non-5V tolerant pin may damage your +board. .. _gpio-modes: GPIO Modes ---------- +Each of the GPIO pins on a Maple board may be configured using +pinMode() to behave in a number of ways: as a digital output pin, +or as an analog input pin, etc., depending on the particular pin. + +A ``WiringPinMode`` value specifies the complete set of possible +configurations; not every pin can have all of these modes. For +example, on the Maple, pin 15 may have mode ``INPUT_ANALOG``, but not +``PWM``. See your :ref:`board's pin maps <gpio-pin-maps>` and its +silkscreen for more information on what functionality is available on +each pin. + .. doxygenenum:: WiringPinMode Function Reference @@ -103,7 +73,11 @@ Function Reference Recommended Reading ------------------- -STMicro documentation for STM32F103RB microcontroller: +* ST Documentation for the STM32F103 series of microcontrollers: - * `Datasheet <http://www.st.com/stonline/products/literature/ds/13587.pdf>`_ (pdf) * `Reference Manual <http://www.st.com/stonline/products/literature/rm/13902.pdf>`_ (pdf) + + * `Programming Manual + <http://www.st.com/stonline/products/literature/pm/15491.pdf>`_ + (PDF; assembly language and register reference) + diff --git a/docs/source/hardware/maple-ret6.rst b/docs/source/hardware/maple-ret6.rst new file mode 100644 index 0000000..06dcaff --- /dev/null +++ b/docs/source/hardware/maple-ret6.rst @@ -0,0 +1,276 @@ +.. highlight:: sh + +.. _maple-ret6: + +Maple RET6 Edition +================== + +.. contents:: Contents + :local: + +Technical Specifications +------------------------ + + * MCU: **STM32F103RET6**, a 32-bit ARM Cortex M3 microprocessor + * Clock Speed: **72 MHz** + * Operating Voltage: 3.3V + * Input Voltage (recommended): 3V-12V + * 39 Digital I/O Pins (:ref:`GPIO <gpio>`) + * 16 Analog Input pins, 12 bit **ADC** resolution (:ref:`ADC <adc>`) + * 15 **PWM** pins at 16-bit resolution (:ref:`PWM <pwm>`) + * Dedicated **USB** port for programming and communications (:ref:`USB<usb>`) + * External **JTAG** interface (:ref:`JTAG <jtag>`) + * **512KB Flash** and **64KB SRAM** + * 64 Channel nested vector interrupt handler (including external interrupt on GPIOs) + * Integrated **SPI** (:ref:`SPI <spi>`) + * Integrated **I2C** (:ref:`I2C <i2c>`) + * 12 Channels of Direct Memory Access (**DMA**) + * 3 **USART** and 2 **UART** devices (:ref:`USART <usart>`) + * Six 4-channel and two basic **timers** (:ref:`Timers <timers>`) + * Supplies up to 800mA @ 3.3v + * Support for low power and sleep modes (<500uA) + * Dimensions are 2.05″x2.1″ + +.. _maple-ret6-powering: + +Powering the Maple RET6 Edition +------------------------------- + +The Maple RET6 Edition's power source is determined by the header to +the left of the "LeafLabs" label on the silkscreen. The RET6 Edition +can be powered from the barrel jack connector, USB, or a LiPo battery. +We ship the RET6 Edition with a jumper on the USB selector. In order +to power it off of an alternative source, unplug the board, then move +the jumper to the desired selector before reconnecting power. + +You can also power the Maple via the pin labeled "Vin" on the lower +header. However, don't do this while simultaneously powering the +board from another source, or you could damage the it. + +Using the Built-in Battery Charger +---------------------------------- + +The RET6 Edition has a built-in LiPo battery charger. In order to use +it, put a jumper across the CHRG header on the power selection header +and across the USB, or EXT selectors, depending on whether you're +charging the battery via USB cable or barrel jack connector. The LED +labeled CHRG will light up while the battery is being charged. When +the battery is finished charging, the LED labeled DONE will also light +up. + +.. _maple-ret6-gpios: + +GPIO Information +---------------- + +The RET6 Edition features 38 ready-to-use general purpose input/output +(GPIO) pins for digital input/output, numbered ``D0`` through ``D37``. +These numbers correspond to the numeric values next to each header on +the Maple silkscreen. More GPIOs (numbered ``D39``\ --``43``) are +available through use in combination with the +:ref:`lang-disabledebugports` function; see the :ref:`board-specific +debug pin constants <lang-board-values-debug>` for more information. + +.. TODO [0.1.0] silkscreen pictures which expand abbreviations + +.. _maple-ret6-pin-map-master: + +Master Pin Map +^^^^^^^^^^^^^^ + +.. TODO [0.0.10] Update from base Maple information + +This table shows the available functionality on every GPIO pin, by +peripheral type. The "STM32" column refers to the port and number that +the header is connected to on the microcontroller. The "5V?" column +documents whether or not the pin is 5 volt tolerant. + +.. csv-table:: + :header: "Pin", "STM32", ":ref:`ADC <adc>`", ":ref:`Timer <timers>`", ":ref:`I2C <i2c>`", ":ref:`UART <usart>`", ":ref:`SPI <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" + "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_SMBA", "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" + +.. TODO [0.0.10] Another table for the JTAG pins + +Timer Pin Map +^^^^^^^^^^^^^ + +.. TODO [0.0.10] Add Timer 5,6,7,8 information + +The following table shows what pins are associated with a particular +timer's capture/compare channels. + +.. csv-table:: + :header: Timer, Ch. 1, Ch. 2, Ch. 3, Ch. 4 + :delim: | + + 1 | D6 | D7 | D8 | - + 2 | D2 | D3 | D1 | D0 + 3 | D12 | D11 | D27 | D28 + 4 | D5 | D9 | D14 | D24 + +.. _maple-ret6-exti-map: + +EXTI Line Pin Map +^^^^^^^^^^^^^^^^^ + +The following table shows which pins connect to which :ref:`EXTI lines +<external-interrupts-exti-line>` on the Maple RET6 Edition. + +.. list-table:: + :widths: 1 1 + :header-rows: 1 + + * - EXTI Line + - Pins + * - EXTI0 + - 2, 15, 27 + * - EXTI1 + - 3, 16, 28 + * - EXTI2 + - 1, 17, 25 + * - EXTI3 + - 0, 18 + * - EXTI4 + - 10, 19 + * - EXTI5 + - 4, 13, 20 + * - EXTI6 + - 5, 12, 35 + * - EXTI7 + - 9, 11, 36 + * - EXTI8 + - 6, 14, 37 + * - EXTI9 + - 7, 25, 28 + * - EXTI10 + - 8, 26, 29 + * - EXTI11 + - 30 + * - EXTI12 + - 31 + * - EXTI13 + - 21, 32 + * - EXTI14 + - 22, 33 + * - EXTI15 + - 23, 34 + +.. _maple-ret6-usart-map: + +USART Pin Map +^^^^^^^^^^^^^ + +.. FIXME [0.0.10] UART4 and UART5 information + +The Maple RET6 Edition has three serial ports (also known as a UARTs +or USARTs): ``Serial1``, ``Serial2``, and ``Serial3``. They +communicate using the pins summarized in the following table: + +.. csv-table:: + :header: Serial Port, TX, RX, CK, CTS, RTS + :delim: | + + ``Serial1`` | 7 | 8 | 6 | - | - + ``Serial2`` | 1 | 0 | 10 | 2 | 3 + ``Serial3`` | 29 | 30 | 31 | 32 | 33 + +Board-Specific Values +--------------------- + +.. TODO [0.0.10] + +Stub. + +Hardware Design Files +--------------------- + +The hardware schematics and board layout files are available in the +`Maple Github repository <https://github.com/leaflabs/maple>`_. Other +than the processor used, the design files for the Maple RET6 edition +are identical to the Maple Rev 5, which are in the ``maple-r5`` +subdirectory of the Maple repository. A schematic for a JTAG adapter +suitable for use with Maple is available in the ``jtagadapter`` +directory. + +From the Github repository main page, you can download the entire +repository by clicking the "Download" button. If you are familiar +with `git <http://git-scm.com/>`_, you can also clone the repository +at the command line with :: + + $ git clone git://github.com/leaflabs/maple.git + +.. _maple-ret6-failure-modes: + +Failure Modes +------------- + +The following known failure modes apply to all Maple boards. The +failure modes aren't design errors, but are easy ways to break or +damage your board permanently. + +* **High voltage on non-tolerant pins**: not all header pins are 5V + compatible; so e.g. connecting certain serial devices in the wrong + way could over-voltage the pins. The :ref:`pin-mapping master table + <maple-ret6-pin-map-master>` details which pins are 5V-tolerant. + +Errata +------ + +This section lists known issues and warnings for the Maple RET6 Edition. + +* **DAC, UART4, UART5 GPIOs unavailable**: Pins related to the digital + to analog converter (DAC) and UARTs 4 and 5 are not broken out to + headers. The RET6 Edition's hardware layout is identical to that of + the Maple Rev 5, which wasn't designed for use with these + STM32F103RET6-only peripherals. + +Recommended Reading +------------------- + +* STMicro documentation for STM32F103RE microcontroller: + + * `Datasheet + <http://www.st.com/internet/com/TECHNICAL_RESOURCES/TECHNICAL_LITERATURE/DATASHEET/CD00191185.pdf>`_ (PDF) + * `Reference Manual + <http://www.st.com/stonline/products/literature/rm/13902.pdf>`_ (PDF) + * `Programming Manual + <http://www.st.com/stonline/products/literature/pm/15491.pdf>`_ + (PDF; assembly language and register reference) diff --git a/docs/source/hardware/maple.rst b/docs/source/hardware/maple.rst index 874ee82..f4fcaf4 100644 --- a/docs/source/hardware/maple.rst +++ b/docs/source/hardware/maple.rst @@ -1,6 +1,6 @@ .. highlight:: sh -.. _hardware-maple: +.. _maple: Maple ===== @@ -8,7 +8,30 @@ Maple .. contents:: Contents :local: -.. _maple-hardware-identify-rev: +Technical Specifications +------------------------ + + * MCU: **STM32F103RBT6**, a 32-bit ARM Cortex M3 microprocessor + * Clock Speed: **72 MHz** + * Operating Voltage: 3.3V + * Input Voltage (recommended): 3V-12V + * 39 Digital I/O Pins (:ref:`GPIO <gpio>`) + * 16 Analog Input Pins, 12-bit **ADC** resolution (:ref:`ADC <adc>`) + * 15 **PWM** pins at 16-bit resolution (:ref:`PWM <pwm>`) + * Dedicated **USB** port for programming and communications (:ref:`USB <usb>`) + * External **JTAG** interface (:ref:`JTAG <jtag>`) + * **128KB Flash** and **20KB SRAM** + * 64 Channel nested vector interrupt handler (including external interrupt on GPIOs) + * Integrated **SPI** (:ref:`SPI <spi>`) + * Integrated **I2C** (:ref:`I2C <i2c>`) + * 7 Channels of Direct Memory Access (**DMA**) + * 3 **USART** divices (:ref:`USART <usart>`) + * Four 4-channel **timers** (:ref:`Timers <timers>`) + * Supplies up to 800mA @ 3.3v + * Support for low power and sleep modes (<500uA) + * Dimensions are 2.05″x2.1″ + +.. _maple-identify-rev: Identifying your Rev -------------------- @@ -16,17 +39,22 @@ Identifying your Rev We went through three versions ("Revs") of the Maple hardware: Rev 1, Rev 3, and Rev 5 [#frev2_4]_; Rev 5, the final design, is currently on sale. The following sections will help you to help you identify your -Rev. Known issues are listed in the :ref:`errata <errata>`. +Rev. -Rev 1 +Rev 5 ^^^^^ -A small number of Maple Rev 1 boards went on sale in late 2009. They -have a light red silkscreen and a single pixelated leaf as a logo. +These boards went on sale in November 2010. They have white buttons, +and "r5" in small print near the "LeafLabs Maple" text next to the +"infinity leaf" logo. The Maple Rev 5 repositioned the double header +on the right hand side to better fit 0.1 inch pitch breadboard. This +necessitated the removal of pins 21 and 22 from the double header; +they are still available, but don't have any headers installed on +them. -.. figure:: /_static/img/maple_rev1.png +.. figure:: /_static/img/maple_rev5.png :align: center - :alt: Maple Rev 1 + :alt: Maple Rev 5 Rev 3 ^^^^^ @@ -40,22 +68,17 @@ It also includes a built-in LiPo battery charger. :align: center :alt: Maple Rev 3 -Rev 5 +Rev 1 ^^^^^ -These boards went on sale in November 2010. They have white buttons, -and "r5" in small print near the "LeafLabs Maple" text next to the -"infinity leaf" logo. The Maple Rev 5 repositioned the double header -on the right hand side to better fit 0.1 inch pitch breadboard. This -necessitated the removal of pins 21 and 22 from the double header; -they are still available, but don't have any headers installed on -them. +A small number of Maple Rev 1 boards went on sale in late 2009. They +have a light red silkscreen and a single pixelated leaf as a logo. -.. figure:: /_static/img/maple_rev5.png +.. figure:: /_static/img/maple_rev1.png :align: center - :alt: Maple Rev 5 + :alt: Maple Rev 1 -.. _hardware-maple-powering: +.. _maple-powering: Powering the Maple ------------------ @@ -82,25 +105,303 @@ connector. The LED labeled CHRG will light up while the battery is being charged. When the battery is finished charging, the LED labeled DONE will also light up. +.. _maple-gpios: + +GPIO Information +---------------- + +The Maple features 38 ready-to-use general purpose input/output (GPIO) +pins for digital input/output, numbered ``D0`` through ``D37``. These +numbers correspond to the numeric values next to each header on the +Maple silkscreen. More GPIOs (numbered ``D39``\ --``43``) are +available through use in combination with the +:ref:`lang-disabledebugports` function; see the :ref:`board-specific +debug pin constants <lang-board-values-debug>` for more information. + +.. TODO [0.1.0] silkscreen pictures which expand abbreviations + +.. _maple-pin-map-master: + +Master Pin Map +^^^^^^^^^^^^^^ + +This table shows the available functionality on every GPIO pin, by +peripheral type. The "STM32" column refers to the port and number that +the header is connected to on the microcontroller. The "5V?" column +documents whether or not the pin is 5 volt tolerant. + +.. csv-table:: + :header: "Pin", "STM32", ":ref:`ADC <adc>`", ":ref:`Timer <timers>`", ":ref:`I2C <i2c>`", ":ref:`UART <usart>`", ":ref:`SPI <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" + "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_SMBA", "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" + +.. TODO [0.0.10] JTAG pins + +Timer Pin Map +^^^^^^^^^^^^^ + +The following table shows what pins are associated with a particular +timer's capture/compare channels. + +.. csv-table:: + :header: Timer, Ch. 1, Ch. 2, Ch. 3, Ch. 4 + :delim: | + + 1 | D6 | D7 | D8 | - + 2 | D2 | D3 | D1 | D0 + 3 | D12 | D11 | D27 | D28 + 4 | D5 | D9 | D14 | D24 + +.. _maple-exti-map: + +EXTI Line Pin Map +^^^^^^^^^^^^^^^^^ + +The following table shows which pins connect to which :ref:`EXTI lines +<external-interrupts-exti-line>` on the Maple. + +.. list-table:: + :widths: 1 1 + :header-rows: 1 + + * - EXTI Line + - Pins + * - EXTI0 + - 2, 15, 27 + * - EXTI1 + - 3, 16, 28 + * - EXTI2 + - 1, 17, 25 + * - EXTI3 + - 0, 18 + * - EXTI4 + - 10, 19 + * - EXTI5 + - 4, 13, 20 + * - EXTI6 + - 5, 12, 35 + * - EXTI7 + - 9, 11, 36 + * - EXTI8 + - 6, 14, 37 + * - EXTI9 + - 7, 25, 28 + * - EXTI10 + - 8, 26, 29 + * - EXTI11 + - 30 + * - EXTI12 + - 31 + * - EXTI13 + - 21, 32 + * - EXTI14 + - 22, 33 + * - EXTI15 + - 23, 34 + +.. _maple-usart-map: + +USART Pin Map +^^^^^^^^^^^^^ + +.. FIXME [0.0.10] UART4, UART5 + +The Maple has three serial ports (also known as a UARTs or USARTs): +``Serial1``, ``Serial2``, and ``Serial3``. They communicate using the +pins summarized in the following table: + +.. csv-table:: + :header: Serial Port, TX, RX, CK, CTS, RTS + :delim: | + + ``Serial1`` | 7 | 8 | 6 | - | - + ``Serial2`` | 1 | 0 | 10 | 2 | 3 + ``Serial3`` | 29 | 30 | 31 | 32 | 33 + +Board-Specific Values +--------------------- + +.. TODO [0.0.10] + +Stub. + Hardware Design Files --------------------- The hardware schematics and board layout files are available in the -`Maple github repository <https://github.com/leaflabs/maple>`_. The +`Maple Github repository <https://github.com/leaflabs/maple>`_. The design files for Rev 1, Rev 3, and Rev 5 are respectively in the ``maple-r1``, ``maple-r3``, and ``maple-r5`` subdirectories. A schematic for a JTAG adapter suitable for use with Maple is available in the ``jtagadapter`` directory. -From the github repository main page, you can download the entire +From the Github repository main page, you can download the entire repository by clicking the "Download" button. If you are familiar with `git <http://git-scm.com/>`_, you can also clone the repository at the command line with :: $ git clone git://github.com/leaflabs/maple.git +.. _maple-failure-modes: + +Failure Modes +------------- + +The following are known failure modes. The failure modes aren't +design errors, but are easy ways to break or damage your board +permanently. + +* **High voltage on non-tolerant pins**: not all header pins are 5V + compatible; so e.g. connecting certain serial devices in the wrong + way could over-voltage the pins. The :ref:`pin-mapping master table + <maple-pin-map-master>` details which pins are 5V-tolerant. + +Errata +------ + +This section lists known issues and warnings for each revision of the +Maple board. + +Rev 5 +^^^^^ + +* **Pin 3 AIN missing**: Pin 3 is capable of analog input, but on Rev + 5s manufactured during Fall 2010, the corresponding "AIN" is missing + from its silkscreen. This mistake was fixed in later manufacturing + runs. + +Rev 3 +^^^^^ + +* **Pin 3 AIN missing**: Pin 3 is capable of analog input, but the + corresponding "AIN" is missing from the Rev 3 silkscreen. + +.. _maple-rev3-bad-buttons: + +* **Bad/Sticky Buttons**: a number of Rev 3 boards sold in May-June 2010 + have questionable RESET and BUT buttons. + + What seems to have happened is that the flux remover we used to + clean the boards before shipping eroded the plastic internals, which + resulted in intermittent functionality. All buttons on all shipped + boards did function in testing, but some may have been unreliable in + regular use. + + If you have this problem, we will be happy to ship you new buttons + if you think you can re-solder them yourself, or you can ship us + your board and we will swap out that part. + + For reference, the button part number is KMR211GLFS and the flux + remover we used is "Precision Electronics Cleaner" from RadioShack, + which is "Safe on most plastics" and contains Dipropylene glycol + monomethyl ether, hydrotreated heavy naphtha, dipropylene glycol + methyl ether acetate, and carbon dioxide. + +* **Resistors on pins 0 and 1**: these header pins, which are RX/TX on + USART2 (:ref:`Serial2 <lang-serial>`), have resistors in-line + between the STM32 and the headers. These resistors increase the + impedance of the lines for ADC reads and affect the open drain GPIO + functionality of the pins. + + These resistors were accidentally copied over from older Arduino USB + designs, where they appear to protect the USB-Serial converter from + TTL voltage on the headers. + +* **Silkscreen Errors**: the silkscreen on the bottom indicated PWM + functionality on pin 25 and listen the external header GND pin as + number 38 (actually 38 is connected to the BUT button). We manually + sharpied over both of these mistakes. + +* **PWM Marketing Mistake**: We originally sold the Maple advertising + 22 channels of 16-bit hardware PWM; actually the Maple only has 15. + +Rev 1 +^^^^^ + +* **ADC noise**: generally very high, in particular when the USB port + is being used for communications (including keep-alive pings when + connected to a computer). + + This issue was resolved in Rev 3 with a 4-layer design and a + geometrically isolated ADC V\ :sub:`ref` plane. + +* **Resistors on pins 0 and 1**: these header pins, which are RX/TX on + USART2 (:ref:`Serial2 <lang-serial>`), have resistors in-line + between the STM32 and the headers. These resistors increase the + impedance of the lines for ADC reads and affect the open drain GPIO + functionality of the pins. + + These resistors were accidentally copied over from older Arduino USB + designs, where they appear to protect the USB-Serial converter from + TTL voltage on the headers. + +* **Silkscreen Differences**: the pin numbering scheme on Rev 1 is + different from Rev 3, and thus Rev 3 software is difficult to use + with Rev 1 boards. Notably, the analog input bank is labeled A0-A4 + on Rev 1 but 15-20 on Rev 3, and the extra header bank does not have + a pinout table on the bottom. + +* **No BUT Button**: the BUT button, useful for serial bootloading, + was only added in Rev 3. As a workaround, you can directly short the + appropriate MCU pin to Vcc; see `this forum posting + <http://forums.leaflabs.com/topic.php?id=32#post-126>`_. + +* **PWM Marketing Mistake**: We originally sold the Maple advertising + 22 channels of 16-bit hardware PWM; the correct number is 15. + +Recommended Reading +------------------- + +* STMicro documentation for STM32F103RB microcontroller: + + * `Datasheet + <http://www.st.com/stonline/products/literature/ds/13587.pdf>`_ + (PDF) + * `Reference Manual + <http://www.st.com/stonline/products/literature/rm/13902.pdf>`_ + (PDF) + * `Programming Manual + <http://www.st.com/stonline/products/literature/pm/15491.pdf>`_ + (PDF; assembly language and register reference) + .. rubric:: Footnotes .. [#frev2_4] Revs 2 and 4 were prototypes that didn't pass internal testing. - diff --git a/docs/source/i2c.rst b/docs/source/i2c.rst index b4a996b..e3d68c0 100644 --- a/docs/source/i2c.rst +++ b/docs/source/i2c.rst @@ -1,15 +1,8 @@ .. _i2c: -===== |i2c| ===== -.. note:: - - The |i2c| interface is currently only available from the 'i2c' branch - of the github `libmaple <http://github.com/leaflabs/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 @@ -22,7 +15,7 @@ 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 +basis. 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. @@ -32,15 +25,15 @@ interrupt handlers) at the same time. 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. +.. FIXME [0.0.10 add links to board-specific values] -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. +Maple boards have two |i2c| ports. Maples reliably communicate 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 @@ -55,33 +48,22 @@ oscilloscope to debug any issues. Function Reference ------------------ -The function API for |i2c| is not finished! See the `source code -<http://github.com/leaflabs/libmaple/blob/i2c/libmaple/i2c.h>`_ for -now. - -.. TODO link to libmaple I2C docs once (1) finished, (2) in master +Currently, only low-level support in :ref:`libmaple-i2c` exists. A +Wiring-style library is planned for a future release. 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 +The STM32 microcontroller has hardware support for SMBus, but software +for it is not yet implemented. .. _i2c-recommended-reading: Recommended Reading ------------------- -* `i2c-bus.org <http://www.i2c-bus.org/>`_ -* `Wikipedia Article on i2c <http://en.wikipedia.org/wiki/I%C2%B2C>`_ -* `Arduino i2c/TWI reference <http://www.arduino.cc/playground/Learning/I2C>`_ -* STMicro documentation for STM32F103RB microcontroller: - - * `Datasheet <http://www.st.com/stonline/products/literature/ds/13587.pdf>`_ (pdf) - * `Reference Manual <http://www.st.com/stonline/products/literature/rm/13902.pdf>`_ (pdf) - * `Application Note on Advanced I2C Usage - <http://www.st.com/stonline/products/literature/an/15021.pdf>`_ - (pdf) +* `I2C Bus <http://www.i2c-bus.org/>`_ +* `Wikipedia: I2C <http://en.wikipedia.org/wiki/I%C2%B2C>`_ +* `Arduino I2C/TWI reference <http://www.arduino.cc/playground/Learning/I2C>`_ +* ST `Application Note on Advanced I2C Usage + <http://www.st.com/stonline/products/literature/an/15021.pdf>`_ (PDF) diff --git a/docs/source/index.rst b/docs/source/index.rst index 4369fbd..8c7ac84 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -1,13 +1,12 @@ .. _index: -Maple Documentation Contents +LeafLabs Documentation Index ============================ Welcome! This is the Maple documentation index. If you just bought a -Maple, you probably want to head to the :ref:`quickstart +Maple board, you probably want to head to the :ref:`quickstart <maple-quickstart>`. If you're having problems, check out the -:ref:`troubleshooting <troubleshooting>` and :ref:`known problems -<errata>` pages. +:ref:`troubleshooting <troubleshooting>` page. Have fun! @@ -32,12 +31,12 @@ Have fun! Language <language> Libraries <libraries> - Arduino Compatibility <compatibility> + Arduino Compatibility <arduino-compatibility> libmaple <libmaple> - External Interrupts <external-interrupts> Bootloader <bootloader> Troubleshooting <troubleshooting> - Known Problems <errata> + Notes on GCC's ARM target <arm-gcc> + Complete Language Index <language-index> .. _index-hardware: @@ -47,6 +46,7 @@ Have fun! :maxdepth: 1 adc + external-interrupts gpio i2c jtag @@ -56,7 +56,7 @@ Have fun! usb usart -.. _index-schematics: +.. _index-boards: **Board Hardware Documentation:** @@ -64,6 +64,7 @@ Have fun! :maxdepth: 1 hardware/maple.rst + hardware/maple-ret6.rst .. TODO write/include these upon Mini and Native release @@ -72,15 +73,3 @@ Have fun! .. hardware/maple-mini.rst .. hardware/maple-native.rst - -.. _index-reference: - -**Reference:** - -.. toctree:: - :maxdepth: 1 - - Technical Specifications <specs> - Complete Language Index <language-index> - - diff --git a/docs/source/jtag.rst b/docs/source/jtag.rst index 858021e..4151a53 100644 --- a/docs/source/jtag.rst +++ b/docs/source/jtag.rst @@ -1,11 +1,12 @@ +.. highlight:: cpp + .. _jtag: ====== JTAG ====== -.. TODO update adapter schematic, add information on using it with our -.. devices. +.. FIXME [0.1.0] Updated adapter schematic, better information JTAG is an interface for low-level debugging of digital devices. It gives instruction by instruction control over the microprocessor and @@ -37,32 +38,46 @@ Wiring Diagram to connect a standard 20-pin ARM JTAG device to the 8-pin JTAG port on the Maple. -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 -<http://leaflabs.com/store/>`_, 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. +The Maple has holes for a 8-pin JTAG header, but that header is not +soldered on. To use JTAG, simply solder on standard 0.1" pitch male +header pins (either the exact 4 by 2 block, or two 4-pin pieces of +straight breakaway header). Compatible Devices ------------------ We have had good experience with the `Olimex ARM-USB-OCD <http://www.olimex.com/dev/arm-usb-ocd.html>`_ device, which costs -about 55 euro plus shipping (as of November 2010). +about €55 plus shipping (as of April 2011). + +Function Reference +------------------ + +You can disable or enable the JTAG and Serial Wire debugging ports in +software using the ``disableDebugPorts()`` and ``enableDebugPorts()`` +functions. + +* :ref:`lang-disabledebugports` +* :ref:`lang-enabledebugports` Recommended Reading ------------------- -* `Wikipedia Article on Joint Test Action Group (JTAG) <http://en.wikipedia.org/wiki/Joint_Test_Action_Group>`_ -* `STM32/gdb/OpenOCD HOWTO <http://fun-tech.se/stm32/OpenOCD/gdb.php>`_ -* STMicro documentation for STM32F103RB microcontroller: +* `Wikipedia Article on Joint Test Action Group (JTAG) + <http://en.wikipedia.org/wiki/Joint_Test_Action_Group>`_ + +* `STM32, GDB, OpenOCD How To + <http://fun-tech.se/stm32/OpenOCD/gdb.php>`_ + +* `LeafLabs Wiki JTAG How To + <http://wiki.leaflabs.com/index.php?title=Maple_JTAG_How_To>`_ + +* `LeafLabs forum thread on JTAG + <http://forums.leaflabs.com/topic.php?id=536>`_ + +* ST documentation: - * `Datasheet <http://www.st.com/stonline/products/literature/ds/13587.pdf>`_ (pdf) - * `Reference Manual <http://www.st.com/stonline/products/literature/rm/13902.pdf>`_ (pdf) -* There's a `thread on JTAG - <http://forums.leaflabs.com/topic.php?id=536>`_ in our `forum`_ - which you may find useful. + * Reference Manual `RM0008 + <http://www.st.com/stonline/products/literature/rm/13902.pdf>`_ + (PDF), Chapter 31, "Debug support", and Chapter 9, + "General-purpose and alternate function I/Os". diff --git a/docs/source/lang/api/abs.rst b/docs/source/lang/api/abs.rst index 0cc6c23..d9f1ca3 100644 --- a/docs/source/lang/api/abs.rst +++ b/docs/source/lang/api/abs.rst @@ -45,5 +45,4 @@ Arduino Compatibility Maple's implementation of ``abs()`` is compatible with Arduino. - -.. include:: cc-attribution.txt +.. include:: /arduino-cc-attribution.txt diff --git a/docs/source/lang/api/analogread.rst b/docs/source/lang/api/analogread.rst index 35c6fbc..6665a94 100644 --- a/docs/source/lang/api/analogread.rst +++ b/docs/source/lang/api/analogread.rst @@ -20,53 +20,56 @@ Library Documentation Discussion ---------- -Reads the value from the specified analog pin. The Maple board -contains a 16-channel, 12-bit analog to digital converter. This means -that it will map input voltages between 0 and 3.3 volts into integer -values between 0 and 4095. This yields a resolution between readings -of 3.3V / 4096 units, or 0.8 millivolts. However, a number of factors +Reads the value from the specified analog pin. The Maple boards +contain 16-channel, 12-bit analog to digital converters. This means +that a converter will map input voltages between 0 and 3.3 volts into +integer values between 0 and 4095. However, a number of factors interfere with getting full accuracy and precision. For more information, see :ref:`adc`. Before calling analogRead() on a pin, that pin must first be -configured for analog input, using :ref:`lang-pinMode` (you only -have to do this once, so it's usually done in :ref:`lang-setup`\ ). +configured for analog input, using :ref:`lang-pinMode`. You only have +to do this once, so it's usually done in :ref:`lang-setup`\ . Parameter Discussion -------------------- The pin parameter is the number of the analog input pin to read from. -Header pins on the Maple with ADC functionality (marked as "AIN" on -the silkscreen) are: - - 0, 1, 2, 3, 10, 11, 12, 13, 15, 16, 17, 18, 19, 20, 27, 28 - -Note that pins 3, 27, and 28 are not marked AIN on the silkscreen -for Maple revisions through Rev 5, however, they **do work** as -analog input pins. +The pins which support analog to digital conversion have ``AIN`` +listed underneath their number on your board's silkscreen. These pin +numbers are available to your program in the :ref:`boardADCPins +<lang-board-values-adc-pins>` board-specific array. The number of +pins which are capable of analog to digital conversion on your board +is given by the ``BOARD_NR_ADC_PINS`` constant. These values are +documented for each board in the :ref:`Board Hardware Documentation +<index-boards>` pages. + +.. note:: Pin 3 is not marked ``AIN`` on the silkscreen for Maple + revisions through Rev 5; however **it does work** as an analog + input pin. Note ---- If the analog input pin is not connected to anything, the value -returned by analogRead() will fluctuate based on a number of factors -(e.g. the values of the other analog inputs, how close your hand is to -the board, etc.) in a seemingly random way. - +returned by ``analogRead()`` will fluctuate due to a number of reasons +(like the values of the other analog inputs, how close your hand is to +the board, etc.) in a "random" way. Example ------- - :: +:: + int analogPin = 3; // Potentiometer wiper (middle terminal) connected + // to analog pin 3. outside leads to ground and +3.3V. + // You may have to change this value if your board + // cannot perform ADC conversion on pin 3. - int analogPin = 3; // potentiometer wiper (middle terminal) connected - // to analog pin 3. outside leads to ground and +3.3V int val = 0; // variable to store the value read void setup() { pinMode(analogPin, INPUT_ANALOG); // set up pin for analog input - SerialUSB.begin(); // set up usb virtual COM port } void loop() { @@ -75,7 +78,6 @@ Example // a serial monitor } - Arduino Compatibility --------------------- @@ -97,23 +99,21 @@ shift <lang-bitshift>` the value of a Maple readout by 2, like so:: // be aware that you're losing a lot of precision if you do this int adc_reading = analogRead(pin) >> 2; +.. FIXME [0.1.0] Mention that Native can do analogReference() + On the Arduino, the input range and resolution can be changed using -their implementation of `analogReference() -<http://arduino.cc/en/Reference/AnalogReference>`_\ . Because of the -way its hardware (as of Rev 5) was designed, it's not possible to -implement analogReference on the Maple, so this function doesn't -exist. If your inputs lie in a different voltage range than 0V--3.3V, -you'll need to bring them into that range before using analogRead. -Some basic tools to accomplish this are `resistor dividers -<http://en.wikipedia.org/wiki/Voltage_divider>`_ and `Zener diodes -<http://en.wikipedia.org/wiki/Voltage_source#Zener_voltage_source>`_\ -. However, opamps and other powered components can also be used if -greater precision is required. - -See also +the `analogReference() +<http://arduino.cc/en/Reference/AnalogReference>`_ function. Because +of hardware restrictions, this function is not available on the Maple +and Maple RET6 Edition. If your inputs lie in a different voltage +range than 0V--3.3V, you'll need to bring them into that range before +using ``analogRead()``. See the :ref:`ADC reference <adc-range>` for +more information. + +See Also -------- -- :ref:`ADC note <adc>` +- :ref:`ADC tutorial <adc>` - `(Arduino) Tutorial: Analog Input Pins <http://arduino.cc/en/Tutorial/AnalogInputPins>`_ -.. include:: cc-attribution.txt +.. include:: /arduino-cc-attribution.txt diff --git a/docs/source/lang/api/analogwrite.rst b/docs/source/lang/api/analogwrite.rst index 9147b96..dd2192a 100644 --- a/docs/source/lang/api/analogwrite.rst +++ b/docs/source/lang/api/analogwrite.rst @@ -52,7 +52,7 @@ you much more precise control over the duty cycle of your PWM output. If you're porting code from the Arduino and want a quick-and-dirty fix, one solution is to :ref:`map <lang-map>` the argument to -analogWrite into the right range:: +analogWrite() into the right range:: // Arduino code: analogWrite(pin, duty); @@ -65,14 +65,14 @@ This will convert values in the range 0-255 to values in the range which control PWM output. See the :ref:`timers reference <timers>` for more information. -Another fix is to consult the :ref:`pin mapping mega table -<pin-mapping-mega-table>` to find the timer which controls PWM on the -pin you're using, then set that Timer's overflow to 255. Subsequent -calls to analogWrite() should work as on the Arduino (with the same -loss of precision). Note, however, that that affects the overflow for -the **entire timer**, so other code relying on that timer (such as any -:ref:`interrupts <lang-attachinterrupt>` the timer controls) will -likely need to be modified as well. +Another fix is to consult your board's :ref:`pin maps <gpio-pin-maps>` +to find the timer which controls PWM on the pin you're using, then set +that Timer's overflow to 255. Subsequent calls to analogWrite() +should work as on the Arduino (with the same loss of precision). +Note, however, that that affects the overflow for the **entire +timer**, so other code relying on that timer (such as any +:ref:`interrupts <lang-hardwaretimer-interrupts>` the timer controls) +will likely need to be modified as well. Difference 2: You must use pinMode() to set up PWM ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -140,24 +140,28 @@ If your application definitely requires Arduino's PWM frequency, then the steps are: 1. Figure out which :ref:`timer <lang-hardwaretimer>` controls PWM - output on your pin (\ :ref:`this table <pwm-timer-table>` is your - friend here). Let's say it's ``Timern``\ , where ``n`` is some - number 1, 2, 3, or 4. + output on your pin (\ :ref:`your board's Timer Pin Map + <gpio-pin-maps>` is your friend here). -2. Call ``Timern.setPeriod(2041)``\ . This will set the timer's - period to approximately 2041 microseconds, which is a frequency of - approximately 490 Hz. +2. Let's say it's timer ``n``, where ``n`` is some number. You'll + then need to put "``HardwareTimer timer(n);``" with your variables, + as described in the :ref:`HardwareTimer + <lang-hardwaretimer-getting-started>` reference. + +3. In your :ref:`lang-setup`, put "``timer.setPeriod(2041);``". This + will set the timer's period to approximately 2041 microseconds, + which is a frequency of approximately 490 Hz. Be aware that this will change the period for the **entire timer**\ , and will affect anything else in your program that depends on that timer. The important examples are :ref:`timer interrupts -<lang-hardwaretimer-attachinterrupt>` and :ref:`PWM +<lang-hardwaretimer-interrupts>` and :ref:`PWM <timers-pwm-conflicts>`\ . -See also +See Also -------- -- :ref:`Maple PWM tutorial <pwm>` +- :ref:`pwm` .. rubric:: Footnotes @@ -169,5 +173,4 @@ See also Maple uses 2 bytes of memory, and an unsigned (i.e., nonnegative) integer with size 2 bytes can hold the values between 0 and 65,535. - -.. include:: cc-attribution.txt +.. include:: /arduino-cc-attribution.txt diff --git a/docs/source/lang/api/attachinterrupt.rst b/docs/source/lang/api/attachinterrupt.rst index 7c5a6c7..39902ac 100644 --- a/docs/source/lang/api/attachinterrupt.rst +++ b/docs/source/lang/api/attachinterrupt.rst @@ -5,9 +5,8 @@ attachInterrupt() ================= -Used to specify a function to call when an external interrupt (like an -GPIO changing from LOW to HIGH, a button getting pressed, etc.) -occurs. +Used to specify a function to call when an :ref:`external interrupt +<external-interrupts>` occurs. .. contents:: Contents :local: @@ -15,9 +14,9 @@ occurs. Library Documentation --------------------- -.. FIXME once breathe knows how to get the correct attachInterupt -.. (right now it's copying from HardwareTimer), replace with a -.. doxygenfunction directive +.. FIXME [doxygenfunction] once Breathe knows how to get the correct +.. attachInterupt (right now it's copying from HardwareTimer), replace +.. with a doxygenfunction directive .. cpp:function:: void attachInterrupt(uint8 pin, voidFuncPtr handler, ExtIntTriggerMode mode) @@ -47,49 +46,29 @@ Discussion Because the function will run in interrupt context, inside of it, :ref:`lang-delay` won't work, and the value returned by -:ref:`lang-millis` will not increment. Serial data received while -in the function may be lost. You should declare as ``volatile`` any +:ref:`lang-millis` will not increment. Serial data received while in +the function may be lost. You should declare as ``volatile`` any global variables that you modify within the attached function. -There are a few constraints you should be aware of if you're using -more than one interrupt at a time; the :ref:`external-interrupts` page -has the details. - -Using Interrupts ----------------- - -Interrupts are useful for making things happen automatically in -microcontroller programs, and can help solve timing problems. A -good task for using an interrupt might be reading a rotary encoder, -or monitoring user input. - -If you wanted to insure that a program always caught the pulses -from a rotary encoder, never missing a pulse, it would make it very -tricky to write a program to do anything else, because the program -would need to constantly poll the sensor lines for the encoder, in -order to catch pulses when they occurred. Other sensors have a -similar interface dynamic too, such as trying to read a sound -sensor that is trying to catch a click, or an infrared slot sensor -(photo-interrupter) trying to catch a coin drop. In all of these -situations, using an interrupt can free the microcontroller to get -some other work done while not missing the doorbell. +There are a few limits you should be aware of if you're using more +than one interrupt at a time; the :ref:`External Interrupts +<external-interrupts-exti-line>` page has more information. Example ------- -:: + :: - int maple_led_pin = 13; volatile int state = LOW; // must declare volatile, since it's - // modified within the blink handler + // modified within the blink() handler void setup() { - pinMode(maple_led_pin, OUTPUT); + pinMode(BOARD_LED_PIN, OUTPUT); attachInterrupt(0, blink, CHANGE); } void loop() { - digitalWrite(maple_led_pin, state); + digitalWrite(BOARD_LED_PIN, state); } void blink() { @@ -106,10 +85,10 @@ additional four: numbers 2 (pin 21), 3 (pin 20), 4 (pin 19), and 5 number goes with which pin -- just tell ``attachInterrupt()`` the pin you want. -See also +See Also -------- -- :ref:`detachInterrupt <lang-detachinterrupt>` -- :ref:`external-interrupts` +- :ref:`lang-detachinterrupt` +- :ref:`external-interrupts` -.. include:: cc-attribution.txt +.. include:: /arduino-cc-attribution.txt diff --git a/docs/source/lang/api/bit.rst b/docs/source/lang/api/bit.rst index dd5c050..3df042c 100644 --- a/docs/source/lang/api/bit.rst +++ b/docs/source/lang/api/bit.rst @@ -12,33 +12,27 @@ Syntax ``bit(n)`` - Parameters ---------- * **n** the bit to set. - Value ----- The value of an integer with the given bit set. - Arduino Compatibility --------------------- -The Maple implementation of bit is compatible with Arduino. +The Maple implementation of ``bit()`` is compatible with Arduino. - -See also +See Also -------- - - :ref:`lang-bitread` - :ref:`lang-bitwrite` - :ref:`lang-bitset` - :ref:`lang-bitclear` - -.. include:: cc-attribution.txt +.. include:: /arduino-cc-attribution.txt diff --git a/docs/source/lang/api/bitclear.rst b/docs/source/lang/api/bitclear.rst index 941f912..f487059 100644 --- a/docs/source/lang/api/bitclear.rst +++ b/docs/source/lang/api/bitclear.rst @@ -10,7 +10,6 @@ Syntax ``bitClear(x, n)`` - Parameters ---------- @@ -19,20 +18,17 @@ Parameters * **n** which bit to clear, starting at 0 for the least-significant (rightmost) bit - Returns ------- -None. - +Nothing. Arduino Compatibility --------------------- -This implementation is compatible with that of Arduino. +The Maple implementation of ``bitClear()`` is compatible with Arduino. - -See also +See Also -------- - :ref:`bit <lang-bit>`\ () @@ -40,5 +36,4 @@ See also - :ref:`bitWrite <lang-bitwrite>`\ () - :ref:`bitSet <lang-bitset>`\ () - -.. include:: cc-attribution.txt +.. include:: /arduino-cc-attribution.txt diff --git a/docs/source/lang/api/bitread.rst b/docs/source/lang/api/bitread.rst index 46b4478..fd9fbbe 100644 --- a/docs/source/lang/api/bitread.rst +++ b/docs/source/lang/api/bitread.rst @@ -5,13 +5,11 @@ bitRead() (Macro) Gets the value of a bit in a number. - Syntax ------ ``bitRead(x, n)`` - Parameters ---------- @@ -20,27 +18,22 @@ Parameters * **n** which bit to read, starting at 0 for the least-significant (rightmost) bit - Value ----- The value of the bit (0 or 1). - Arduino Compatibility --------------------- The Maple implementation of ``bitRead`` is compatible with Arduino. - -See also +See Also -------- - - :ref:`lang-bit` - :ref:`lang-bitwrite` - :ref:`lang-bitset` - :ref:`lang-bitclear` - -.. include:: cc-attribution.txt +.. include:: /arduino-cc-attribution.txt diff --git a/docs/source/lang/api/bitset.rst b/docs/source/lang/api/bitset.rst index ccd76de..83ab5f8 100644 --- a/docs/source/lang/api/bitset.rst +++ b/docs/source/lang/api/bitset.rst @@ -5,13 +5,11 @@ bitSet() (Macro) Sets (writes a 1 to) a bit of a numeric variable. - Syntax ------ ``bitSet(x, n)`` - Parameters ---------- @@ -20,19 +18,16 @@ Parameters * **n** which bit to set, starting at 0 for the least-significant (rightmost) bit - Value ----- None. - Arduino Compatibility --------------------- The Maple implementation of bitSet is compatible with Arduino. - See Also -------- @@ -41,6 +36,4 @@ See Also - :ref:`lang-bitwrite` - :ref:`lang-bitclear` - - -.. include:: cc-attribution.txt +.. include:: /arduino-cc-attribution.txt diff --git a/docs/source/lang/api/bitwrite.rst b/docs/source/lang/api/bitwrite.rst index b3feff2..6106545 100644 --- a/docs/source/lang/api/bitwrite.rst +++ b/docs/source/lang/api/bitwrite.rst @@ -32,9 +32,9 @@ Nothing. Arduino Compatibility --------------------- -Maple's version of ``bitWrite()`` is compatible with Arduino. +Maple's implementation of ``bitWrite()`` is compatible with Arduino. -See also +See Also -------- - :ref:`bit() <lang-bit>` @@ -42,5 +42,4 @@ See also - :ref:`bitSet() <lang-bitSet>` - :ref:`bitClear() <lang-bitClear>` - -.. include:: cc-attribution.txt +.. include:: /arduino-cc-attribution.txt diff --git a/docs/source/lang/api/board-values.rst b/docs/source/lang/api/board-values.rst new file mode 100644 index 0000000..05e3837 --- /dev/null +++ b/docs/source/lang/api/board-values.rst @@ -0,0 +1,180 @@ +.. _lang-board-values: + +Board-Specific Constants +======================== + +There are a number of board-specific values: constants or variables +which are different depending on which LeafLabs board you have. + +This page lists and documents the board-specific values. You should +use these when appropriate in your own programs. This will help make +it easier to share your code with other people who have different +boards. Some example usages are given :ref:`below +<lang-board-values-examples>`. + +The actual values for each board are given in the :ref:`Board Hardware +Documentation <index-boards>`. + +.. contents:: Contents + :local: + +Constants +--------- + +- ``CLOCK_SPEED_MHZ``: Clock speed of your board, in megahertz + (MHz). This is the same as ``CYCLES_PER_MICROSECOND``. + +- ``CLOCK_SPEED_HZ``: Clock speed of your board, in hertz (Hz). This + is the same as ``CLOCK_SPEED_MHZ * 1000000``. + +- ``CYCLES_PER_MICROSECOND``: Number of CPU cycles per microsecond on + your board. + +.. _lang-board-values-but: + +- ``BOARD_BUTTON_PIN``: Pin connected to the built-in button (labeled + "BUT" on your board's silkscreen). + +.. _lang-board-values-led: + +- ``BOARD_LED_PIN``: Pin connected to the built-in LED. + +- ``BOARD_NR_GPIO_PINS``: Total number of :ref:`GPIO pins <gpio>` that + are broken out to headers. Some of these might already be connected + to external hardware (like the built-in LED and button). To find + out if a pin is in use, see :ref:`boardUsesPin() + <lang-boardusespin>` (and also :ref:`boardUsedPins + <lang-board-values-used-pins>`). + +.. _lang-board-values-nr-pwm-pins: + +- ``BOARD_NR_PWM_PINS``: Total *number* of GPIO pins that can be used + for :ref:`PWM <pwm>`. The actual *pins* that can do PWM are in the + :ref:`boardPWMPins <lang-board-values-pwm-pins>` array. + +.. _lang-board-values-nr-adc-pins: + +- ``BOARD_NR_ADC_PINS``: Total number of GPIO pins that can be used + for :ref:`analog-to-digital conversion <adc>`. The actual pins that + can do ADC conversion are in the :ref:`boardADCPins + <lang-board-values-adc-pins>` array. + +.. _lang-board-values-nr-used-pins: + +- ``BOARD_NR_USED_PINS``: Total number of GPIO pins that are already + connected to some external hardware (like a built-in LED) on the + board. The actual pins that that are already used are in the + :ref:`boardUsedPins <lang-board-values-used-pins>` array. To see if + a pin is already in use, use the :ref:`boardUsesPin() + <lang-boardusespin>` function. + +.. _lang-board-values-usart: + +- USART (serial port) related constants: + + * ``BOARD_USART1_TX_PIN``, ``BOARD_USART2_TX_PIN``, ``BOARD_USART3_TX_PIN``: + TX pins for the 3 USARTS. + + * ``BOARD_USART1_RX_PIN``, ``BOARD_USART2_RX_PIN``, ``BOARD_USART3_RX_PIN``: + RX pins for the 3 USARTS. + + * ``BOARD_UART4_TX_PIN``, ``BOARD_UART5_TX_PIN``: TX pins for + UARTs 4 and 5 (high-density boards like Maple Native only). + + * ``BOARD_UART4_RX_PIN``, ``BOARD_UART5_RX_PIN``: RX pins for + UARTs 4 and 5 (high-density boards like Maple Native only). + + * ``BOARD_NR_USARTS``: Number of serial ports on the board. This + number includes UARTs 4 and 5 if they are available. + +.. _lang-board-values-debug: + +- Debug (JTAG, SW-Debug) related constants: ``BOARD_JTMS_SWDIO_PIN``, + ``BOARD_JTCK_SWCLK_PIN``, ``BOARD_JTDI_PIN``, ``BOARD_JTDO_PIN``, + and ``BOARD_NJTRST_PIN``. + + These constants are the pin numbers for :ref:`GPIOs <gpio>` used by + the :ref:`jtag` and Serial-Wire Debug peripherals. Except for the + Maple Mini, these pins are usually reserved for special purposes by + default (i.e., they are in :ref:`boardUsedPins + <lang-board-values-used-pins>`). However, they can be used as + ordinary GPIOs if you call the :ref:`lang-disabledebugports` + function. (Be careful with this on the Maple, as writing to + ``BOARD_NJTRST_PIN`` may cause your board to reset!). + +.. _lang-board-values-pwm-pins: + +.. _lang-board-values-adc-pins: + +.. _lang-board-values-used-pins: + +Pin Arrays +---------- + +Some :ref:`arrays <lang-array>` of pin numbers are available which you +can use to find out certain important information about a given pin. + +- ``boardPWMPins``: Pin numbers of each pin capable of :ref:`PWM + <pwm>` output, using :ref:`pwmWrite() <lang-pwmwrite>`. The total + number of these pins is :ref:`BOARD_NR_PWM_PINS + <lang-board-values-nr-pwm-pins>`. + +- ``boardADCPins``: Pin numbers of each pin capable of :ref:`ADC + <adc>` conversion, using :ref:`analogRead() <lang-analogread>`. The + total number of these pins is :ref:`BOARD_NR_ADC_PINS + <lang-board-values-nr-adc-pins>`. + +- ``boardUsedPins``: Pin numbers of each pin that, by default, is used + for some special purpose by the board. The total number of these + pins is :ref:`BOARD_NR_USED_PINS <lang-board-values-nr-used-pins>`. + To check if a pin is used for a special purpose, use + :ref:`boardUsesPin() <lang-boardusespin>`. + +.. _lang-board-values-examples: + +Examples +-------- + +:ref:`BOARD_LED_PIN <lang-board-values-led>` On the Maple, the +built-in LED is connected to pin 13. On the Maple Mini, however, it +is connected to pin 33. You can write a "blinky" program that works +on all LeafLabs boards using ``BOARD_LED_PIN`` and :ref:`toggleLED() +<lang-toggleled>`:: + + void setup() { + pinMode(BOARD_LED_PIN, OUTPUT); + } + + void loop() { + toggleLED(); + delay(100); + } + +:ref:`BOARD_BUTTON_PIN <lang-board-values-but>`: Similarly, you can +write a single program that prints a message whenever the button is +pressed which will work on all LeafLabs boards using +``BOARD_BUTTON_PIN`` and :ref:`isButtonPressed() +<lang-isbuttonpressed>`:: + + void setup() { + pinMode(BOARD_BUTTON_PIN, INPUT); + } + + void loop() { + if (isButtonPressed()) { + SerialUSB.println("You pressed the button!"); + } + } + +See Also +-------- + +- :ref:`lang-boardusespin` +- :ref:`lang-isbuttonpressed` +- :ref:`lang-waitforbuttonpress` +- :ref:`lang-pinmode` +- :ref:`lang-toggleled` +- :ref:`lang-analogread` +- :ref:`lang-pwmwrite` +- :ref:`lang-enabledebugports` +- :ref:`lang-disabledebugports` diff --git a/docs/source/lang/api/boardusespin.rst b/docs/source/lang/api/boardusespin.rst new file mode 100644 index 0000000..126c4a0 --- /dev/null +++ b/docs/source/lang/api/boardusespin.rst @@ -0,0 +1,27 @@ +.. highlight:: cpp + +.. _lang-boardusespin: + +boardUsesPin() +============== + +Each LeafLabs board connects some pins to external hardware. The most +important examples of this are the pins connected to the built-in LED +and button. You can check if a board uses a particular pin using this +function. + +Library Documentation +--------------------- + +.. doxygenfunction:: boardUsesPin + +See Also +-------- + +- :ref:`Board-specific values <lang-board-values>` +- :ref:`boardUsedPins <lang-board-values-used-pins>` +- :ref:`BOARD_LED_PIN <lang-board-values-led>` +- :ref:`toggleLED() <lang-toggleled>` +- :ref:`BOARD_BUTTON_PIN <lang-board-values-but>` +- :ref:`isButtonPressed() <lang-isbuttonpressed>` +- :ref:`waitForButtonPress() <lang-waitforbuttonpress>` diff --git a/docs/source/lang/api/constants.rst b/docs/source/lang/api/constants.rst index 72738b8..00c1a5c 100644 --- a/docs/source/lang/api/constants.rst +++ b/docs/source/lang/api/constants.rst @@ -61,14 +61,6 @@ pin is configured as an ``INPUT`` (using :ref:`pinMode() <lang-digitalread>`, the microcontroller will report ``HIGH`` if a voltage of 3 volts or more is present at the pin. -.. TODO? Following seems false; check it out sometime, leave out for now: - -.. A pin may also be configured as an ``INPUT`` with ``pinMode()``, and -.. subsequently made ``HIGH`` with :ref:`digitalWrite() -.. <lang-digitalwrite>`, this will set the internal pullup resistors, -.. which will *steer* the input pin to a HIGH reading unless it is pulled -.. LOW by external circuitry. - When a pin is configured to ``OUTPUT`` with pinMode, and set to ``HIGH`` with :ref:`digitalWrite() <lang-digitalwrite>`, the pin is at 3.3 volts. In this state it can *source* current, e.g. light an LED @@ -301,23 +293,16 @@ exponent indicators. Some examples are given in the following table: Board-Specific Constants ------------------------ -This section documents constants whose value might change across -different LeafLabs boards. You can use these constants to help ensure -that your code will be portable across different boards. - -.. TODO replace "upcoming" when Mini, Native come out - -.. _lang-constants-led: - -- ``BOARD_LED_PIN``: the number of the pin which connects to the - built-in LED. On the Maple, this is pin 13, but it's not guaranteed - to be the same in upcoming boards like the Maple Mini. - -.. _lang-constants-but: +There are several :ref:`board-specific constants <lang-board-values>` +whose value depends on which LeafLabs board you have. If you use +them, it will help make sure that your code will work well on all +LeafLabs boards, not just the one you have. This will make it much +easier to share your code with others. -- ``BOARD_BUTTON_PIN``: the number of the pin which connects to the - built-in button (labeled "BUT"). On the Maple, this is pin 38, but - it's not guaranteed to be the same in other boards. +For example, the pin number connected to the board's built-in LED is +different on the different boards, but the board-specific constant +:ref:`BOARD_LED_PIN <lang-board-values-led>` will always be the +correct value for each board. See Also -------- @@ -333,5 +318,6 @@ See Also - :ref:`unsigned long long <lang-unsignedlonglong>` - :ref:`float <lang-float>` - :ref:`double <lang-double>` +- :ref:`Board-Specific Values <lang-board-values>` -.. include:: cc-attribution.txt +.. include:: /arduino-cc-attribution.txt diff --git a/docs/source/lang/api/constrain.rst b/docs/source/lang/api/constrain.rst index d19b61c..28af1e3 100644 --- a/docs/source/lang/api/constrain.rst +++ b/docs/source/lang/api/constrain.rst @@ -59,11 +59,10 @@ Arduino Compatibility Maple's implementation of ``constrain()`` is compatible with Arduino. -See also +See Also -------- - :ref:`min() <lang-min>` - :ref:`max() <lang-max>` - -.. include:: cc-attribution.txt +.. include:: /arduino-cc-attribution.txt diff --git a/docs/source/lang/api/cos.rst b/docs/source/lang/api/cos.rst index 3fbb0af..c340f09 100644 --- a/docs/source/lang/api/cos.rst +++ b/docs/source/lang/api/cos.rst @@ -19,14 +19,12 @@ Note that the Maple implementation comes from `newlib <http://sourceware.org/newlib/>`_\ , while Arduino's is that of `avr-libc <http://avr-libc.nongnu.org/>`_\ . -See also +See Also -------- - - :ref:`sin() <lang-sin>` - :ref:`tan() <lang-tan>` - :ref:`float <lang-float>` - :ref:`double <lang-double>` - -.. include:: cc-attribution.txt +.. include:: /arduino-cc-attribution.txt diff --git a/docs/source/lang/api/delay.rst b/docs/source/lang/api/delay.rst index 90ca268..9ef06a0 100644 --- a/docs/source/lang/api/delay.rst +++ b/docs/source/lang/api/delay.rst @@ -57,10 +57,9 @@ Example .. _lang-delay-seealso: -See also +See Also -------- - - :ref:`millis() <lang-millis>` - :ref:`micros() <lang-micros>` - :ref:`delayMicroseconds() <lang-delayMicroseconds>` @@ -68,5 +67,4 @@ See also <http://arduino.cc/en/Tutorial/BlinkWithoutDelay>`_ example (works unmodified on Maple) - -.. include:: cc-attribution.txt +.. include:: /arduino-cc-attribution.txt diff --git a/docs/source/lang/api/delaymicroseconds.rst b/docs/source/lang/api/delaymicroseconds.rst index 24a8286..7078660 100644 --- a/docs/source/lang/api/delaymicroseconds.rst +++ b/docs/source/lang/api/delaymicroseconds.rst @@ -48,9 +48,9 @@ Arduino Compatibility --------------------- While we have made every effort we could to ensure that the timing of -delayMicroseconds is as accurate as possible, we cannot guarantee it -will behave as the Arduino implementation down to the microsecond, -especially for smaller values of ``us``. +``delayMicroseconds()`` is as accurate as possible, we cannot +guarantee it will behave as the Arduino implementation down to the +microsecond, especially for smaller values of ``us``. See Also -------- @@ -59,7 +59,4 @@ See Also - :ref:`micros <lang-micros>` - :ref:`delay <lang-delay>` - - - -.. include:: cc-attribution.txt +.. include:: /arduino-cc-attribution.txt diff --git a/docs/source/lang/api/detachinterrupt.rst b/docs/source/lang/api/detachinterrupt.rst index 41642a7..82ce974 100644 --- a/docs/source/lang/api/detachinterrupt.rst +++ b/docs/source/lang/api/detachinterrupt.rst @@ -9,9 +9,8 @@ Used to disable an interrupt specified with Library Documentation --------------------- -.. FIXME once breathe knows how to get the correct detachInterupt -.. (right now it's copying from HardwareTimer), replace with a -.. doxygenfunction directive +.. FIXME [Breathe] once we can get the correct detachInterupt(), +.. replace with doxygenfunction. .. cpp:function:: void detachInterrupt(uint8 pin) @@ -39,5 +38,6 @@ See Also -------- - :ref:`attachInterrupt() <lang-attachInterrupt>` +- :ref:`external-interrupts` -.. include:: cc-attribution.txt +.. include:: /arduino-cc-attribution.txt diff --git a/docs/source/lang/api/digitalread.rst b/docs/source/lang/api/digitalread.rst index 3502587..ccf4a4c 100644 --- a/docs/source/lang/api/digitalread.rst +++ b/docs/source/lang/api/digitalread.rst @@ -8,51 +8,44 @@ digitalRead() Reads the value from a specified digital pin, either :ref:`HIGH <lang-constants-high>` or :ref:`LOW <lang-constants-low>`. - Library Documentation --------------------- .. doxygenfunction:: digitalRead +Discussion +---------- + +If the pin isn't connected to anything, ``digitalRead()`` can return +either HIGH or LOW (and this will change in a way that seems random). Example ------- -The following example turns the LED on when the button is pressed:: - - int ledPin = 13; // LED connected to Maple pin 13 - int buttonPin = 38; // BUT connected to Maple pin 38 +The following example turns the LED on or off when the button is pressed:: void setup() { - pinMode(ledPin, OUTPUT); - pinMode(buttonPin, INPUT); + pinMode(BOARD_LED_PIN, OUTPUT); + pinMode(BOARD_BUTTON_PIN, INPUT); } void loop() { - int val = digitalRead(buttonPin); // reads the input pin - digitalWrite(ledPin, val); + int val = digitalRead(BOARD_BUTTON_PIN); // reads the input pin + togglePin(BOARD_LED_PIN, val); } -Note ----- - -If the pin isn't connected to anything, ``digitalRead()`` can return -either HIGH or LOW (and this can change in a way that seems random). - Arduino Compatibility --------------------- The Maple version of ``digitalRead()`` is compatible with Arduino. - See Also -------- -- :ref:`pinMode <lang-pinMode>` -- :ref:`digitalWrite <lang-digitalWrite>` - - - - +- :ref:`BOARD_BUTTON_PIN <lang-board-values-but>` +- :ref:`lang-isButtonPressed` +- :ref:`lang-pinmode` +- :ref:`lang-digitalWrite` +- :ref:`lang-togglepin` -.. include:: cc-attribution.txt +.. include:: /arduino-cc-attribution.txt diff --git a/docs/source/lang/api/digitalwrite.rst b/docs/source/lang/api/digitalwrite.rst index 6124d5f..376cbc3 100644 --- a/docs/source/lang/api/digitalwrite.rst +++ b/docs/source/lang/api/digitalwrite.rst @@ -21,42 +21,26 @@ If the pin has been configured as an ``OUTPUT`` with :ref:`pinMode() <lang-pinmode>` its voltage will be set to the corresponding value: 3.3V for ``HIGH``, and 0V (ground) for ``LOW``. -.. TODO make the following paragraphs true, but refer the reader to -.. INPUT_PULLUP and INPUT_PULLDOWN: - -If the pin is configured as an ``INPUT``, writing a ``HIGH`` value -with ``digitalWrite()`` will enable an internal pullup resistor. -Writing ``LOW`` will disable the pullup. The pullup resistor is enough -to light an LED dimly, so if LEDs appear to work, but very dimly, this -is a likely cause. The remedy is to set the pin to an output with the -:ref:`pinMode() <lang-pinmode>` function. - -.. note:: Pin 13 is harder to use as an input than the other pins - because it has an LED and resistor soldered to it in series. If you - enable its internal pull-up resistor, it will likely hang at around - 1.1V instead of the expected 3.3V because the onboard LED and - series resistor pull the voltage level down. If you must use pin 13 - as a digital input, use an external pull-down resistor. +Because it is soldered to an LED and resistor in series, your board's +:ref:`BOARD_LED_PIN <lang-board-values-led>` will respond slightly +more slowly as an output than the other pins. Example ------- The following example sets pin 13 to ``HIGH``, makes a one-second-long delay, sets the pin back to ``LOW``, and delays again, causing a -blinking pattern:: - - - int ledPin = 13; // LED connected to digital pin 13 +blinking pattern (you could also use :ref:`lang-toggleled`):: void setup() { - pinMode(ledPin, OUTPUT); // sets the digital pin as output + pinMode(BOARD_LED_PIN, OUTPUT); // sets the digital pin as output } void loop() { - digitalWrite(ledPin, HIGH); // sets the LED on - delay(1000); // waits for a second - digitalWrite(ledPin, LOW); // sets the LED off - delay(1000); // waits for a second + digitalWrite(BOARD_LED_PIN, HIGH); // sets the LED on + delay(1000); // waits for a second + digitalWrite(BOARD_LED_PIN, LOW); // sets the LED off + delay(1000); // waits for a second } See Also @@ -64,5 +48,8 @@ See Also - :ref:`pinMode <lang-pinmode>` - :ref:`digitalRead <lang-digitalread>` +- :ref:`BOARD_LED_PIN <lang-board-values-led>` +- :ref:`lang-toggleled` +- :ref:`lang-togglepin` -.. include:: cc-attribution.txt +.. include:: /arduino-cc-attribution.txt diff --git a/docs/source/lang/api/disabledebugports.rst b/docs/source/lang/api/disabledebugports.rst new file mode 100644 index 0000000..43ac337 --- /dev/null +++ b/docs/source/lang/api/disabledebugports.rst @@ -0,0 +1,31 @@ +.. highlight:: cpp + +.. _lang-disabledebugports: + +disableDebugPorts() +=================== + +Used to disable the JTAG and Serial Wire debugging ports. + +Library Documentation +--------------------- + +.. doxygenfunction:: disableDebugPorts + +Example +------- + + :: + + void setup() { + disableDebugPorts(); + } + + void loop() { + // Now you can use the debug port pins as ordinary pins + } + +See Also +-------- + +* :ref:`lang-enabledebugports` diff --git a/docs/source/lang/api/enabledebugports.rst b/docs/source/lang/api/enabledebugports.rst new file mode 100644 index 0000000..bee2b0a --- /dev/null +++ b/docs/source/lang/api/enabledebugports.rst @@ -0,0 +1,31 @@ +.. highlight:: cpp + +.. _lang-enabledebugports: + +enableDebugPorts() +================== + +Used to enable the JTAG and Serial Wire debugging ports. + +Library Documentation +--------------------- + +.. doxygenfunction:: enableDebugPorts + +Example +------- + + :: + + void setup() { + enableDebugPorts(); + // Now you can debug using JTAG and SW-Debug + } + + void loop() { + } + +See Also +-------- + +* :ref:`lang-disabledebugports` diff --git a/docs/source/lang/api/hardwarespi.rst b/docs/source/lang/api/hardwarespi.rst index 53a225d..289ded5 100644 --- a/docs/source/lang/api/hardwarespi.rst +++ b/docs/source/lang/api/hardwarespi.rst @@ -5,160 +5,161 @@ HardwareSPI =========== -This class is used for creating objects to manage the Maple's built-in -SPI ports. The Maple has two SPI ports. The relevant pins -corresponding to each port's logic signals are documented in the -following table (and on the Maple silkscreen): +This page describes how to use the built-in SPI ports. It does not +describe the SPI protocol itself. For more information about SPI, see +the :ref:`SPI reference <spi>`. -.. _lang-hardwarespi-pinout: +.. contents:: Contents + :local: -.. list-table:: - :header-rows: 1 +Getting Started +--------------- - * - Port number - - NSS - - MOSI - - MISO - - SCK +.. TODO [0.1.0] Add a note about calling disableDebugPorts() when +.. using SPI3 on Maple Native - * - 1 - - 10 - - 11 - - 12 - - 13 +In order to get started, you'll first need to define a ``HardwareSPI`` +variable, which you'll use to control the SPI port. Do this by +putting the line "``HardwareSPI spi(number);``" with your variables, +where ``number`` is the SPI port's number. - * - 2 - - 31 - - 32 - - 33 - - 34 +Here's an example (we'll fill in :ref:`setup() <lang-setup>` and +:ref:`loop() <lang-loop>` later):: -If you use a SPI port, you cannot simultaneously use its associated -pins for other purposes. + // Use SPI port number 1 + HardwareSPI spi(1); -Library Documentation ---------------------- + void setup() { + // Your setup code + } + + void loop() { + // Do stuff with SPI + } -Using the SPI Class -^^^^^^^^^^^^^^^^^^^ +Turning the SPI Port On +----------------------- -You can declare that you want to use SPI in your sketch by putting -``HardwareSPI Spi(number);`` with your variables, where ``number`` is -1 or 2, depending on which SPI you want to use. Then you can use the -functions described in the next section. For example:: +Now it's time to turn your SPI port on. Do this with the ``begin()`` +function (an example is given below). - // Use SPI 1 - HardwareSpi Spi(1); +.. FIXME [0.0.10] Breathe doesn't include the class; fix & submit pull req + +.. doxygenfunction:: HardwareSPI::begin + +The speed at which the SPI port communicates is configured using a +``SPIFrequency`` value: + +.. FIXME [0.1.0] Breathe's enum output is enormous; shrink & submit pull req + +.. doxygenenum:: SPIFrequency + +.. note:: Due to hardware issues, you can't use the frequency + ``SPI_140_625KHz`` with SPI port 1. + +You'll need to determine the correct values for ``frequency``, +``bitOrder``, and ``mode`` yourself, by consulting the datasheet for +the device you're communicating with. Continuing our example from +before, we'll add a call to ``begin()`` to our ``setup()``:: + + // Use SPI port number 1 + HardwareSPI spi(1); void setup() { - Spi.begin(SPI_18MHZ); + // Turn on the SPI port + spi.begin(SPI_18MHZ, MSBFIRST, 0); } void loop() { - // Get the next byte from the peripheral - uint8 byte = Spi.recv(); + // Do stuff with SPI } -HardwareSPI Class Reference -^^^^^^^^^^^^^^^^^^^^^^^^^^^ +If you call ``begin()`` with no arguments (as in "``spi.begin();``"), +it's the same as if you wrote "``spi.begin(SPI_1_125MHZ, MSBFIRST, +0);``". -.. cpp:class:: HardwareSPI +Communicating Over SPI +---------------------- - Class for interacting with SPI. +Now that you've got your SPI port set up, it's time to start +communicating. You can send data using ``HardwareSPI::write()``, +receive data using ``HardwareSPI::read()``, and do both using +``HardwareSPI::transfer()``. -.. cpp:function:: HardwareSPI::HardwareSPI(uint32 spi_num) +.. cpp:function:: void HardwareSPI::write(byte data) - Construct an object for managing a SPI peripheral. ``spi_num`` - must be 1 or 2; see the :ref:`table above - <lang-hardwarespi-pinout>` for pinout information. + Send a single byte of data. -.. cpp:function:: void HardwareSPI::begin(SPIFrequency freq, uint32 endianness, uint32 mode) + **Parameters**: - Configure the baudrate of the given SPI port and set up the header - pins appropriately. + - ``data``: Byte to send - Parameters: +.. cpp:function:: byte HardwareSPI::read() - - ``freq``: one of the ``SPIFrequency`` values, given :ref:`below - <lang-hardwarespi-spifrequency>`. + Get the next available, unread byte. If there aren't any unread + bytes, this function will wait until one is received. - - ``endianness``: either ``LSBFIRST`` (little-endian) or - ``MSBFIRST`` (big-endian). +.. cpp:function:: byte HardwareSPI::transmit(byte data) - - ``mode``: one of 0, 1, 2, or 3, and specifies which SPI mode is - used. The mode number determines a combination of polarity and - phase according to the following table: + Send a byte, then return the next byte received. - .. list-table:: - :header-rows: 1 + **Parameters:** - * - Mode - - Polarity - - Phase + - ``data``: Byte to send - * - 0 - - 0 - - 0 + **Returns:** Next unread byte - * - 1 - - 0 - - 1 +Continuing our example from before, let's send a number over SPI and +print out whatever we get back over :ref:`lang-serialusb`:: - * - 2 - - 1 - - 0 + // Use SPI port number 1 + HardwareSPI spi(1); - * - 3 - - 1 - - 1 + void setup() { + // Turn on the SPI port + spi.begin(SPI_18MHZ, MSBFIRST, 0); + } - For more information on polarity and phase, see the - :ref:`external references, below <lang-hardwarespi-seealso>`. + void loop() { + // Send 245 over SPI, and wait for a response. + spi.write(245); + byte response = spi.read(); + // Print out the response received. + SerialUSB.print("response: "); + SerialUSB.println(response, DEC); + } + +HardwareSPI Class Reference +--------------------------- + +There are a number of other things you can accomplish with your +``spi`` object. A full function listing follows. -.. cpp:function:: void HardwareSPI::begin() +.. doxygenclass:: HardwareSPI + :members: HardwareSPI, begin, beginSlave, end, read, write, transfer - A convenience ``begin()``, equivalent to ``begin(SPI_1_125MHZ, - MSBFIRST, 0)``. +Deprecated Functions +-------------------- -.. cpp:function:: uint8 HardwareSpi::send(uint8 *data, uint32 length) +The following functions are defined for now, but they have been +deprecated, and will be removed in a future Maple IDE release. You +shouldn't use them in new programs, and you should change any of your +programs which do use them to the up-to-date functions discussed +above. + +.. cpp:function:: uint8 HardwareSPI::send(uint8 *data, uint32 length) Writes ``data`` into the port buffer to be transmitted as soon as possible, where ``length`` is the number of bytes to send from ``data``. Returns the last byte shifted back from slave. -.. cpp:function:: uint8 HardwareSpi::send(uint8 data) +.. cpp:function:: uint8 HardwareSPI::send(uint8 data) Writes the single byte ``data`` into the port buffer to be transmitted as soon as possible. Returns the data byte shifted back from the slave. -.. cpp:function:: uint8 HardwareSpi::recv() +.. cpp:function:: uint8 HardwareSPI::recv() Reads a byte from the peripheral. Returns the next byte in the buffer. - -SPI Speeds -^^^^^^^^^^ - -.. _lang-hardwarespi-spifrequency: - -The possible SPI speeds are configured using the ``SPIFrequency`` enum: - -.. doxygenenum:: SPIFrequency - -.. _lang-hardwarespi-seealso: - -See Also --------- - -* `Wikipedia Article on Serial Peripheral Interface Bus (SPI) - <http://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus>`_ -* `Arduino reference on SPI - <http://www.arduino.cc/playground/Code/Spi>`_ -* `Hardcore SPI on Arduino <http://klk64.com/arduino-spi/>`_ by kik64 -* STMicro documentation for STM32F103RB microcontroller: - - * `Datasheet <http://www.st.com/stonline/products/literature/ds/13587.pdf>`_ (pdf) - * `Reference Manual <http://www.st.com/stonline/products/literature/rm/13902.pdf>`_ (pdf) - - diff --git a/docs/source/lang/api/hardwaretimer.rst b/docs/source/lang/api/hardwaretimer.rst index c7a630d..09245f0 100644 --- a/docs/source/lang/api/hardwaretimer.rst +++ b/docs/source/lang/api/hardwaretimer.rst @@ -5,369 +5,341 @@ HardwareTimer ============= -This class defines the public API for interfacing with the STM32's -built-in timer peripherals. More information on these peripherals -(including code examples) is available in the :ref:`timers reference -<timers>`. +This page describes how to control the built-in timers. It does not +describe how the timers work on your board. For more information on +that, the :ref:`timers reference <timers>`. -Library Documentation ---------------------- +.. warning:: The timer interface is still taking shape, and is + expected to change significantly between releases. Because of + that, the functionality described in this page shouldn't be + considered stable. -HardwareTimer Class Reference -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + If you want a timer API that will be consistent between releases of + the Maple IDE, your best bet for now is to use the low-level + support in :ref:`libmaple-timer`. -To interact with a particular timer, call one of the methods -documented below on one of the predefined ``HardwareTimer`` instances. -For example, to set the prescale factor on timer 1 to 5, call -``Timer1.setPrescaleFactor(5)``. +.. contents:: Contents + :local: -.. TODO add code examples that people can copy and paste in case -.. they're unfamiliar with namespace syntax +.. _lang-hardwaretimer-getting-started: -.. cpp:class:: HardwareTimer +Getting Started +--------------- - Class for interacting with a timer. There are four predefined - instances available on the Maple: ``Timer1``, ``Timer2``, - ``Timer3``, and ``Timer4``. +You'll first need to define a ``HardwareTimer`` variable, which you'll +use to control the timer. Do this by putting the line +"``HardwareTimer timer(number);``" with your variables, where +``number`` is the timer's number. -.. _lang-hardwaretimer-attachinterrupt: +Here's an example (we'll fill in :ref:`setup() <lang-setup>` and +:ref:`loop() <lang-loop>` later):: -.. cpp:function:: void HardwareTimer::attachInterrupt(int channel, voidFuncPtr handler) + // Use timer 1 + HardwareTimer timer(1); - Attach an interrupt handler to the given ``channel``. This - interrupt handler will be called when the timer's counter reaches - the given channel :ref:`compare <lang-hardwaretimer-setcompare>` - value. + void setup() { + // Your setup code + } - ``handler`` should be a function which takes no arguments and has - :ref:`void <lang-void>` value; i.e. it should have signature :: + void loop() { + // ... + } - void handler(void); +Configuring the Prescaler and Overflow +-------------------------------------- - You can later detach the interrupt using :ref:`detachInterrupt() - <lang-hardwaretimer-detachinterrupt>`. +After defining your ``timer`` variable, you'll probably want to +configure how fast your timer's counter changes (using the prescaler) +and when it gets reset to zero (using the overflow value). You can do +that with the ``setPrescaleFactor()`` and ``setOverflow()`` functions. - .. note:: The function (often called an *interrupt service - routine*, or ISR) should attempt to return as quickly as - possible. :ref:`Blinking the LED <lang-toggleled>`, some - logic, :ref:`PWM <pwm>` updates, and :ref:`Serial - <lang-serial>` writes are fine; writing to - :ref:`SerialUSB <lang-serialusb>` or :ref:`waiting - <lang-waitforbuttonpress>` for user input can take a long - time and prevent other interrupts from firing on time. - - Tip: if you have a :ref:`delay() <lang-delay>` in your - ISR, you're probably doing it wrong. - -.. cpp:function:: void HardwareTimer::attachCompare1Interrupt(voidFuncPtr handler) - - Equivalent to :ref:`attachInterrupt - <lang-hardwaretimer-attachinterrupt>`\ ``(1, handler)``. - -.. cpp:function:: void HardwareTimer::attachCompare2Interrupt(voidFuncPtr handler) - - Equivalent to :ref:`attachInterrupt - <lang-hardwaretimer-attachinterrupt>`\ ``(2, handler)``. - -.. cpp:function:: void HardwareTimer::attachCompare3Interrupt(voidFuncPtr handler) +.. _lang-hardwaretimer-setprescalefactor: - Equivalent to :ref:`attachInterrupt - <lang-hardwaretimer-attachinterrupt>`\ ``(3, handler)``. +.. doxygenfunction:: HardwareTimer::setPrescaleFactor + :no-link: -.. cpp:function:: void HardwareTimer::attachCompare4Interrupt(voidFuncPtr handler) +.. _lang-hardwaretimer-setoverflow: - Equivalent to :ref:`attachInterrupt - <lang-hardwaretimer-attachinterrupt>`\ ``(4, handler)``. +.. doxygenfunction:: HardwareTimer::setOverflow + :no-link: -.. _lang-hardwaretimer-setchannelmode: +For example:: -.. cpp:function:: void HardwareTimer::setChannelMode(int channel, TimerMode mode) + // Use timer 1 + HardwareTimer timer(1); - Set the given channel of this timer to the given :ref:`mode - <lang-hardwaretimer-modes>`. The parameter ``channel`` is one of - 1, 2, 3, and 4, and corresponds to the compare channel you would - like to set. Refer to the full :ref:`pin mapping table - <pin-mapping-mega-table>` to match up timer channels and pin - numbers. + void setup() { + timer.setPrescaleFactor(5); + timer.setOverflow(255); + } -.. cpp:function:: void HardwareTimer::setChannel1Mode(TimerMode mode) + void loop() { + // ... + } - Equivalent to :ref:`setChannelMode <lang-hardwaretimer-setchannelmode>`\ - ``(1, mode)``. +You may also find the ``setPeriod()`` function useful: -.. cpp:function:: void HardwareTimer::setChannel2Mode(TimerMode mode) +.. _lang-hardwaretimer-setperiod: - Equivalent to :ref:`setChannelMode <lang-hardwaretimer-setchannelmode>`\ - ``(2, mode)``. +.. doxygenfunction:: HardwareTimer::setPeriod + :no-link: -.. cpp:function:: void HardwareTimer::setChannel3Mode(TimerMode mode) +For example:: - Equivalent to :ref:`setChannelMode <lang-hardwaretimer-setchannelmode>`\ - ``(3, mode)``. + // Use timer 1 + HardwareTimer timer(1); -.. cpp:function:: void HardwareTimer::setChannel4Mode(TimerMode mode) + void setup() { + // Have the timer repeat every 20 milliseconds + int microseconds_per_millisecond = 1000; + timer.setPeriod(20 * microseconds_per_millisecond); + } - Equivalent to :ref:`setChannelMode <lang-hardwaretimer-setchannelmode>`\ - ``(4, mode)``. + void loop() { + // ... + } + +.. _lang-hardwaretimer-interrupts: + +Using Timer Interrupts +---------------------- + +.. TODO [0.2.0] Improve the interrupts section, here or in timers.rst + +In order to use timer interrupts, we recommend the following sequence: + +* Pause the timer. +* Configure the prescaler and overflow. +* Pick a timer channel to handle the interrupt and set the channel's + :ref:`mode <lang-hardwaretimer-timermode>` to ``TIMER_OUTPUT_COMPARE``. +* Set the channel compare value appropriately (this controls what counter value, + from 0 to overflow - 1). If you just want to make the interrupt fire once + every time the timer overflows, and you don't care what the timer count is, + the channel compare value can just be 1. +* Attach an interrupt handler to the channel. +* Refresh the timer. +* Resume the timer. + +Here are two complete examples. + +**LED blink**: This example blinks the built-in LED without doing +anything in ``loop()``. :: + + #define LED_RATE 500000 // in microseconds; should give 0.5Hz toggles + + // We'll use timer 2 + HardwareTimer timer(2); + + void setup() { + // Set up the LED to blink + pinMode(BOARD_LED_PIN, OUTPUT); + + // Pause the timer while we're configuring it + timer.pause(); + + // Set up period + timer.setPeriod(LED_RATE); // in microseconds + + // Set up an interrupt on channel 1 + timer.setChannel1Mode(TIMER_OUTPUT_COMPARE); + timer.setCompare(TIMER_CH1, 1); // Interrupt 1 count after each update + timer.attachCompare1Interrupt(handler_led); + + // Refresh the timer's count, prescale, and overflow + timer.refresh(); + + // Start the timer counting + timer.resume(); + } + + void loop() { + // Nothing! It's all in the handler_led() interrupt: + } + + void handler_led(void) { + toggleLED(); + } + +**Racing Counters**: This example shows how to use multiple timers at +the same time. :: + + int count3 = 0; + int count4 = 0; + + // We'll use timers 3 and 4 + HardwareTimer timer3(3); + HardwareTimer timer4(4); + + void setup() { + // Set up the button for input + pinMode(BOARD_BUTTON_PIN, INPUT_PULLUP); + + // Set up timers to add 1 to their counts each time + // their interrupts fire. + timer3.setMode(TIMER_CH1, TIMER_OUTPUT_COMPARE); + timer4.setMode(TIMER_CH1, TIMER_OUTPUT_COMPARE); + timer3.pause(); + timer4.pause(); + timer3.setCount(0); + timer4.setCount(0); + timer3.setOverflow(30000); + timer4.setOverflow(30000); + timer3.setCompare(TIMER_CH1, 1000); // somewhere in the middle + timer4.setCompare(TIMER_CH1, 1000); + timer3.attachCompare1Interrupt(handler3); + timer4.attachCompare1Interrupt(handler4); + timer3.refresh(); + timer4.refresh(); + timer3.resume(); + timer4.resume(); + } + + void loop() { + // Display the running counts + SerialUSB.print("Count 3: "); + SerialUSB.print(count3); + SerialUSB.print("\t\tCount 4: "); + SerialUSB.println(count4); + + // While the button is held down, pause timer 4 + for (int i = 0; i < 1000; i++) { + if (digitalRead(BOARD_BUTTON_PIN)) { + timer4.pause(); + } else { + timer4.resume(); + } + delay(1); + } + } + + void handler3(void) { + count3++; + } + + void handler4(void) { + count4++; + } + +``HardwareTimer`` Class Reference +--------------------------------- + +This section gives a full listing of the capabilities of a +``HardwareTimer``. + +.. doxygenclass:: HardwareTimer + :members: HardwareTimer, pause, resume, getPrescaleFactor, setPrescaleFactor, getOverflow, setOverflow, getCount, setCount, setPeriod, setMode, getCompare, setCompare, attachInterrupt, detachInterrupt, refresh + +.. _lang-hardwaretimer-timermode: + +.. doxygenenum:: timer_mode + +Deprecated Functionality +------------------------ + +The following functionality exists for now, but it has been +deprecated, and will be removed in a future Maple IDE release. You +shouldn't use it in new programs, and you should change any of your +programs which do use them to use the up-to-date features described +above. + +The ``TimerMode`` type from previous releases has been renamed +``timer_mode``. The mode ``TIMER_OUTPUTCOMPARE`` is still present, +but will be removed in a future release. Use ``TIMER_OUTPUT_COMPARE`` +instead. -.. _lang-hardwaretimer-getcompare: +.. cpp:function:: void HardwareTimer::attachCompare1Interrupt(voidFuncPtr handler) -.. cpp:function:: uint16 HardwareTimer::getCompare(int channel) + Use ``attachInterrupt(1, handler)`` instead. - Gets the compare value for the given ``channel``, from 1 to 4. See - :ref:`setCompare() <lang-hardwaretimer-setcompare>`. +.. cpp:function:: void HardwareTimer::attachCompare2Interrupt(voidFuncPtr handler) -.. cpp:function:: uint16 HardwareTimer::getCompare1() + Use ``attachInterrupt(2, handler)`` instead. - Equivalent to :ref:`getCompare <lang-hardwaretimer-getcompare>`\ - ``(1, mode)``. +.. cpp:function:: void HardwareTimer::attachCompare3Interrupt(voidFuncPtr handler) -.. cpp:function:: uint16 HardwareTimer::getCompare2() + Use ``attachInterrupt(3, handler)`` instead. - Equivalent to :ref:`getCompare <lang-hardwaretimer-getcompare>`\ - ``(2, mode)``. +.. cpp:function:: void HardwareTimer::attachCompare4Interrupt(voidFuncPtr handler) -.. cpp:function:: uint16 HardwareTimer::getCompare3() + Use ``attachInterrupt(4, handler)`` instead. - Equivalent to :ref:`getCompare <lang-hardwaretimer-getcompare>`\ - ``(3, mode)``. +.. _lang-hardwaretimer-setchannelmode: -.. cpp:function:: uint16 HardwareTimer::getCompare4() +.. cpp:function:: void HardwareTimer::setChannelMode(int channel, timer_mode mode) - Equivalent to :ref:`getCompare <lang-hardwaretimer-getcompare>`\ - ``(4, mode)``. + Use ``setMode(channel, mode)`` instead. -.. _lang-hardwaretimer-setcompare: +.. cpp:function:: void HardwareTimer::setChannel1Mode(timer_mode mode) -.. cpp:function:: void HardwareTimer::setCompare(int channel, uint16 compare) + Use ``setMode(1, mode)`` instead. - Sets the compare value for the given ``channel`` to ``compare``. - If ``compare`` is greater than this timer's overflow value, it will - be truncated to the overflow value. The default compare value is - 65,535 (the largest unsigned 16-bit integer value). +.. cpp:function:: void HardwareTimer::setChannel2Mode(timer_mode mode) - When the counter reaches this value the interrupt for this channel - will fire if the given ``channel`` :ref:`mode - <lang-hardwaretimer-setchannelmode>` is ``TIMER_OUTPUTCOMPARE`` and - an interrupt is :ref:`attached - <lang-hardwaretimer-attachinterrupt>`. + Use ``setMode(2, mode)`` instead. - By default, this only changes the relative offsets between events - on a single timer ("phase"); they don't control the frequency with - which they occur. However, a common trick is to increment the - compare value manually in the interrupt handler so that the event - will fire again after the increment period. There can be a - different increment value for each channel, so this trick allows - events to be programmed at 4 different rates on a single - timer. Note that function call overheads mean that the smallest - increment rate is at least a few microseconds. +.. cpp:function:: void HardwareTimer::setChannel3Mode(timer_mode mode) -.. cpp:function:: void HardwareTimer::setCompare1(uint16 compare) + Use ``setMode(3, mode)`` instead. - Equivalent to :ref:`setCompare <lang-hardwaretimer-setcompare>`\ - ``(1, compare)``. +.. cpp:function:: void HardwareTimer::setChannel4Mode(timer_mode mode) -.. cpp:function:: void HardwareTimer::setCompare2(uint16 compare) + Use ``setMode(4, mode)`` instead. - Equivalent to :ref:`setCompare <lang-hardwaretimer-setcompare>`\ - ``(2, compare)``. +.. cpp:function:: uint16 HardwareTimer::getCompare1() -.. cpp:function:: void HardwareTimer::setCompare3(uint16 compare) + Use ``getCompare(1, mode)`` instead. - Equivalent to :ref:`setCompare <lang-hardwaretimer-setcompare>`\ - ``(3, compare)``. +.. cpp:function:: uint16 HardwareTimer::getCompare2() -.. cpp:function:: void HardwareTimer::setCompare4(uint16 compare) + Use ``getCompare(2, mode)`` instead. - Equivalent to :ref:`setCompare <lang-hardwaretimer-setcompare>`\ - ``(4, compare)``. +.. cpp:function:: uint16 HardwareTimer::getCompare3() -.. cpp:function:: uint16 HardwareTimer::getCount() + Use ``getCompare(3, mode)`` instead. - Gets the current timer count. Due to function call overhead, the - return value will be increasingly accurate with smaller prescale - values. Also see :ref:`setCount() <lang-hardwaretimer-setcount>`. +.. cpp:function:: uint16 HardwareTimer::getCompare4() -.. _lang-hardwaretimer-setcount: + Use ``getCompare(4, mode)`` instead. -.. cpp:function:: void HardwareTimer::setCount(uint16 val) +.. cpp:function:: void HardwareTimer::setCompare1(uint16 compare) - Set the timer's current count to ``val``. + Use ``setCompare(1, compare)`` instead. - Note that there is some function call overhead associated with - calling this method, so using it is not a robust way to get - multiple timers to share a count value. +.. cpp:function:: void HardwareTimer::setCompare2(uint16 compare) - If ``val`` exceeds the timer's :ref:`overflow value - <lang-hardwaretimer-getoverflow>`, it is truncated to the overflow - value. + Use ``setCompare(2, compare)`` instead. +.. cpp:function:: void HardwareTimer::setCompare3(uint16 compare) -.. _lang-hardwaretimer-detachinterrupt: + Use ``setCompare(3, compare)`` instead. -.. cpp:function:: void HardwareTimer::detachInterrupt(int channel) +.. cpp:function:: void HardwareTimer::setCompare4(uint16 compare) - Remove the interrupt handler attached to the given ``channel``, if - any. The handler will no longer be called by this timer. + Use ``setCompare(4, compare)`` instead. .. cpp:function:: void HardwareTimer::detachCompare1Interrupt() - Equivalent to :ref:`detachInterrupt - <lang-hardwaretimer-detachinterrupt>`\ ``(1)``. + Use ``detachInterrupt(1)`` instead. .. cpp:function:: void HardwareTimer::detachCompare2Interrupt() - Equivalent to :ref:`detachInterrupt - <lang-hardwaretimer-detachinterrupt>`\ ``(2)``. + Use ``detachInterrupt(2)`` instead. .. cpp:function:: void HardwareTimer::detachCompare3Interrupt() - Equivalent to :ref:`detachInterrupt - <lang-hardwaretimer-detachinterrupt>`\ ``(3)``. + Use ``detachInterrupt(3)`` instead. .. cpp:function:: void HardwareTimer::detachCompare4Interrupt() - Equivalent to :ref:`detachInterrupt - <lang-hardwaretimer-detachinterrupt>`\ ``(4)``. - -.. _lang-hardwaretimer-generateupdate: + Use ``detachInterrupt(4)`` instead. .. cpp:function:: void HardwareTimer::generateUpdate() - Re-initializes the counter (to 0 in upcounting mode, which is the - default), and generates an update of the prescale and overflow - registers. - -.. _lang-hardwaretimer-getoverflow: - -.. cpp:function:: uint16 HardwareTimer::getOverflow() - - Gets the timer's overflow value. See :ref:`setOverflow() - <lang-hardwaretimer-setoverflow>`. - -.. _lang-hardwaretimer-setoverflow: - -.. cpp:function:: void HardwareTimer::setOverflow(uint16 val) - - Sets the timer overflow (or "reload") value to ``val``. - - When the timer's counter reaches this, value it resets to - zero. Its default value is 65535 (the largest unsigned 16-bit - integer); setting the overflow to anything lower will cause - interrupts to be called more frequently (see :ref:`setPeriod() - <lang-hardwaretimer-setperiod>` function for a shortcut). - - After the next :ref:`timer update - <lang-hardwaretimer-generateupdate>`, this number will be the - maximum value for the timer's channel compare values. - -.. _lang-hardwaretimer-pause: - -.. cpp:function:: void HardwareTimer::pause() - - Stop the timer's counter, without affecting its configuration. - - The timer will no longer count or fire interrupts after this - function is called, until it is resumed. This function is useful - during timer setup periods, in order to prevent interrupts from - firing before the timer is fully configured. - - Note that there is some function call overhead associated with this - method, so using it in concert with :ref:`resume() - <lang-hardwaretimer-resume>` is not a robust way to align multiple - timers to the same count value. - -.. _lang-hardwaretimer-setperiod: - -.. cpp:function:: uint16 HardwareTimer::setPeriod(uint32 microseconds) - - Configure the :ref:`prescaler - <lang-hardwaretimer-getprescalefactor>` and :ref:`overflow - <lang-hardwaretimer-getoverflow>` values to generate a timer reload - with a period as close to the given number of ``microseconds`` as - possible. - - The return value is the new overflow value, which may be used to - set channel compare values. However, if a clock that fires an - interrupt every given number of microseconds is all that is - desired, and the relative "phases" are unimportant, channel compare - values may all be set to 1. - -.. _lang-hardwaretimer-getprescalefactor: - -.. cpp:function:: uint16 HardwareTimer::getPrescaleFactor() - - Returns the timer's prescale factor. See - :ref:`setPrescaleFactor() <lang-hardwaretimer-setprescalefactor>`. - -.. _lang-hardwaretimer-setprescalefactor: - -.. cpp:function:: void HardwareTimer::setPrescaleFactor(uint16 factor) - - Set the timer's prescale factor to ``factor``. - - The prescaler acts as a clock divider to slow down the rate at - which the counter increments. - - For example, the system clock rate is 72MHz, so the counter will - reach 65535 in (13.89 nanoseconds) × (65535 counts) = (910.22 - microseconds), or about a thousand times a second. If the - prescaler equals 1098, then the clock rate is effectively 72MHz / - 1098 = 65.56KHz, and the counter will reach 65536 in (15.25 - microseconds) × (65536 counts) = (0.999 seconds), or about once - per second. - - The :ref:`setPeriod() <lang-hardwaretimer-setperiod>` method may - also be used as a convenient alternative. - -.. _lang-hardwaretimer-resume: - -.. cpp:function:: void HardwareTimer::resume() - - Resume a paused timer, without affecting its configuration. - - The timer will resume counting and firing interrupts as - appropriate. - - Note that there is some function call overhead associated with - using this method, so using it in concert with :ref:`pause() - <lang-hardwaretimer-pause>` is not a robust way to align multiple - timers to the same count value. - -.. cpp:function:: timer_dev_num HardwareTimer::getTimerNum() - - Returns the :ref:`timer device number - <lang-hardwaretimer-timer-dev-num>` associated with the timer. For - example, ``Timer1.getTimerNum()`` would return ``TIMER1``. - - In most cases, you should not need to use this function. If you do - use it, be careful; the constant ``TIMER1`` is *not equal* to the - number 1; similarly, ``TIMER2`` is *not* the number 2, etc. Be - sure to refer to the timer device number by name. - -.. _lang-hardwaretimer-modes: - -Timer Modes -^^^^^^^^^^^ -.. doxygenenum:: TimerMode - -.. _lang-hardwaretimer-timer-dev-num: - -Timer Device Numbers -^^^^^^^^^^^^^^^^^^^^ - -These provide a lower-level interface for interacting with timers. -They are mostly useful in context with the :ref:`getTimer() -<lang-hardwaretimer-gettimer>` function. **Be careful** when using -these not to confuse e.g. ``TIMER1`` with the number 1; they are -different. - -.. doxygenenum:: timer_dev_num - -.. _lang-hardwaretimer-convenience: - -.. _lang-hardwaretimer-gettimer: + Use ``refresh()`` instead. -Other Functions -^^^^^^^^^^^^^^^ -.. doxygenfunction:: getTimer +In previous releases, to interact with a particular timers, you would +use one of the predefined ``HardwareTimer`` instances ``Timer1``, +``Timer2``, ``Timer3``, and ``Timer4``. These are still available for +now, but they are also deprecated, and will be removed in a future +release. As detailed in :ref:`lang-hardwaretimer-getting-started`, +you should define your own ``HardwareTimer`` variables. diff --git a/docs/source/lang/api/highbyte.rst b/docs/source/lang/api/highbyte.rst index 50a1fa6..4cb6f9b 100644 --- a/docs/source/lang/api/highbyte.rst +++ b/docs/source/lang/api/highbyte.rst @@ -52,8 +52,4 @@ See Also - :ref:`lowByte() <lang-lowbyte>` - - - - -.. include:: cc-attribution.txt +.. include:: /arduino-cc-attribution.txt diff --git a/docs/source/lang/api/isbuttonpressed.rst b/docs/source/lang/api/isbuttonpressed.rst index dbff0c9..8c350b9 100644 --- a/docs/source/lang/api/isbuttonpressed.rst +++ b/docs/source/lang/api/isbuttonpressed.rst @@ -4,7 +4,8 @@ isButtonPressed() ================= Check whether the board's built-in button (labeled BUT on the -silkscreen) is pressed. +silkscreen) is pressed. The pin number of the built-in button is +given by the constant ``BOARD_BUTTON_PIN``. Library Documentation --------------------- @@ -14,4 +15,6 @@ Library Documentation See Also -------- +- :ref:`Board-specific values <lang-board-values>` +- :ref:`BOARD_BUTTON_PIN <lang-board-values-but>` - :ref:`lang-waitforbuttonpress` diff --git a/docs/source/lang/api/loop.rst b/docs/source/lang/api/loop.rst index d8f6183..c2a5097 100644 --- a/docs/source/lang/api/loop.rst +++ b/docs/source/lang/api/loop.rst @@ -15,7 +15,6 @@ Example :: - int buttonPin = 38; // setup initializes serial and the button pin @@ -42,4 +41,4 @@ See Also - :ref:`setup() <lang-setup>` -.. include:: cc-attribution.txt +.. include:: /arduino-cc-attribution.txt diff --git a/docs/source/lang/api/lowbyte.rst b/docs/source/lang/api/lowbyte.rst index 58e622f..c513711 100644 --- a/docs/source/lang/api/lowbyte.rst +++ b/docs/source/lang/api/lowbyte.rst @@ -22,4 +22,4 @@ Returns The low byte's value (this will be between 0 and 255). -.. include:: cc-attribution.txt +.. include:: /arduino-cc-attribution.txt diff --git a/docs/source/lang/api/map.rst b/docs/source/lang/api/map.rst index 79122b3..69661a0 100644 --- a/docs/source/lang/api/map.rst +++ b/docs/source/lang/api/map.rst @@ -65,4 +65,4 @@ See Also - :ref:`constrain() <lang-constrain>` -.. include:: cc-attribution.txt +.. include:: /arduino-cc-attribution.txt diff --git a/docs/source/lang/api/max.rst b/docs/source/lang/api/max.rst index d38eebe..d356f08 100644 --- a/docs/source/lang/api/max.rst +++ b/docs/source/lang/api/max.rst @@ -53,7 +53,7 @@ functions inside the parentheses. It may lead to incorrect results:: Arduino Compatibility --------------------- -The Maple version of ``max()`` is compatible with Arduino. +The Maple implementation of ``max()`` is compatible with Arduino. See Also -------- @@ -61,5 +61,4 @@ See Also - :ref:`min() <lang-min>` - :ref:`constrain() <lang-constrain>` - -.. include:: cc-attribution.txt +.. include:: /arduino-cc-attribution.txt diff --git a/docs/source/lang/api/micros.rst b/docs/source/lang/api/micros.rst index f12976b..de85303 100644 --- a/docs/source/lang/api/micros.rst +++ b/docs/source/lang/api/micros.rst @@ -43,4 +43,4 @@ See Also - :ref:`delay() <lang-delay>` - :ref:`delayMicroseconds() <lang-delaymicroseconds>` -.. include:: cc-attribution.txt +.. include:: /arduino-cc-attribution.txt diff --git a/docs/source/lang/api/millis.rst b/docs/source/lang/api/millis.rst index 0288c56..db0531c 100644 --- a/docs/source/lang/api/millis.rst +++ b/docs/source/lang/api/millis.rst @@ -49,4 +49,4 @@ See Also - :ref:`delay <lang-delay>` - :ref:`delayMicroseconds <lang-delaymicroseconds>` -.. include:: cc-attribution.txt +.. include:: /arduino-cc-attribution.txt diff --git a/docs/source/lang/api/min.rst b/docs/source/lang/api/min.rst index 1245f6f..3307105 100644 --- a/docs/source/lang/api/min.rst +++ b/docs/source/lang/api/min.rst @@ -62,5 +62,4 @@ See Also - :ref:`max() <lang-max>` - :ref:`constrain() <lang-constrain>` - -.. include:: cc-attribution.txt +.. include:: /arduino-cc-attribution.txt diff --git a/docs/source/lang/api/pinmode.rst b/docs/source/lang/api/pinmode.rst index 03cbcfa..8cee3e5 100644 --- a/docs/source/lang/api/pinmode.rst +++ b/docs/source/lang/api/pinmode.rst @@ -60,13 +60,13 @@ set up a pin for these purposes before a call to, e.g., :ref:`lang-analogRead`. In practice, this should only add a few lines to your :ref:`lang-setup` function. -.. TODO verify following before putting it in: +.. TODO [0.1.0] verify following before putting it in: .. ``OUTPUT_OPEN_DRAIN``, ``INPUT_PULLUP``, ``INPUT_PULLDOWN``, and .. ``PWM_OPEN_DRAIN`` modes represent functionality not currently .. available on Arduino boards. -See also +See Also -------- - :ref:`lang-constants` @@ -74,6 +74,4 @@ See also - :ref:`lang-digitalread` - Maple :ref:`GPIO <gpio>` reference page - - -.. include:: cc-attribution.txt +.. include:: /arduino-cc-attribution.txt diff --git a/docs/source/lang/api/pow.rst b/docs/source/lang/api/pow.rst index 4280400..219a866 100644 --- a/docs/source/lang/api/pow.rst +++ b/docs/source/lang/api/pow.rst @@ -10,8 +10,6 @@ Library Documentation .. doxygenfunction:: pow -.. TODO LATER some examples - See Also -------- @@ -19,5 +17,4 @@ See Also - :ref:`float <lang-float>` - :ref:`double <lang-double>` - -.. include:: cc-attribution.txt +.. include:: /arduino-cc-attribution.txt diff --git a/docs/source/lang/api/pwmwrite.rst b/docs/source/lang/api/pwmwrite.rst index 9d50077..5cc112e 100644 --- a/docs/source/lang/api/pwmwrite.rst +++ b/docs/source/lang/api/pwmwrite.rst @@ -11,8 +11,13 @@ pwmWrite(), the pin will output a steady square wave with the given duty cycle. You can change the duty cycle later by calling pwmWrite() again with the same pin and a different duty. -On the Maple, the pins which support PWM are: 0, 1, 2, 3, 5, 6, 7, 8, -9, 11, 12, 14, 24, 27, and 28. +The pins which support PWM have ``PWM`` listed underneath their number +on your board's silkscreen. These pin numbers are available to your +program in the :ref:`boardPWMPins <lang-board-values-pwm-pins>` +board-specific array. The number of pins which are capable of PWM on +your board is given by the ``BOARD_NR_PWM_PINS`` constant. These +values are documented for each board in the :ref:`Board Hardware +Documentation <index-boards>` pages. The Arduino function :ref:`analogWrite() <lang-analogwrite>` is an alias for ``pwmWrite()``, but it is badly named, and its use is @@ -52,4 +57,5 @@ potentiometer:: See Also -------- -- :ref:`Maple PWM tutorial <pwm>` +- :ref:`Maple PWM tutorial <pwm>` +- :ref:`boardPWMPins <lang-board-values-pwm-pins>` diff --git a/docs/source/lang/api/random.rst b/docs/source/lang/api/random.rst index dd8871d..9875ee6 100644 --- a/docs/source/lang/api/random.rst +++ b/docs/source/lang/api/random.rst @@ -10,9 +10,7 @@ The ``random()`` function generates pseudo-random numbers. Library Documentation --------------------- -.. FIXME keep tracking Sphinx/Breathe's ability to reference -.. overloaded functions so we can use doxygenfunction instead of -.. manually documenting. +.. FIXME [Breathe] use doxygenfunction when possible .. cpp:function:: random(long max) @@ -70,4 +68,4 @@ See Also - :ref:`randomSeed() <lang-randomseed>` -.. include:: cc-attribution.txt +.. include:: /arduino-cc-attribution.txt diff --git a/docs/source/lang/api/randomseed.rst b/docs/source/lang/api/randomseed.rst index d0a15b7..ca7b75f 100644 --- a/docs/source/lang/api/randomseed.rst +++ b/docs/source/lang/api/randomseed.rst @@ -57,4 +57,4 @@ See Also - :ref:`random() <lang-random>` -.. include:: cc-attribution.txt +.. include:: /arduino-cc-attribution.txt diff --git a/docs/source/lang/api/serial.rst b/docs/source/lang/api/serial.rst index 58002e3..0821f43 100644 --- a/docs/source/lang/api/serial.rst +++ b/docs/source/lang/api/serial.rst @@ -12,46 +12,20 @@ devices. Introduction ------------ -The Maple has three serial ports (also known as a UARTs or USARTs): -``Serial1``, ``Serial2``, and ``Serial3``. They communicate using the -pins summarized in the following table: +.. FIXME [0.0.10] UART4, UART5 -.. list-table:: - :header-rows: 1 - - * - Serial port - - TX, RX, CK - - CTS, RTS (if present) - - * - ``Serial1`` - - 7, 8, 6 - - - - * - ``Serial2`` - - 1, 0, 10 - - 2, 3 - - * - ``Serial3`` - - 29, 30, 31 - - 32, 33 - -Thus, if you use a particular serial port, you cannot also use its -communication pins for other purposes at the same time. - -If you want to communicate with the Maple using the provided USB port, -use :ref:`SerialUSB <lang-serialusb>` instead. - -To use them to communicate with an external TTL serial device, connect -the TX pin to your device's RX pin, the RX to your device's TX pin, -and the ground of your Maple to your device's ground. +To use a serial port to communicate with an external serial device, +connect the TX pin to your device's RX pin, the RX to your device's TX +pin, and your Maple board's ground to your device's ground. .. warning:: Don't connect these pins directly to an RS232 serial port; they operate at +/- 12V and can damage your board. - Library Documentation --------------------- +.. FIXME [0.1.0] Tutorial-style usage introduction + All of the ``Serial[1,2,3]`` objects are instances of the ``HardwareSerial`` class, which is documented in this section. (This means that you can use any of these functions on any of ``Serial1``, @@ -87,7 +61,7 @@ means that you can use any of these functions on any of ``Serial1``, .. cpp:function:: HardwareSerial::flush() - Removes the contents of the Serial's associated USART RX FIFO. + Throw away the contents of the serial port's receiver (RX) buffer. That is, clears any buffered characters, so that the next character read is guaranteed to be new. @@ -113,34 +87,25 @@ means that you can use any of these functions on any of ``Serial1``, Print the argument's digits over the USART, in decimal format. -.. cpp:function:: HardwareSerial::print(long long n) +.. cpp:function:: HardwareSerial::print(long n) Print the argument's digits over the USART, in decimal format. Negative values will be prefixed with a ``'-'`` character. -.. cpp:function:: HardwareSerial::print(unsigned long long n) +.. cpp:function:: HardwareSerial::print(unsigned long n) Print the argument's digits over the USART, in decimal format. -.. _lang-serial-print-n-base: - -.. cpp:function:: HardwareSerial::print(int n, int base) - - Print the digits of ``n`` over the USART, in base ``base``. The - ``base`` value 2 corresponds to binary, 8 to octal, 10 to decimal, - and 16 to hexadecimal (you can also use the symbolic constants - ``BIN``, ``OCT``, ``DEC``, ``HEX``). If ``base`` is 10, negative - values will be prefixed with a ``'-'`` character (otherwise, ``n`` - will be interpreted as an unsigned quantity). - -.. cpp:function:: HardwareSerial::print(long long n, int base) +.. cpp:function:: HardwareSerial::print(long n, int base) - Same behavior as the above :ref:`print(int n, int base) - <lang-serial-print-n-base>`, except with 64-bit values. + Print the digits of ``n`` over the USART, in base ``base`` (which + may be between 2 and 16). The ``base`` value 2 corresponds to + binary, 8 to octal, 10 to decimal, and 16 to hexadecimal. Negative + values will be prefixed with a ``'-'`` character. .. cpp:function:: HardwareSerial::print(double n) - Print ``n``, accurate to 6 digits after the decimal point. + Print ``n``, accurate to 2 digits after the decimal point. .. _lang-serial-println: @@ -164,19 +129,15 @@ means that you can use any of these functions on any of ``Serial1``, Like ``print(n)``, followed by ``"\r\n"``. -.. cpp:function:: HardwareSerial::println(long long n) +.. cpp:function:: HardwareSerial::println(long n) Like ``print(n)``, followed by ``"\r\n"``. -.. cpp:function:: HardwareSerial::println(unsigned long long n) +.. cpp:function:: HardwareSerial::println(unsigned long n) Like ``print(n)``, followed by ``"\r\n"``. -.. cpp:function:: HardwareSerial::println(int n, int base) - - Like ``print(n, b)``, followed by ``"\r\n"``. - -.. cpp:function:: HardwareSerial::println(long long n, int base) +.. cpp:function:: HardwareSerial::println(long n, int base) Like ``print(n, b)``, followed by ``"\r\n"``. @@ -219,11 +180,12 @@ Arduino Compatibility Note -------------------------- Unlike the Arduino, none of the Maple's serial ports is connected to -the USB port on the Maple board (for that, use :ref:`SerialUSB -<lang-serialusb>`). Thus, to use these pins to communicate with your -personal computer, you will need an additional USB-to-serial adaptor. +the USB port on the Maple board. If you want to communicate using the +built-in USB port, use :ref:`SerialUSB <lang-serialusb>` instead. You +will need an additional USB-to-serial adapter to communicate between a +USART and your computer. -.. TODO LATER port these examples over +.. FIXME [0.1.0] port these examples over .. Examples .. -------- @@ -236,4 +198,4 @@ personal computer, you will need an additional USB-to-serial adaptor. .. - `Serial Call Response <http://arduino.cc/en/Tutorial/SerialCallResponse>`_ .. - `Serial Call Response ASCII <http://arduino.cc/en/Tutorial/SerialCallResponseASCII>`_ -.. include:: cc-attribution.txt +.. include:: /arduino-cc-attribution.txt diff --git a/docs/source/lang/api/serialusb.rst b/docs/source/lang/api/serialusb.rst index 87fa641..ed466f2 100644 --- a/docs/source/lang/api/serialusb.rst +++ b/docs/source/lang/api/serialusb.rst @@ -16,9 +16,8 @@ Introduction In addition to three :ref:`serial ports <lang-serial>`, the Maple's STM32 microprocessor includes a dedicated USB peripheral. This peripheral is used 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). +terminal. The emulated terminal is relatively slow; it is best for +transferring data at regular serial speeds (kilobaud). Library access to the emulated serial port is provided through the ``SerialUSB`` object. You can mostly use ``SerialUSB`` as a drop-in @@ -30,14 +29,14 @@ replacement for ``Serial1``, ``Serial2``, and ``Serial3``. This means that if you have a number of calls to one of the ``SerialUSB`` ``write()`` or ``print()`` functions in your code, - and you are not monitoring the emulated on a computer, your program - will run much, much slower than if it is being monitored or totally - disconnected (run off of a battery). + and you are not monitoring ``SerialUSB`` on a computer, your + program will run much slower than if it is being monitored or + totally disconnected (run off of a battery). You can avoid this behavior by :ref:`deciphering the port status - using the DTR and RTS line status <lang-serialusb-safe-print>`; the + using the DTR and RTS line status <lang-serialusb-safe-print>` (the behavior of these control lines is platform dependent and we no - longer interpret them by default. + longer interpret them by default). Library Documentation --------------------- @@ -109,35 +108,26 @@ world!")``. Print the argument's digits over the USB connection, in decimal format. -.. cpp:function:: USBSerial::print(long long n) +.. cpp:function:: USBSerial::print(long n) Print the argument's digits over the USB connection, in decimal format. Negative values will be prefixed with a ``'-'`` character. -.. cpp:function:: USBSerial::print(unsigned long long n) +.. cpp:function:: USBSerial::print(unsigned long n) Print the argument's digits over the USB connection, in decimal format. -.. _lang-serial-print-n-base: +.. cpp:function:: USBSerial::print(long n, int base) -.. cpp:function:: USBSerial::print(int n, int base) - - Print the digits of ``n`` over USB, in base ``base``. The ``base`` - value 2 corresponds to binary, 8 to octal, 10 to decimal, and 16 to - hexadecimal (you can also use the symbolic constants ``BIN``, - ``OCT``, ``DEC``, ``HEX``). If ``base`` is 10, negative values - will be prefixed with a ``'-'`` character (otherwise, ``n`` will be - interpreted as an unsigned quantity). - -.. cpp:function:: HardwareSerial::print(long long n, int base) - - Same behavior as the above :ref:`print(int n, int base) - <lang-serialusb-print-n-base>`, except with 64-bit values. + Print the digits of ``n`` over the USB connection, in base ``base`` + (which may be between 2 and 16). The ``base`` value 2 corresponds + to binary, 8 to octal, 10 to decimal, and 16 to hexadecimal. + Negative values will be prefixed with a ``'-'`` character. .. cpp:function:: USBSerial::print(double n) - Print ``n``, accurate to 6 digits after the decimal point. + Print ``n``, accurate to 2 digits after the decimal point. .. _lang-serialusb-println: @@ -161,19 +151,15 @@ world!")``. Like ``print(n)``, followed by ``"\r\n"``. -.. cpp:function:: USBSerial::println(long long n) +.. cpp:function:: USBSerial::println(long n) Like ``print(n)``, followed by ``"\r\n"``. -.. cpp:function:: USBSerial::println(unsigned long long n) +.. cpp:function:: USBSerial::println(unsigned long n) Like ``print(n)``, followed by ``"\r\n"``. -.. cpp:function:: USBSerial::println(int n, int base) - - Like ``print(n, b)``, followed by ``"\r\n"``. - -.. cpp:function:: USBSerial::println(long long n, int base) +.. cpp:function:: USBSerial::println(long n, int base) Like ``print(n, b)``, followed by ``"\r\n"``. @@ -224,7 +210,7 @@ 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 + #define LED_PIN BOARD_LED_PIN void setup() { /* Set up the LED to blink */ @@ -232,22 +218,22 @@ configuration. :: } void loop() { - // LED will stay off if we are disconnected; - // will blink quickly if USB is unplugged (battery etc) + // LED will stay off if we are disconnected, and will blink + // quickly if USB is unplugged (battery power, 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 this logic fails to detect if bytes are going to be read + // by the USB host, then the println() take a long time, + // 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 will be fast 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); + for(int i = 0; i < 10; i++) { + SerialUSB.println(123456, BIN); } } digitalWrite(LED_PIN, 0); diff --git a/docs/source/lang/api/setup.rst b/docs/source/lang/api/setup.rst index 837ddd6..1e8e3b8 100644 --- a/docs/source/lang/api/setup.rst +++ b/docs/source/lang/api/setup.rst @@ -26,4 +26,4 @@ Example // ... } -.. include:: cc-attribution.txt +.. include:: /arduino-cc-attribution.txt diff --git a/docs/source/lang/api/shiftout.rst b/docs/source/lang/api/shiftout.rst new file mode 100644 index 0000000..1d9ba12 --- /dev/null +++ b/docs/source/lang/api/shiftout.rst @@ -0,0 +1,99 @@ +.. highlight:: cpp + +.. _lang-shiftout: + +shiftOut() +========== + +Shift out a byte of data, one bit at a time. + +.. contents:: Contents + :local: + +Library Documentation +--------------------- + +.. doxygenfunction:: shiftOut + +Discussion +---------- + +This is a software implementation. There is also a hardware :ref:`SPI +<spi>` library available which will be faster and consume less CPU +cycles than this function. + +Note that the ``dataPin`` and ``clockPin`` must already be configured +to :ref:`OUTPUT <lang-constants-output>` mode by a call to +:ref:`pinMode() <lang-pinmode>`. + +Also note that since shiftOut() outputs 1 byte (8 bits) at a time, it +requires multiple steps to output values larger than 255. + +Examples +-------- + +To use these examples, replace ``dataPin`` and ``clockPin`` with the +numbers of the pins you want to use:: + + /* MSBFIRST example */ + + uint16 data = 500; + // shift out high byte + shiftOut(dataPin, clockPin, MSBFIRST, (data >> 8)); + // shift out low byte + shiftOut(dataPin, clockPin, MSBFIRST, data); + + /* LSBFIRST serial */ + + data = 500; + // shift out low byte + shiftOut(dataPin, clockPin, LSBFIRST, data); + // shift out high byte + shiftOut(dataPin, clockPin, LSBFIRST, (data >> 8)); + +Arduino Tutorial Example +------------------------ + +This Arduino example runs unmodified on the Maple. For accompanying +circuit, see the `tutorial on controlling a 74HC595 shift register +<http://arduino.cc/en/Tutorial/ShiftOut>`_. + +:: + + //**************************************************************// + // Name : shiftOutCode, Hello World // + // Author : Carlyn Maw, Tom Igoe // + // Date : 25 Oct, 2006 // + // Version : 1.0 // + // Notes : Code for using a 74HC595 Shift Register // + // : to count from 0 to 255 // + //**************************************************************// + + // Pin connected to ST_CP of 74HC595 + int latchPin = 8; + // Pin connected to SH_CP of 74HC595 + int clockPin = 12; + // Pin connected to DS of 74HC595 + int dataPin = 11; + + void setup() { + // Set pins to output because they are addressed in the main loop + pinMode(latchPin, OUTPUT); + pinMode(clockPin, OUTPUT); + pinMode(dataPin, OUTPUT); + } + + void loop() { + // Count up routine + for (int j = 0; j < 256; j++) { + // Ground latchPin and hold low for as long as you are transmitting + digitalWrite(latchPin, LOW); + shiftOut(dataPin, clockPin, LSBFIRST, j); + // Return the latch pin high to signal chip that it + // no longer needs to listen for information + digitalWrite(latchPin, HIGH); + delay(1000); + } + } + +.. include:: /lang/cc-attribution.txt diff --git a/docs/source/lang/api/sin.rst b/docs/source/lang/api/sin.rst index 398b8f3..3e28c0b 100644 --- a/docs/source/lang/api/sin.rst +++ b/docs/source/lang/api/sin.rst @@ -28,5 +28,4 @@ See Also - :ref:`float <lang-float>` - :ref:`double <lang-double>` - -.. include:: cc-attribution.txt +.. include:: /arduino-cc-attribution.txt diff --git a/docs/source/lang/api/sq.rst b/docs/source/lang/api/sq.rst index bd32648..96724d3 100644 --- a/docs/source/lang/api/sq.rst +++ b/docs/source/lang/api/sq.rst @@ -42,5 +42,4 @@ Arduino Compatibility Maple's implementation of ``sq()`` is compatible with Arduino. - -.. include:: cc-attribution.txt +.. include:: /arduino-cc-attribution.txt diff --git a/docs/source/lang/api/tan.rst b/docs/source/lang/api/tan.rst index 4bbe0db..b1aed31 100644 --- a/docs/source/lang/api/tan.rst +++ b/docs/source/lang/api/tan.rst @@ -22,10 +22,9 @@ Note that the Maple implementation comes from `newlib See Also -------- - - :ref:`sin <lang-sin>` - :ref:`cos <lang-cos>` - :ref:`float <lang-float>` - :ref:`double <lang-double>` -.. include:: cc-attribution.txt +.. include:: /arduino-cc-attribution.txt diff --git a/docs/source/lang/api/toggleled.rst b/docs/source/lang/api/toggleled.rst index 0cc20c2..54a7d64 100644 --- a/docs/source/lang/api/toggleled.rst +++ b/docs/source/lang/api/toggleled.rst @@ -13,5 +13,5 @@ Library Documentation See Also -------- -- :ref:`BOARD_LED_PIN <lang-constants-led>` +- :ref:`BOARD_LED_PIN <lang-board-values-led>` - :ref:`togglePin() <lang-togglepin>` diff --git a/docs/source/lang/api/volatile.rst b/docs/source/lang/api/volatile.rst index 276bb6a..1b72897 100644 --- a/docs/source/lang/api/volatile.rst +++ b/docs/source/lang/api/volatile.rst @@ -24,8 +24,8 @@ for efficiency). A variable should be declared ``volatile`` whenever its value can be changed by something beyond the control of the code section in which it appears, such as an :ref:`external interrupt -<external-interrupts>`. On the Maple, the only place that this is -likely to occur is in sections of code associated with interrupts. +<external-interrupts>`. (The only place that this is likely to occur +in most programs is inside of code called by interrupts). Example ------- @@ -55,11 +55,11 @@ Example } } -See also +See Also -------- - :ref:`External Interrupts <external-interrupts>` - :ref:`lang-attachinterrupt` - :ref:`lang-detachinterrupt` -.. include:: cc-attribution.txt +.. include:: /arduino-cc-attribution.txt diff --git a/docs/source/lang/api/waitforbuttonpress.rst b/docs/source/lang/api/waitforbuttonpress.rst index 34c5066..a5bd45c 100644 --- a/docs/source/lang/api/waitforbuttonpress.rst +++ b/docs/source/lang/api/waitforbuttonpress.rst @@ -14,4 +14,6 @@ Library Documentation See Also -------- +- :ref:`Board-specific values <lang-board-values>` +- :ref:`BOARD_BUTTON_PIN <lang-board-values-but>` - :ref:`lang-isbuttonpressed` diff --git a/docs/source/lang/cc-attribution.txt b/docs/source/lang/cc-attribution.txt index e100140..11302b2 100644 --- a/docs/source/lang/cc-attribution.txt +++ b/docs/source/lang/cc-attribution.txt @@ -3,7 +3,8 @@ .. admonition:: License and Attribution - This documentation page was adapted from the `Arduino Reference - Documentation <http://arduino.cc/en/Reference/HomePage>`_\ , which - is released under a `Creative Commons Attribution-ShareAlike 3.0 - License <http://creativecommons.org/licenses/by-sa/3.0/>`_. + Some information in this page was adapted from the `Arduino + Reference Documentation + <http://arduino.cc/en/Reference/HomePage>`_\ , which is released + under a `Creative Commons Attribution-ShareAlike 3.0 License + <http://creativecommons.org/licenses/by-sa/3.0/>`_. diff --git a/docs/source/lang/cpp/arithmetic.rst b/docs/source/lang/cpp/arithmetic.rst index 7e8c3fc..cef3954 100644 --- a/docs/source/lang/cpp/arithmetic.rst +++ b/docs/source/lang/cpp/arithmetic.rst @@ -117,11 +117,8 @@ See Also -------- - The individual sizes (in bits) of various available types are - defined in `libmaple_types.h - <http://github.com/leaflabs/libmaple/blob/master/libmaple/libmaple_types.h>`_\ - . + defined in :ref:`libmaple_types.h <libmaple-libmaple_types>`. - :ref:`sizeof <lang-sizeof>`\ () - -.. include:: cc-attribution.txt +.. include:: /arduino-cc-attribution.txt diff --git a/docs/source/lang/cpp/array.rst b/docs/source/lang/cpp/array.rst index 30a818f..39d4d91 100644 --- a/docs/source/lang/cpp/array.rst +++ b/docs/source/lang/cpp/array.rst @@ -113,11 +113,9 @@ Arduino Compatibility Arrays on Maple are identical those on Arduino. -See also +See Also -------- - :ref:`Storing arrays in FLASH memory <arm-gcc-attribute-flash>` - - -.. include:: cc-attribution.txt +.. include:: /arduino-cc-attribution.txt diff --git a/docs/source/lang/cpp/assignment.rst b/docs/source/lang/cpp/assignment.rst index f9430b4..6379298 100644 --- a/docs/source/lang/cpp/assignment.rst +++ b/docs/source/lang/cpp/assignment.rst @@ -57,4 +57,4 @@ See Also <http://icu-project.org/docs/papers/cpp_report/the_anatomy_of_the_assignment_operator.html>`_ for more information. -.. include:: cc-attribution.txt +.. include:: /arduino-cc-attribution.txt diff --git a/docs/source/lang/cpp/bitshift.rst b/docs/source/lang/cpp/bitshift.rst index e1c8de0..47413f2 100644 --- a/docs/source/lang/cpp/bitshift.rst +++ b/docs/source/lang/cpp/bitshift.rst @@ -140,5 +140,4 @@ See Also - :ref:`lang-bitwrite` - :ref:`lang-bitclear` - -.. include:: cc-attribution.txt +.. include:: /arduino-cc-attribution.txt diff --git a/docs/source/lang/cpp/bitwisemath.rst b/docs/source/lang/cpp/bitwisemath.rst index 28fe6bf..59794ba 100644 --- a/docs/source/lang/cpp/bitwisemath.rst +++ b/docs/source/lang/cpp/bitwisemath.rst @@ -182,5 +182,4 @@ See Also - :ref:`Compound bitwise operations <lang-compoundbitwise>` (``&=``, ``|=``, ``^=``). - -.. include:: cc-attribution.txt +.. include:: /arduino-cc-attribution.txt diff --git a/docs/source/lang/cpp/boolean.rst b/docs/source/lang/cpp/boolean.rst index 8d6aa5c..f09345e 100644 --- a/docs/source/lang/cpp/boolean.rst +++ b/docs/source/lang/cpp/boolean.rst @@ -87,5 +87,4 @@ See Also ``|=``, ``^=``). - :ref:`if statement <lang-if>` - -.. include:: cc-attribution.txt +.. include:: /arduino-cc-attribution.txt diff --git a/docs/source/lang/cpp/booleanvariables.rst b/docs/source/lang/cpp/booleanvariables.rst index 6051b8c..0d76a12 100644 --- a/docs/source/lang/cpp/booleanvariables.rst +++ b/docs/source/lang/cpp/booleanvariables.rst @@ -42,13 +42,11 @@ Example } } -See also +See Also -------- - - :ref:`Boolean constants <lang-constants-bool>` - :ref:`Boolean operators <lang-boolean>` - :ref:`Variables <lang-variables>` - -.. include:: cc-attribution.txt +.. include:: /arduino-cc-attribution.txt diff --git a/docs/source/lang/cpp/break.rst b/docs/source/lang/cpp/break.rst index ce8ac17..f367b99 100644 --- a/docs/source/lang/cpp/break.rst +++ b/docs/source/lang/cpp/break.rst @@ -29,7 +29,4 @@ Example delay(50); } - - - -.. include:: cc-attribution.txt +.. include:: /arduino-cc-attribution.txt diff --git a/docs/source/lang/cpp/built-in-types.rst b/docs/source/lang/cpp/built-in-types.rst index a7349e6..28e8cdc 100644 --- a/docs/source/lang/cpp/built-in-types.rst +++ b/docs/source/lang/cpp/built-in-types.rst @@ -41,55 +41,60 @@ Integral types .. cpp:type:: int8 - Synonym for ``char``. + 8-bit integer value. Synonym for ``signed char``. .. cpp:type:: uint8 - Synonym for ``unsigned char``. + 8-bit unsigned integer value. Synonym for ``unsigned char``. + +.. cpp:type:: byte + + 8-bit unsigned integer value. Synonym for ``unsigned char``. .. cpp:type:: int16 - Synonym for ``short``. + 16-bit integer value. Synonym for ``short``. .. cpp:type:: uint16 - Synonym for ``unsigned short``. + 16-bit unsigned integer value. Synonym for ``unsigned short``. .. cpp:type:: int32 - Synonym for ``int``. + 32-bit integer value. Synonym for ``int``. .. cpp:type:: uint32 - Synonym for ``unsigned int`` + Unsigned 32-bit integer value. Synonym for ``unsigned int`` .. cpp:type:: int64 - Synonym for ``long long`` + 64-bit integer value. Synonym for ``long long`` .. cpp:type:: uint64 - Synonym for ``unsigned long long``. + Unsigned 64-bit integer value. Synonym for ``unsigned long long``. Floating-Point Types -------------------- .. cpp:type:: float - 32-bit, IEEE 754 single-precision floating-point type. + 32-bit, IEEE-754 single-precision floating-point type. .. cpp:type:: double - 64-bit, IEEE 754 double-precision floating-point type. + 64-bit, IEEE-754 double-precision floating-point type. Other Types ----------- .. cpp:type:: voidFuncPtr - Pointer to a function that takes no arguments and returns nothing, i.e. - - :: + Pointer to a function that takes no arguments and returns nothing, i.e. :: typedef void (*voidFuncPtr)(void); +.. cpp:type:: bool + + Boolean type. diff --git a/docs/source/lang/cpp/byte.rst b/docs/source/lang/cpp/byte.rst index 45c9d5f..4634594 100644 --- a/docs/source/lang/cpp/byte.rst +++ b/docs/source/lang/cpp/byte.rst @@ -30,5 +30,4 @@ See Also - :ref:`byte() <lang-bytecast>` (casting a value to a byte) - :ref:`Variables <lang-variables>` - -.. include:: cc-attribution.txt +.. include:: /arduino-cc-attribution.txt diff --git a/docs/source/lang/cpp/bytecast.rst b/docs/source/lang/cpp/bytecast.rst index b3f0de2..24c3b9e 100644 --- a/docs/source/lang/cpp/bytecast.rst +++ b/docs/source/lang/cpp/bytecast.rst @@ -24,13 +24,11 @@ Syntax ``byte(x)`` - Parameters ---------- **x**: a value of any integer type - Returns ------- @@ -38,13 +36,9 @@ The value, converted to a ``byte``. Note, however, that if the value is larger than the maximum value you can store in a byte (255), then the results might be strange and unexpected. - See Also -------- - :ref:`lang-byte` - - - -.. include:: cc-attribution.txt +.. include:: /arduino-cc-attribution.txt diff --git a/docs/source/lang/cpp/char.rst b/docs/source/lang/cpp/char.rst index b8747f3..686c0d1 100644 --- a/docs/source/lang/cpp/char.rst +++ b/docs/source/lang/cpp/char.rst @@ -10,7 +10,6 @@ value from -128 to 127). Character literals are written in single quotes, like this: ``'A'`` (for multiple characters - strings - use double quotes: ``"ABC"``). - Just like everything else on a computer, characters are stored as numbers. You can see the specific encoding in the `ASCII chart <http://en.wikipedia.org/wiki/ASCII#ASCII_printable_characters>`_\ @@ -25,26 +24,21 @@ The ``char`` datatype is a signed type, meaning that it encodes numbers from -128 to 127. For an unsigned type, which stores values from 0 to 255, just use the type ``unsigned char`` (two words). - Example ------- :: - // the following two lines are equivalent, using the ASCII + // The following two lines are equivalent, using the ASCII // character encoding: char c = 'A'; char c = 65; - -See also +See Also -------- - - :ref:`lang-int` - :ref:`lang-array` (a string is just an array of ``char``\ s) - :ref:`Serial.println() <lang-serial-println>` - - .. include:: cc-attribution.txt diff --git a/docs/source/lang/cpp/charcast.rst b/docs/source/lang/cpp/charcast.rst index a480dec..640ad85 100644 --- a/docs/source/lang/cpp/charcast.rst +++ b/docs/source/lang/cpp/charcast.rst @@ -12,13 +12,11 @@ Syntax ``char(x)`` - Parameters ---------- **x**: a value of any type - Returns ------- @@ -26,11 +24,9 @@ The value, converted to a ``char``. Note, however, that if the value is outside the range of a ``char`` (-128 to 127), then the results might be strange and unexpected. - See Also -------- - :ref:`char <lang-char>` - -.. include:: cc-attribution.txt +.. include:: /arduino-cc-attribution.txt diff --git a/docs/source/lang/cpp/comments.rst b/docs/source/lang/cpp/comments.rst index c5f118a..1428dc3 100644 --- a/docs/source/lang/cpp/comments.rst +++ b/docs/source/lang/cpp/comments.rst @@ -61,7 +61,4 @@ just ignores them. This can be especially useful when trying to locate a problem, or when a program refuses to compile and the compiler error is cryptic or unhelpful. - - - -.. include:: cc-attribution.txt +.. include:: /arduino-cc-attribution.txt diff --git a/docs/source/lang/cpp/comparison.rst b/docs/source/lang/cpp/comparison.rst index b24355f..9cd0a9f 100644 --- a/docs/source/lang/cpp/comparison.rst +++ b/docs/source/lang/cpp/comparison.rst @@ -83,5 +83,4 @@ Comparison operators, along with :ref:`boolean operators appears within a conditional doesn't mean it's automatically wrong. Be careful to know what you mean.) - -.. include:: cc-attribution.txt +.. include:: /arduino-cc-attribution.txt diff --git a/docs/source/lang/cpp/compoundarithmetic.rst b/docs/source/lang/cpp/compoundarithmetic.rst index 420f1db..d70a43c 100644 --- a/docs/source/lang/cpp/compoundarithmetic.rst +++ b/docs/source/lang/cpp/compoundarithmetic.rst @@ -40,5 +40,4 @@ See Also - :ref:`Arithmetic operators <lang-arithmetic>` - -.. include:: cc-attribution.txt +.. include:: /arduino-cc-attribution.txt diff --git a/docs/source/lang/cpp/compoundbitwise.rst b/docs/source/lang/cpp/compoundbitwise.rst index 8231130..4efe5df 100644 --- a/docs/source/lang/cpp/compoundbitwise.rst +++ b/docs/source/lang/cpp/compoundbitwise.rst @@ -226,5 +226,4 @@ See Also - :ref:`Boolean operations <lang-boolean>` (``&&``, ``||``) - :ref:`Bitwise operators <lang-bitwisemath>` (``&``, ``|``, ``^``, ``~``) - .. include:: cc-attribution.txt diff --git a/docs/source/lang/cpp/const.rst b/docs/source/lang/cpp/const.rst index 52de85f..ad0c580 100644 --- a/docs/source/lang/cpp/const.rst +++ b/docs/source/lang/cpp/const.rst @@ -21,7 +21,7 @@ method for defining constants than ``#define``. Example ------- -:: + :: // this defines a variable called "pi", which cannot be changed: const float pi = 3.14; @@ -33,7 +33,6 @@ Example pi = 7; // illegal - you can't write to (modify) a constant - **#define** or **const** ------------------------ @@ -48,5 +47,4 @@ See Also - :ref:`#define <lang-define>` - :ref:`volatile <lang-volatile>` - -.. include:: cc-attribution.txt +.. include:: /arduino-cc-attribution.txt diff --git a/docs/source/lang/cpp/continue.rst b/docs/source/lang/cpp/continue.rst index 13d1815..2a694f6 100644 --- a/docs/source/lang/cpp/continue.rst +++ b/docs/source/lang/cpp/continue.rst @@ -27,6 +27,4 @@ Example delay(50); } - - -.. include:: cc-attribution.txt +.. include:: /arduino-cc-attribution.txt diff --git a/docs/source/lang/cpp/curly-braces.rst b/docs/source/lang/cpp/curly-braces.rst index a4bd3dc..df2fe2a 100644 --- a/docs/source/lang/cpp/curly-braces.rst +++ b/docs/source/lang/cpp/curly-braces.rst @@ -99,11 +99,8 @@ reference page for more information):: .. rubric:: Footnotes -.. TODO remove this once IDE 0.1.0 released - .. [#fbug] At present this feature is slightly buggy as the IDE will often find (incorrectly) a brace in text that has been commented out. - -.. include:: cc-attribution.txt +.. include:: /arduino-cc-attribution.txt diff --git a/docs/source/lang/cpp/define.rst b/docs/source/lang/cpp/define.rst index 677390d..bdf7283 100644 --- a/docs/source/lang/cpp/define.rst +++ b/docs/source/lang/cpp/define.rst @@ -13,7 +13,6 @@ defined value at compile time. This can have some unwanted side effects. In general, the :ref:`const <lang-const>` keyword is preferred for defining constants. - Syntax ------ @@ -42,7 +41,7 @@ is, **don't do this, either**:: Example ------- -:: + :: #define LED_PIN 13 // The compiler will replace any mention of LED_PIN with @@ -52,5 +51,4 @@ See Also -------- - :ref:`const <lang-const>` - -.. include:: cc-attribution.txt +.. include:: /arduino-cc-attribution.txt diff --git a/docs/source/lang/cpp/double.rst b/docs/source/lang/cpp/double.rst index 1527778..59422eb 100644 --- a/docs/source/lang/cpp/double.rst +++ b/docs/source/lang/cpp/double.rst @@ -43,6 +43,4 @@ See Also - :ref:`float <lang-float>` - - -.. include:: cc-attribution.txt +.. include:: /arduino-cc-attribution.txt diff --git a/docs/source/lang/cpp/doublecast.rst b/docs/source/lang/cpp/doublecast.rst index 16a9907..d3f32ce 100644 --- a/docs/source/lang/cpp/doublecast.rst +++ b/docs/source/lang/cpp/doublecast.rst @@ -24,4 +24,4 @@ See Also - :ref:`float <lang-float>` - :ref:`float() <lang-floatcast>` -.. include:: cc-attribution.txt +.. include:: /arduino-cc-attribution.txt diff --git a/docs/source/lang/cpp/dowhile.rst b/docs/source/lang/cpp/dowhile.rst index fe92226..d229122 100644 --- a/docs/source/lang/cpp/dowhile.rst +++ b/docs/source/lang/cpp/dowhile.rst @@ -23,5 +23,4 @@ Example:: x = readSensors(); // check the sensors } while (x < 100); - -.. include:: cc-attribution.txt +.. include:: /arduino-cc-attribution.txt diff --git a/docs/source/lang/cpp/float.rst b/docs/source/lang/cpp/float.rst index 6937c8c..5195fac 100644 --- a/docs/source/lang/cpp/float.rst +++ b/docs/source/lang/cpp/float.rst @@ -47,4 +47,4 @@ See Also - :ref:`double <lang-double>` - :ref:`Variables <lang-variables>` -.. include:: cc-attribution.txt +.. include:: /arduino-cc-attribution.txt diff --git a/docs/source/lang/cpp/floatcast.rst b/docs/source/lang/cpp/floatcast.rst index 4766478..af92543 100644 --- a/docs/source/lang/cpp/floatcast.rst +++ b/docs/source/lang/cpp/floatcast.rst @@ -25,4 +25,4 @@ See Also - :ref:`double <lang-double>` - :ref:`double() <lang-doublecast>` -.. include:: cc-attribution.txt +.. include:: /arduino-cc-attribution.txt diff --git a/docs/source/lang/cpp/for.rst b/docs/source/lang/cpp/for.rst index 71c5aca..78ea562 100644 --- a/docs/source/lang/cpp/for.rst +++ b/docs/source/lang/cpp/for.rst @@ -123,7 +123,7 @@ questions (answers are in footnote [#fanswers]_\ ): 2. Why does it stop at 64? -See also +See Also -------- - :ref:`while <lang-while>` loops @@ -139,4 +139,4 @@ See also false, and the loop stops. -.. include:: cc-attribution.txt +.. include:: /arduino-cc-attribution.txt diff --git a/docs/source/lang/cpp/goto.rst b/docs/source/lang/cpp/goto.rst index ff2f248..2c0b3b0 100644 --- a/docs/source/lang/cpp/goto.rst +++ b/docs/source/lang/cpp/goto.rst @@ -126,5 +126,4 @@ See Also - Knuth, Donald. `Structured Programming with go to Statements <http://pplab.snu.ac.kr/courses/adv_pl05/papers/p261-knuth.pdf>`_ (PDF) - -.. include:: cc-attribution.txt +.. include:: /arduino-cc-attribution.txt diff --git a/docs/source/lang/cpp/if.rst b/docs/source/lang/cpp/if.rst index bef89e2..d57b9f1 100644 --- a/docs/source/lang/cpp/if.rst +++ b/docs/source/lang/cpp/if.rst @@ -118,4 +118,4 @@ See Also - :ref:`boolean operators <lang-boolean>` - :ref:`comparison operators <lang-comparison>` -.. include:: cc-attribution.txt +.. include:: /arduino-cc-attribution.txt diff --git a/docs/source/lang/cpp/include.rst b/docs/source/lang/cpp/include.rst index 74fe7af..163509d 100644 --- a/docs/source/lang/cpp/include.rst +++ b/docs/source/lang/cpp/include.rst @@ -67,6 +67,4 @@ root <http://en.wikipedia.org/wiki/Cube_root>`_ of a number:: SerialUSB.println(cubeRootOf3); } - - -.. include:: cc-attribution.txt +.. include:: /arduino-cc-attribution.txt diff --git a/docs/source/lang/cpp/increment.rst b/docs/source/lang/cpp/increment.rst index 6dffa80..c423d1a 100644 --- a/docs/source/lang/cpp/increment.rst +++ b/docs/source/lang/cpp/increment.rst @@ -34,4 +34,4 @@ See Also - :ref:`lang-compoundarithmetic` -.. include:: cc-attribution.txt +.. include:: /arduino-cc-attribution.txt diff --git a/docs/source/lang/cpp/intcast.rst b/docs/source/lang/cpp/intcast.rst index 386fe14..da838c7 100644 --- a/docs/source/lang/cpp/intcast.rst +++ b/docs/source/lang/cpp/intcast.rst @@ -23,7 +23,4 @@ See Also - :ref:`int <lang-int>` - - - -.. include:: cc-attribution.txt +.. include:: /arduino-cc-attribution.txt diff --git a/docs/source/lang/cpp/longcast.rst b/docs/source/lang/cpp/longcast.rst index f588fc6..493ad67 100644 --- a/docs/source/lang/cpp/longcast.rst +++ b/docs/source/lang/cpp/longcast.rst @@ -24,4 +24,4 @@ See Also - :ref:`long <lang-long>` - :ref:`long long <lang-longlong>` -.. include:: cc-attribution.txt +.. include:: /arduino-cc-attribution.txt diff --git a/docs/source/lang/cpp/longlong.rst b/docs/source/lang/cpp/longlong.rst index 0ba56ed..d942cb4 100644 --- a/docs/source/lang/cpp/longlong.rst +++ b/docs/source/lang/cpp/longlong.rst @@ -53,4 +53,4 @@ See Also - :ref:`Integer Constants <lang-constants-integers>` - :ref:`Variables <lang-variables>` -.. include:: cc-attribution.txt +.. include:: /arduino-cc-attribution.txt diff --git a/docs/source/lang/cpp/modulo.rst b/docs/source/lang/cpp/modulo.rst index 289fba0..013d07e 100644 --- a/docs/source/lang/cpp/modulo.rst +++ b/docs/source/lang/cpp/modulo.rst @@ -67,4 +67,4 @@ See Also - :ref:`Arithmetic <lang-arithmetic>` -.. include:: cc-attribution.txt +.. include:: /arduino-cc-attribution.txt diff --git a/docs/source/lang/cpp/pointer.rst b/docs/source/lang/cpp/pointer.rst index 0a42270..ff4ec32 100644 --- a/docs/source/lang/cpp/pointer.rst +++ b/docs/source/lang/cpp/pointer.rst @@ -28,4 +28,4 @@ See Also - http://xkcd.com/138/ -.. include:: cc-attribution.txt +.. include:: /arduino-cc-attribution.txt diff --git a/docs/source/lang/cpp/return.rst b/docs/source/lang/cpp/return.rst index b4ef5fd..d9aecbe 100644 --- a/docs/source/lang/cpp/return.rst +++ b/docs/source/lang/cpp/return.rst @@ -57,5 +57,4 @@ See Also - :ref:`comments <lang-comments>` - -.. include:: cc-attribution.txt +.. include:: /arduino-cc-attribution.txt diff --git a/docs/source/lang/cpp/scope.rst b/docs/source/lang/cpp/scope.rst index 7b65bab..a270428 100644 --- a/docs/source/lang/cpp/scope.rst +++ b/docs/source/lang/cpp/scope.rst @@ -117,4 +117,4 @@ See Also - `C++ programming Wikibook <http://en.wikibooks.org/wiki/C%2B%2B_Programming/Programming_Languages/C%2B%2B/Code/Statements/Scope>`_. - Wikipedia article on `scope <http://en.wikipedia.org/wiki/Scope_%28programming%29>`_ -.. include:: cc-attribution.txt +.. include:: /arduino-cc-attribution.txt diff --git a/docs/source/lang/cpp/semicolon.rst b/docs/source/lang/cpp/semicolon.rst index 8164616..05e6218 100644 --- a/docs/source/lang/cpp/semicolon.rst +++ b/docs/source/lang/cpp/semicolon.rst @@ -19,7 +19,4 @@ compiler error comes up, one of the first things to check is a missing semicolon, in the immediate vicinity, preceding the line at which the compiler complained. - - - -.. include:: cc-attribution.txt +.. include:: /arduino-cc-attribution.txt diff --git a/docs/source/lang/cpp/sizeof.rst b/docs/source/lang/cpp/sizeof.rst index eae509c..ec2dea6 100644 --- a/docs/source/lang/cpp/sizeof.rst +++ b/docs/source/lang/cpp/sizeof.rst @@ -60,5 +60,5 @@ would look something like this:: standard guarantees, however, is that a ``char`` occupies at *least* 8 bits. -.. include:: cc-attribution.txt +.. include:: /arduino-cc-attribution.txt diff --git a/docs/source/lang/cpp/sqrt.rst b/docs/source/lang/cpp/sqrt.rst index 956a754..fbabf82 100644 --- a/docs/source/lang/cpp/sqrt.rst +++ b/docs/source/lang/cpp/sqrt.rst @@ -21,5 +21,4 @@ See Also - :ref:`pow <lang-pow>` - :ref:`sq <lang-sq>` - -.. include:: cc-attribution.txt +.. include:: /arduino-cc-attribution.txt diff --git a/docs/source/lang/cpp/static.rst b/docs/source/lang/cpp/static.rst index 5d1802e..8c52ba0 100644 --- a/docs/source/lang/cpp/static.rst +++ b/docs/source/lang/cpp/static.rst @@ -53,5 +53,4 @@ then incremented, so it starts out at one. Subsequent calls to it was declared ``static``. Thus, ``numSensorReadings`` is a count of the number of times that ``readSensors()`` has been called. - -.. include:: cc-attribution.txt +.. include:: /arduino-cc-attribution.txt diff --git a/docs/source/lang/cpp/string.rst b/docs/source/lang/cpp/string.rst index 0a270da..84917c1 100644 --- a/docs/source/lang/cpp/string.rst +++ b/docs/source/lang/cpp/string.rst @@ -124,5 +124,4 @@ See Also - :ref:`__attribute__ <arm-gcc-attribute-flash>` - :ref:`Variables <lang-variables>` - -.. include:: cc-attribution.txt +.. include:: /arduino-cc-attribution.txt diff --git a/docs/source/lang/cpp/switchcase.rst b/docs/source/lang/cpp/switchcase.rst index b484bc5..e31ccf3 100644 --- a/docs/source/lang/cpp/switchcase.rst +++ b/docs/source/lang/cpp/switchcase.rst @@ -110,9 +110,9 @@ value as the variable to compare. In this case, you can write down all of the values the ``enum`` takes as ``case`` statements, and be sure you've covered all the possibilities. -See also: +See Also: --------- - :ref:`if/else <lang-if>` -.. include:: cc-attribution.txt +.. include:: /arduino-cc-attribution.txt diff --git a/docs/source/lang/cpp/unsignedchar.rst b/docs/source/lang/cpp/unsignedchar.rst index 5b946ed..45fedeb 100644 --- a/docs/source/lang/cpp/unsignedchar.rst +++ b/docs/source/lang/cpp/unsignedchar.rst @@ -16,18 +16,17 @@ won't store negative numbers; it is also subject to the same Example ------- -:: + :: unsigned char c = 240; See Also -------- - - :ref:`byte <lang-byte>` - :ref:`int <lang-int>` - :ref:`array <lang-array>` - :ref:`SerialUSB.println() <lang-serialusb-println>` - :ref:`Serial.println() <lang-serial-println>` -.. include:: cc-attribution.txt +.. include:: /arduino-cc-attribution.txt diff --git a/docs/source/lang/cpp/unsignedint.rst b/docs/source/lang/cpp/unsignedint.rst index ad3e2f2..b7d9716 100644 --- a/docs/source/lang/cpp/unsignedint.rst +++ b/docs/source/lang/cpp/unsignedint.rst @@ -56,4 +56,4 @@ See Also - :ref:`Integer Constants <lang-constants-integers>` - :ref:`Variables <lang-variables>` -.. include:: cc-attribution.txt +.. include:: /arduino-cc-attribution.txt diff --git a/docs/source/lang/cpp/unsignedlonglong.rst b/docs/source/lang/cpp/unsignedlonglong.rst index 910b7e4..a1143f0 100644 --- a/docs/source/lang/cpp/unsignedlonglong.rst +++ b/docs/source/lang/cpp/unsignedlonglong.rst @@ -40,4 +40,4 @@ See Also - :ref:`Integer Constants <lang-constants-integers>` - :ref:`Variables <lang-variables>` -.. include:: cc-attribution.txt +.. include:: /arduino-cc-attribution.txt diff --git a/docs/source/lang/cpp/variables.rst b/docs/source/lang/cpp/variables.rst index 336d5ab..9ffdd1d 100644 --- a/docs/source/lang/cpp/variables.rst +++ b/docs/source/lang/cpp/variables.rst @@ -165,6 +165,5 @@ See Also (usually) stored <http://en.wikipedia.org/wiki/Two%27s_complement>`_ on computers. - -.. include:: cc-attribution.txt +.. include:: /arduino-cc-attribution.txt diff --git a/docs/source/lang/cpp/void.rst b/docs/source/lang/cpp/void.rst index 88c9c64..7af0acd 100644 --- a/docs/source/lang/cpp/void.rst +++ b/docs/source/lang/cpp/void.rst @@ -28,6 +28,4 @@ Example // ... } -.. TODO doc page on function declaration? - -.. include:: cc-attribution.txt +.. include:: /arduino-cc-attribution.txt diff --git a/docs/source/lang/cpp/while.rst b/docs/source/lang/cpp/while.rst index 9047d05..e66e0aa 100644 --- a/docs/source/lang/cpp/while.rst +++ b/docs/source/lang/cpp/while.rst @@ -35,4 +35,4 @@ Example var++; } -.. include:: cc-attribution.txt +.. include:: /arduino-cc-attribution.txt diff --git a/docs/source/lang/unimplemented/notone.rst b/docs/source/lang/unimplemented/notone.rst index 485c9c5..8af878b 100644 --- a/docs/source/lang/unimplemented/notone.rst +++ b/docs/source/lang/unimplemented/notone.rst @@ -10,41 +10,28 @@ Stops the generation of a square wave triggered by `tone <http://arduino.cc/en/Reference/Tone>`_\ (). Has no effect if no tone is being generated. - - **NOTE:** if you want to play different pitches on multiple pins, you need to call noTone() on one pin before calling tone() on the next pin. - - Syntax ------ noTone(pin) - - Parameters ---------- pin: the pin on which to stop generating the tone - - Returns ------- -nothing - - +Nothing. -See also +See Also -------- - - `tone <http://arduino.cc/en/Reference/Tone>`_ () - - .. include:: /lang/cc-attribution.txt diff --git a/docs/source/lang/unimplemented/shiftout.rst b/docs/source/lang/unimplemented/shiftout.rst deleted file mode 100644 index ff3852f..0000000 --- a/docs/source/lang/unimplemented/shiftout.rst +++ /dev/null @@ -1,136 +0,0 @@ -.. _lang-shiftout: - -shiftOut() -========== - -Description ------------ - -Shifts out a byte of data one bit at a time. Starts from either the -most (i.e. the leftmost) or least (rightmost) significant bit. Each -bit is written in turn to a data pin, after which a clock pin is -pulsed to indicate that the bit is available. - - - -This is a software implementation; Arduino (as of 0019) also -provides an `SPI library <http://arduino.cc/en/Reference/SPI>`_ -that uses the hardware implementation. - - - -Syntax ------- - -shiftOut(dataPin, clockPin, bitOrder, value) - - - -Parameters ----------- - -dataPin: the pin on which to output each bit (*int*) - - - -clockPin: the pin to toggle once the **dataPin** has been set to -the correct value (*int*) - - - -bitOrder: which order to shift out the bits; either **MSBFIRST** or -**LSBFIRST**. -(Most Significant Bit First, or, Least Significant Bit First) - - - -value: the data to shift out. (*byte*) - - - -Returns -------- - -None - - - -Note ----- - -The **dataPin** and **clockPin** must already be configured as -outputs by a call to -`pinMode <http://arduino.cc/en/Reference/PinMode>`_\ (). - - - -**shiftOut** is currently written to output 1 byte (8 bits) so it -requires a two step operation to output values larger than 255. - -:: - - // Do this for MSBFIRST serial - int data = 500; - // shift out highbyte - shiftOut(dataPin, clock, MSBFIRST, (data >> 8)); - // shift out lowbyte - shiftOut(data, clock, MSBFIRST, data); - - // Or do this for LSBFIRST serial - data = 500; - // shift out lowbyte - shiftOut(dataPin, clock, LSBFIRST, data); - // shift out highbyte - shiftOut(dataPin, clock, LSBFIRST, (data >> 8)); - - - -Example -------- - -*For accompanying circuit, see the `tutorial on controlling a 74HC595 shift register <http://arduino.cc/en/Tutorial/ShiftOut>`_.* - - - -:: - - //**************************************************************// - // Name : shiftOutCode, Hello World // - // Author : Carlyn Maw,Tom Igoe // - // Date : 25 Oct, 2006 // - // Version : 1.0 // - // Notes : Code for using a 74HC595 Shift Register // - // : to count from 0 to 255 // - //**************************************************************** - - //Pin connected to ST_CP of 74HC595 - int latchPin = 8; - //Pin connected to SH_CP of 74HC595 - int clockPin = 12; - ////Pin connected to DS of 74HC595 - int dataPin = 11; - - void setup() { - //set pins to output because they are addressed in the main loop - pinMode(latchPin, OUTPUT); - pinMode(clockPin, OUTPUT); - pinMode(dataPin, OUTPUT); - } - - void loop() { - //count up routine - for (int j = 0; j < 256; j++) { - //ground latchPin and hold low for as long as you are transmitting - digitalWrite(latchPin, LOW); - shiftOut(dataPin, clockPin, LSBFIRST, j); - //return the latch pin high to signal chip that it - //no longer needs to listen for information - digitalWrite(latchPin, HIGH); - delay(1000); - } - } - - - - -.. include:: /lang/cc-attribution.txt diff --git a/docs/source/lang/unimplemented/tone.rst b/docs/source/lang/unimplemented/tone.rst index f83bf6b..13d581e 100644 --- a/docs/source/lang/unimplemented/tone.rst +++ b/docs/source/lang/unimplemented/tone.rst @@ -12,70 +12,47 @@ continues until a call to `noTone <http://arduino.cc/en/Reference/NoTone>`_\ (). The pin can be connected to a piezo buzzer or other speaker to play tones. - - Only one tone can be generated at a time. If a tone is already playing on a different pin, the call to tone() will have no effect. If the tone is playing on the same pin, the call will set its frequency. - - Use of the tone() function will interfere with PWM output on pins 3 and 11 (on boards other than the Mega). - - **NOTE:** if you want to play different pitches on multiple pins, you need to call noTone() on one pin before calling tone() on the next pin. - - Syntax ------ tone(pin, frequency) tone(pin, frequency, duration) - - Parameters ---------- pin: the pin on which to generate the tone - - frequency: the frequency of the tone in hertz - - duration: the duration of the tone in milliseconds (optional) - - Returns ------- nothing - - -See also +See Also -------- - - `noTone <http://arduino.cc/en/Reference/NoTone>`_\ () - `analogWrite <http://arduino.cc/en/Reference/AnalogWrite>`_\ () - `Tutorial:Tone <http://arduino.cc/en/Tutorial/Tone>`_ - `Tutorial:Pitch follower <http://arduino.cc/en/Tutorial/Tone2>`_ - `Tutorial:Simple Keyboard <http://arduino.cc/en/Tutorial/Tone3>`_ - `Tutorial: multiple tones <http://arduino.cc/en/Tutorial/Tone4>`_ - - - `Tutorial: PWM <http://arduino.cc/en/Tutorial/PWM>`_ - - -.. include:: /lang/cc-attribution.txt +.. include:: /arduino-cc-attribution.txt diff --git a/docs/source/language-index.rst b/docs/source/language-index.rst index 3c55c33..225a9ae 100644 --- a/docs/source/language-index.rst +++ b/docs/source/language-index.rst @@ -10,6 +10,22 @@ API references for documented libmaple functionality. The "C++ for Maple" pages are intended as a minimal reference/refresher for programmers familiar with the Arduino language. +.. admonition:: **Looking for Something Else?** + + - See the :ref:`libraries` for extra built-in libraries for dealing + with different kinds of hardware. + + - If you're looking for something from the C standard library (like + ``atoi()``, for instance): the :ref:`CodeSourcery GCC compiler + <arm-gcc>` used to compile your programs is configured to link + against `newlib <http://sourceware.org/newlib/>`_, and allows the + use of any of its header files. However, dynamic memory allocation + (``malloc()``, etc.) is not available. + + - If you're looking for pointers to low-level details, see the + :ref:`Language Recommended Reading + <language-recommended-reading>`. + .. _index-language-index-cpp: .. _index-language-index-api: @@ -26,14 +42,13 @@ programmers familiar with the Arduino language. | | | +----------------------------------+------------------------------------+ -.. Unimplemented in libmaple or not part of current release: +.. Unimplemented or not part of current release: .. toctree:: :hidden: + lang/unimplemented/tone.rst lang/unimplemented/notone.rst lang/unimplemented/pulsein.rst - lang/unimplemented/shiftout.rst lang/unimplemented/stringclass.rst lang/unimplemented/stringobject.rst - lang/unimplemented/tone.rst diff --git a/docs/source/language.rst b/docs/source/language.rst index 2ebe03c..3ecbe43 100644 --- a/docs/source/language.rst +++ b/docs/source/language.rst @@ -10,6 +10,8 @@ The Maple can be programmed in the `Wiring <http://www.wiring.org.co/reference/>`_ language, which is the same language used to program the `Arduino <http://arduino.cc/>`_ boards. +.. TODO [0.2.0?] Wiring tutorial + C or C++ programmers curious about the differences between the Wiring language and C++ may wish to skip to the :ref:`arduino_c_for_c_hackers`. @@ -17,43 +19,29 @@ language and C++ may wish to skip to the .. contents:: Contents :local: -Unique Maple Additions ----------------------- - -.. _language-assert: - -``ASSERT(...)`` - The ``ASSERT()`` function can be very useful for basic program - debugging. The function accepts a boolean; for example:: - - ASSERT(state == WAIT); +.. admonition:: **Looking for Something Else?** - zero is false and any other number is true. If the boolean is true - the assertion passes and the program continues as usual. If it is - false, the assertion fails: the program is halted, debug - information is printed to USART2, and the status LED begins to - throb in intensity (it's noticeably different from blinking). The - debug information is printed at 9600 baud and consists of the - filename and line number where the particular assertion failed. + - See the :ref:`libraries` for extra built-in libraries for dealing + with different kinds of hardware. - Including assertions in a program increases the program size. When - using libmaple **from the command line only**, they can be - disabled by making the definition :: + - If you're looking for something from the C standard library (like + ``atoi()``, for instance): the :ref:`CodeSourcery GCC compiler + <arm-gcc>` used to compile your programs is configured to link + against `newlib <http://sourceware.org/newlib/>`_, and allows the + use of any of its header files. However, dynamic memory allocation + (``malloc()``, etc.) is not available. - #define DEBUG_LEVEL DEBUG_NONE - - before including either wirish.h or libmaple.h. In this case, all - assertions will pass without any lost clock cycles. Note that - this will **not work in the IDE**; even with this definition, - assertions will still be enabled. + - If you're looking for pointers to low-level details, see this page's + :ref:`Recommended Reading <language-recommended-reading>`. .. _language-lang-docs: Maple Language Reference ------------------------ -The following table summarizes the available core language features. -A more exhaustive index is available at the :ref:`language-index`. +The following table summarizes the most important core language +features. An exhaustive index is available at the +:ref:`language-index`. +--------------------------------------------+----------------------------------------------+---------------------------------------------------+ | Structure | Variables | Functions | @@ -70,13 +58,13 @@ A more exhaustive index is available at the :ref:`language-index`. | |* :ref:`true <lang-constants-true>` | |* :ref:`togglePin() <lang-togglepin>` | |* :ref:`for <lang-for>` | :ref:`false <lang-constants-false>` | | | | |* :ref:`toggleLED() <lang-toggleled>` | -|* :ref:`switch/case <lang-switchcase>` |* :ref:`BOARD_LED_PIN <lang-constants-led>` | | | -| | :ref:`BOARD_BUTTON_PIN <lang-constants-but>`|* :ref:`isButtonPressed() <lang-isbuttonpressed>` | -|* :ref:`while <lang-while>` | | | -| |* :ref:`Constants |* :ref:`waitForButtonPress() | -|* :ref:`do...while <lang-dowhile>` | <lang-constants>` (:ref:`integers | <lang-waitforbuttonpress>` | -| | <lang-constants-integers>`, :ref:`floating | | -|* :ref:`break <lang-break>` | point <lang-constants-fp>`) |**Analog I/O** | +|* :ref:`switch/case <lang-switchcase>` |* :ref:`Constants | | +| | <lang-constants>` (:ref:`integers |* :ref:`isButtonPressed() <lang-isbuttonpressed>` | +|* :ref:`while <lang-while>` | <lang-constants-integers>`, :ref:`floating | | +| | point <lang-constants-fp>`) |* :ref:`waitForButtonPress() | +|* :ref:`do...while <lang-dowhile>` | | <lang-waitforbuttonpress>` | +| |* :ref:`Board-specific values | | +|* :ref:`break <lang-break>` | <lang-board-values>` |**Analog I/O** | | | | | |* :ref:`continue <lang-continue>` |**Data Types** |* :ref:`analogRead() <lang-analogread>` | | | | | @@ -91,7 +79,7 @@ A more exhaustive index is available at the :ref:`language-index`. | |* :ref:`boolean <lang-boolean>` (1 byte) | | |* :ref:`{} (curly braces) | |* noTone(): TODO | | <lang-curly-braces>` |* :ref:`char <lang-char>` (1 byte) | | -| | |* shiftOut(): TODO | +| | |* :ref:`shiftOut() <lang-shiftout>` | |* :ref:`// (single-line comment) |* :ref:`unsigned char | | | <lang-comments-singleline>` | <lang-unsignedchar>` (1 byte) |* pulseIn(): TODO | | | | | @@ -106,7 +94,7 @@ A more exhaustive index is available at the :ref:`language-index`. |**Arithmetic Operators** | <lang-int>` | | | | |* :ref:`delayMicroseconds() | |* :ref:`= <lang-assignment>` |* ``unsigned long`` (4 bytes), synonym for | <lang-delaymicroseconds>` | -| (assignment operator) | :ref:`unsigned int <lang-unsignedint>` | | +| (assignment) | :ref:`unsigned int <lang-unsignedint>` | | | | | | |* :ref:`+ <lang-arithmetic>` (addition) |* :ref:`long long <lang-longlong>` (8 bytes) |**Math** | | | | | @@ -183,13 +171,13 @@ A more exhaustive index is available at the :ref:`language-index`. | | | | |* :ref:`++ <lang-increment>` | |* :ref:`Serial <lang-serial>` | | (increment) | | | -| | |**Looking for something else?** | +| | | | |* :ref:`- - <lang-increment>` | | | -| (decrement) | | See the :ref:`libraries` page for interfacing with| -| | | particular types of hardware. Maple links | -|* :ref:`+= <lang-compoundarithmetic>` | | against `newlib <http://sourceware.org/newlib/>`_ | -| (compound add) | | and allows the use of any of its functions; see | -| | | its documentation for more details. | +| (decrement) | | | +| | | | +|* :ref:`+= <lang-compoundarithmetic>` | | | +| (compound add) | | | +| | | | |* :ref:`-= | | | | <lang-compoundarithmetic>` (compound | | | | subtract) | | | @@ -217,6 +205,35 @@ A more exhaustive index is available at the :ref:`language-index`. | | | | +--------------------------------------------+----------------------------------------------+---------------------------------------------------+ +.. _language-assert: + +``ASSERT(...)`` +--------------- + +The ``ASSERT()`` function can be very useful for basic program +debugging. It accepts a boolean; for example:: + + ASSERT(state == WAIT); + +Zero is false and any other number is true. If the boolean is true, the +assertion passes and the program continues as usual. If it is false, +the assertion fails: the program is halted, debug information is +printed to USART2, and the status LED begins to throb in intensity +(it's noticeably different from blinking). The debug information is +printed at 9600 baud and consists of the filename and line number +where the particular assertion failed. + +Including assertions in a program increases the program size. When +using libmaple **from the command line only**, they can be disabled by +making the definition :: + + #define DEBUG_LEVEL DEBUG_NONE + +before including either wirish.h or libmaple.h. In this case, all +assertions will pass without any lost clock cycles. Note that this +will **not work in the IDE**; even with this definition, assertions +will still be enabled. + .. _language-missing-features: Missing Arduino Features @@ -256,13 +273,12 @@ Unimplemented Arduino Features ------------------------------ The following Wiring/Arduino features are currently unimplemented on -the Maple. However, they will be present in future versions: +the Maple. +- `tone() <http://www.arduino.cc/en/Reference/Tone>`_ - `noTone() <http://www.arduino.cc/en/Reference/NoTone>`_ - `pulseIn() <http://www.arduino.cc/en/Reference/PulseIn>`_ -- `shiftOut() <http://www.arduino.cc/en/Reference/ShiftOut>`_ - `String <http://arduino.cc/en/Reference/StringObject>`_ -- `tone() <http://www.arduino.cc/en/Reference/Tone>`_ .. _our reference page: http://leaflabs.com/docs/external-interrupts/ @@ -283,16 +299,18 @@ programming ideas and C++. Note for C/C++ Hackers ---------------------- -This is a note for programmers comfortable with C or C++ (although, -you C programmers should remember that `C++ is not a superset of C -<http://en.wikipedia.org/wiki/Compatibility_of_C_and_C%2B%2B>`_) who -want a better understanding of the differences between C++ and the -Wiring language. The good news is that the differences are relatively -few; Wiring is just a thin wrapper around C++. +This is a note for programmers comfortable with C or C++ who want a +better understanding of the differences between C++ and the Wiring +language. -Some potentially better news is that the Maple can be programmed using -a :ref:`standard Unix toolchain <unix-toolchain>`, so if you'd rather -stick with :command:`gcc`, :command:`make`, and friends, you can. +The good news is that the differences are relatively few; Wiring is +just a thin wrapper around C++. Some potentially better news is that +the Maple can be programmed using a :ref:`standard Unix toolchain +<unix-toolchain>`, so if you'd rather stick with :command:`gcc`, +:command:`make`, and friends, you can. If you're using the Unix +toolchain and want to skip past the Wiring conveniences and get +straight to registers, you are encouraged to move on to the +:ref:`libmaple` documentation. A *sketch* is the IDE's notion of a project; it consists of one or more files written in the Wiring language, which is mostly the same as @@ -318,9 +336,9 @@ work. As of |today|, Maple only has 20 KB RAM, anyway, so it's doubtful that static allocation is not what you want. The Wiring language also does not require you to define your own -``main`` method (in fact, it forbids you from doing so). Instead, you -are required to define two functions, ``setup`` and ``loop``, with -type signatures :: +``main`` method (in fact, we currently forbid you from doing so). +Instead, you are required to define two functions, ``setup`` and +``loop``, with type signatures :: void setup(void); void loop(void); @@ -335,26 +353,33 @@ parses the result to produce a list of all functions defined in the global scope. (We borrow this stage from the Arduino IDE, which in turn borrows it from Wiring. It uses regular expressions to parse C++, which is, of course, `Bad and Wrong -<http://www.retrologic.com/jargon/B/Bad-and-Wrong.html>`_. An -upcoming rewrite of the IDE performs this preprocessing step -correctly, using a real parser. Until then, you have our apologies.) -The order in which the individual sketch files are concatenated is not -defined; it is unwise to write code that depends on a particular -ordering. +<http://www.retrologic.com/jargon/B/Bad-and-Wrong.html>`_. In the +future, we'll do this correctly, using a better parser. Until then, +you have our apologies.) The order in which the individual sketch +files are concatenated is not defined; it is unwise to write code that +depends on a particular ordering. The concatenated sketch files are then appended onto a file which includes `WProgram.h -<http://github.com/leaflabs/libmaple/blob/master/wirish/WProgram.h>`_ +<https://github.com/leaflabs/libmaple/blob/master/wirish/WProgram.h>`_ (which includes the wirish and libmaple libraries, and declares ``setup()`` and ``loop()``), and then provides declarations for all the function definitions found in the previous step. At this point, -we have a file that is a valid C++ translation unit, but lacks a -``main()`` method. The final step of compilation provides this -method, which behaves roughly like:: +we have a file that is a valid C++ translation unit, but lacks +``main()``. The final step of compilation provides ``main()``, which +behaves roughly like:: int main(void) { + // Call libmaple's built-in initialization routines + init(); + + // Perform the user's initialization setup(); - while (true) loop(); + + // Call user loop() forever. + while (true) { + loop(); + } } (The truth is a little bit more complicated, but not by much). @@ -425,20 +450,31 @@ Which could plausibly be turned into the final source file :: } int main() { + init(); setup(); while (true) loop(); } -(Recall that it's legal C++ for a function to be declared multiple -times, as long as it's defined exactly once). - +.. _language-recommended-reading: Recommended Reading ------------------- +* :ref:`libmaple Documentation <libmaple>` +* Your board's :ref:`Board Hardware Documentation <index-boards>` page +* ST Documentation: + * Reference Manual `RM0008 + <http://www.st.com/stonline/products/literature/rm/13902.pdf>`_ + (PDF). This is the most important reference work on the STM32 + line, and covers the low-level hardware capabilities and + interfaces in great detail. + * `Programming Manual + <http://www.st.com/stonline/products/literature/pm/15491.pdf>`_ + (PDF). This is an assembly language and register reference for + the STM32 line. +* ARM Documentation: + * `Cortex-M3 Technical Reference Manual, Revision r1p1 + <http://infocenter.arm.com/help/topic/com.arm.doc.ddi0337e/DDI0337E_cortex_m3_r1p1_trm.pdf>`_ + (PDF). This ARM manual specifies much of the the Cortex M3 + Architecture, including instruction timings. * `newlib Documentation <http://sourceware.org/newlib/>`_ -* STMicro documentation for STM32F103RB microcontroller: - - * `Datasheet <http://www.st.com/stonline/products/literature/ds/13587.pdf>`_ (pdf) - * `Reference Manual <http://www.st.com/stonline/products/literature/rm/13902.pdf>`_ (pdf) - * `Programming Manual <http://www.st.com/stonline/products/literature/pm/15491.pdf>`_ (assembly language and register reference) diff --git a/docs/source/libmaple.rst b/docs/source/libmaple.rst index 8cc39a3..375e821 100644 --- a/docs/source/libmaple.rst +++ b/docs/source/libmaple.rst @@ -5,37 +5,35 @@ ``libmaple`` ============ -.. image:: /_static/img/libmaple-screenshot-small.png - :align: center - :alt: libmaple screenshot - -`LeafLabs libmaple <libmaple-libmaple>`_ is the low level library we -have developed for for the ARM Cortex-M3 chips manufactured by -STMicroelectronics used in the Maple boards (the `STM32F103x`_ -series). We found the generic peripheral libraries too painful to -build on top of, and reimplemented the functionality we needed in a -simpler (and less general) form. - -.. _libmaple-libmaple: http://github.com/leaflabs/libmaple -.. _STM32F103x: http://www.st.com/stonline/stappl/productcatalog/app?path=/pages/stcom/PcStComPartNumberSearch.searchPartNumber&search=stm32f103 - -This library is transparently included in the `Maple IDE -<http://leaflabs.com/docs/maple-ide/>`_, but we develop it separately -using good old Unix command line tools and release it for advanced -users who might chafe at the "sketch" programming model of the -IDE. Included are some examples, a Makefile, and the compatibility -wrappers and code to imitate the Arduino programming library. - -**Check out the latest source**:: - - git clone git://github.com/leaflabs/libmaple.git - -**Table of contents:** +LeafLabs' libmaple (`source code on Github +<https://github.com/leaflabs/libmaple>`_) is the library we have +developed for the `STM32 <http://www.st.com/stonline>`_ line of ARM Cortex M3 +microcontrollers. Its high-level interfaces are :ref:`largely +compatible <arduino-compatibility>` with the AVR libraries written for +the `Arduino <http://arduino.cc>`_ and `Wiring +<http://wiring.org.co/>`_ development boards. + +libmaple is split into two pieces: a lower level layer written in pure +C, which we call *libmaple proper* (in the `libmaple/ +<https://github.com/leaflabs/libmaple/tree/master/libmaple>`_ +directory of the source repository), and the Wiring-style C++ API +written on top of it, called *Wirish* (in `wirish/ +<https://github.com/leaflabs/libmaple/tree/master/wirish>`_). + +libmaple is bundled with the :ref:`Maple IDE <ide>`. However, we +develop it separately, and :ref:`release it standalone +<unix-toolchain>` for advanced users who might chafe at the "sketch" +programming model of the IDE. + +As always, :ref:`patches are welcome <libmaple-contributing>`. + +**Contents:** .. toctree:: - :maxdepth: 2 + :maxdepth: 1 - Guide to using GCC's ARM target <arm-gcc> + libmaple/overview + libmaple/apis + libmaple/contributing + libmaple/coding-standard -.. TODO LATER create, style, and host a pure Doxygen libmaple -.. reference docs. This implies determining a stable API. diff --git a/docs/source/libmaple/api/adc.rst b/docs/source/libmaple/api/adc.rst new file mode 100644 index 0000000..8817055 --- /dev/null +++ b/docs/source/libmaple/api/adc.rst @@ -0,0 +1,12 @@ +.. highlight:: c +.. _libmaple-adc: + +``adc.h`` +========= + +[Stub] support. + +Library Documentation +--------------------- + +.. doxygenfile:: adc.h diff --git a/docs/source/libmaple/api/bitband.rst b/docs/source/libmaple/api/bitband.rst new file mode 100644 index 0000000..fd57944 --- /dev/null +++ b/docs/source/libmaple/api/bitband.rst @@ -0,0 +1,12 @@ +.. highlight:: c +.. _libmaple-bitband: + +``bitband.h`` +============= + +[Stub] support. + +Library Documentation +--------------------- + +.. doxygenfile:: bitband.h diff --git a/docs/source/libmaple/api/bkp.rst b/docs/source/libmaple/api/bkp.rst new file mode 100644 index 0000000..9a697c7 --- /dev/null +++ b/docs/source/libmaple/api/bkp.rst @@ -0,0 +1,12 @@ +.. highlight:: c +.. _libmaple-bkp: + +``bkp.h`` +========= + +[Stub] support. + +Library Documentation +--------------------- + +.. doxygenfile:: bkp.h diff --git a/docs/source/libmaple/api/dac.rst b/docs/source/libmaple/api/dac.rst new file mode 100644 index 0000000..038753b --- /dev/null +++ b/docs/source/libmaple/api/dac.rst @@ -0,0 +1,12 @@ +.. highlight:: c +.. _libmaple-dac: + +``dac.h`` +========= + +[Stub] support. + +Library Documentation +--------------------- + +.. doxygenfile:: dac.h diff --git a/docs/source/libmaple/api/delay.rst b/docs/source/libmaple/api/delay.rst new file mode 100644 index 0000000..a0d013a --- /dev/null +++ b/docs/source/libmaple/api/delay.rst @@ -0,0 +1,12 @@ +.. highlight:: c +.. _libmaple-delay: + +``delay.h`` +=========== + +[Stub] support. + +Library Documentation +--------------------- + +.. doxygenfile:: delay.h diff --git a/docs/source/libmaple/api/dma.rst b/docs/source/libmaple/api/dma.rst new file mode 100644 index 0000000..1512d0c --- /dev/null +++ b/docs/source/libmaple/api/dma.rst @@ -0,0 +1,12 @@ +.. highlight:: c +.. _libmaple-dma: + +``dma.h`` +========= + +[Stub] support. + +Library Documentation +--------------------- + +.. doxygenfile:: dma.h diff --git a/docs/source/libmaple/api/exti.rst b/docs/source/libmaple/api/exti.rst new file mode 100644 index 0000000..2909aa7 --- /dev/null +++ b/docs/source/libmaple/api/exti.rst @@ -0,0 +1,12 @@ +.. highlight:: c +.. _libmaple-exti: + +``exti.h`` +========== + +[Stub] support. + +Library Documentation +--------------------- + +.. doxygenfile:: exti.h diff --git a/docs/source/libmaple/api/flash.rst b/docs/source/libmaple/api/flash.rst new file mode 100644 index 0000000..6f2f9d3 --- /dev/null +++ b/docs/source/libmaple/api/flash.rst @@ -0,0 +1,12 @@ +.. highlight:: c +.. _libmaple-flash: + +``flash.h`` +=========== + +[Stub] support. + +Library Documentation +--------------------- + +.. doxygenfile:: flash.h diff --git a/docs/source/libmaple/api/fsmc.rst b/docs/source/libmaple/api/fsmc.rst new file mode 100644 index 0000000..cecfc99 --- /dev/null +++ b/docs/source/libmaple/api/fsmc.rst @@ -0,0 +1,12 @@ +.. highlight:: c +.. _libmaple-fsmc: + +``fsmc.h`` +========== + +[Stub] support. + +Library Documentation +--------------------- + +.. doxygenfile:: fsmc.h diff --git a/docs/source/libmaple/api/gpio.rst b/docs/source/libmaple/api/gpio.rst new file mode 100644 index 0000000..2cfec23 --- /dev/null +++ b/docs/source/libmaple/api/gpio.rst @@ -0,0 +1,12 @@ +.. highlight:: c +.. _libmaple-gpio: + +``gpio.h`` +========== + +[Stub] support. + +Library Documentation +--------------------- + +.. doxygenfile:: gpio.h diff --git a/docs/source/libmaple/api/i2c.rst b/docs/source/libmaple/api/i2c.rst new file mode 100644 index 0000000..14dd304 --- /dev/null +++ b/docs/source/libmaple/api/i2c.rst @@ -0,0 +1,12 @@ +.. highlight:: c +.. _libmaple-i2c: + +``i2c.h`` +========= + +[Stub] support. + +Library Documentation +--------------------- + +.. doxygenfile:: i2c.h diff --git a/docs/source/libmaple/api/iwdg.rst b/docs/source/libmaple/api/iwdg.rst new file mode 100644 index 0000000..3911ece --- /dev/null +++ b/docs/source/libmaple/api/iwdg.rst @@ -0,0 +1,12 @@ +.. highlight:: c +.. _libmaple-iwdg: + +``iwdg.h`` +========== + +[Stub] support. + +Library Documentation +--------------------- + +.. doxygenfile:: iwdg.h diff --git a/docs/source/libmaple/api/libmaple.rst b/docs/source/libmaple/api/libmaple.rst new file mode 100644 index 0000000..d4f28f0 --- /dev/null +++ b/docs/source/libmaple/api/libmaple.rst @@ -0,0 +1,12 @@ +.. highlight:: c +.. _libmaple-libmaple: + +``libmaple.h`` +============== + +[Stub] support. + +Library Documentation +--------------------- + +.. doxygenfile:: libmaple.h diff --git a/docs/source/libmaple/api/libmaple_types.rst b/docs/source/libmaple/api/libmaple_types.rst new file mode 100644 index 0000000..bbea2c1 --- /dev/null +++ b/docs/source/libmaple/api/libmaple_types.rst @@ -0,0 +1,12 @@ +.. highlight:: c +.. _libmaple-libmaple_types: + +``libmaple_types.h`` +==================== + +[Stub] support. + +Library Documentation +--------------------- + +.. doxygenfile:: libmaple_types.h diff --git a/docs/source/libmaple/api/nvic.rst b/docs/source/libmaple/api/nvic.rst new file mode 100644 index 0000000..b94dc31 --- /dev/null +++ b/docs/source/libmaple/api/nvic.rst @@ -0,0 +1,12 @@ +.. highlight:: c +.. _libmaple-nvic: + +``nvic.h`` +========== + +[Stub] support. + +Library Documentation +--------------------- + +.. doxygenfile:: nvic.h diff --git a/docs/source/libmaple/api/pwr.rst b/docs/source/libmaple/api/pwr.rst new file mode 100644 index 0000000..82e4864 --- /dev/null +++ b/docs/source/libmaple/api/pwr.rst @@ -0,0 +1,12 @@ +.. highlight:: c +.. _libmaple-pwr: + +``pwr.h`` +========= + +[Stub] support. + +Library Documentation +--------------------- + +.. doxygenfile:: pwr.h diff --git a/docs/source/libmaple/api/rcc.rst b/docs/source/libmaple/api/rcc.rst new file mode 100644 index 0000000..81dc604 --- /dev/null +++ b/docs/source/libmaple/api/rcc.rst @@ -0,0 +1,12 @@ +.. highlight:: c +.. _libmaple-rcc: + +``rcc.h`` +========= + +[Stub] support. + +Library Documentation +--------------------- + +.. doxygenfile:: rcc.h diff --git a/docs/source/libmaple/api/ring_buffer.rst b/docs/source/libmaple/api/ring_buffer.rst new file mode 100644 index 0000000..a014fa4 --- /dev/null +++ b/docs/source/libmaple/api/ring_buffer.rst @@ -0,0 +1,12 @@ +.. highlight:: c +.. _libmaple-ring_buffer: + +``ring_buffer.h`` +================= + +[Stub] support. + +Library Documentation +--------------------- + +.. doxygenfile:: ring_buffer.h diff --git a/docs/source/libmaple/api/scb.rst b/docs/source/libmaple/api/scb.rst new file mode 100644 index 0000000..78cc7eb --- /dev/null +++ b/docs/source/libmaple/api/scb.rst @@ -0,0 +1,12 @@ +.. highlight:: c +.. _libmaple-scb: + +``scb.h`` +========= + +[Stub] support. + +Library Documentation +--------------------- + +.. doxygenfile:: scb.h diff --git a/docs/source/libmaple/api/spi.rst b/docs/source/libmaple/api/spi.rst new file mode 100644 index 0000000..b0c7e86 --- /dev/null +++ b/docs/source/libmaple/api/spi.rst @@ -0,0 +1,12 @@ +.. highlight:: c +.. _libmaple-spi: + +``spi.h`` +========= + +[Stub] support. + +Library Documentation +--------------------- + +.. doxygenfile:: spi.h diff --git a/docs/source/libmaple/api/stm32.rst b/docs/source/libmaple/api/stm32.rst new file mode 100644 index 0000000..2784540 --- /dev/null +++ b/docs/source/libmaple/api/stm32.rst @@ -0,0 +1,12 @@ +.. highlight:: c +.. _libmaple-stm32: + +``stm32.h`` +=========== + +[Stub] support. + +Library Documentation +--------------------- + +.. doxygenfile:: stm32.h diff --git a/docs/source/libmaple/api/systick.rst b/docs/source/libmaple/api/systick.rst new file mode 100644 index 0000000..a02b8e4 --- /dev/null +++ b/docs/source/libmaple/api/systick.rst @@ -0,0 +1,20 @@ +.. highlight:: c + + +.. _libmaple-systick: + +.. FIXME [0.0.10] move these to the right places: + +.. _libmaple-systick_disable: + +.. _libmaple-systick_resume: + +``systick.h`` +============= + +[Stub] support. + +Library Documentation +--------------------- + +.. doxygenfile:: systick.h diff --git a/docs/source/libmaple/api/timer.rst b/docs/source/libmaple/api/timer.rst new file mode 100644 index 0000000..3acbf4f --- /dev/null +++ b/docs/source/libmaple/api/timer.rst @@ -0,0 +1,12 @@ +.. highlight:: c +.. _libmaple-timer: + +``timer.h`` +=========== + +[Stub] support. + +Library Documentation +--------------------- + +.. doxygenfile:: timer.h diff --git a/docs/source/libmaple/api/usart.rst b/docs/source/libmaple/api/usart.rst new file mode 100644 index 0000000..26e6b9c --- /dev/null +++ b/docs/source/libmaple/api/usart.rst @@ -0,0 +1,12 @@ +.. highlight:: c +.. _libmaple-usart: + +``usart.h`` +=========== + +[Stub] support. + +Library Documentation +--------------------- + +.. doxygenfile:: usart.h diff --git a/docs/source/libmaple/api/util.rst b/docs/source/libmaple/api/util.rst new file mode 100644 index 0000000..50ffe76 --- /dev/null +++ b/docs/source/libmaple/api/util.rst @@ -0,0 +1,12 @@ +.. highlight:: c +.. _libmaple-util: + +``util.h`` +========== + +[Stub] support. + +Library Documentation +--------------------- + +.. doxygenfile:: util.h diff --git a/docs/source/libmaple/apis.rst b/docs/source/libmaple/apis.rst new file mode 100644 index 0000000..f493406 --- /dev/null +++ b/docs/source/libmaple/apis.rst @@ -0,0 +1,14 @@ +.. _libmaple-apis: + +APIs +==== + +This is the master index for libmaple proper's APIs. + +**Contents** + +.. toctree:: + :maxdepth: 1 + :glob: + + api/* diff --git a/docs/source/libmaple/coding-standard.rst b/docs/source/libmaple/coding-standard.rst new file mode 100644 index 0000000..23d20f8 --- /dev/null +++ b/docs/source/libmaple/coding-standard.rst @@ -0,0 +1,412 @@ +.. _libmaple-coding-standard: + +Coding Standard +=============== + +This page documents the coding standard for :ref:`libmaple`. It's +intended as a guide for how you should structure any code you would +like included into the LeafLabs releases of libmaple. + +LeafLabs team members are required to follow these when producing new +code. Community contributors to libmaple are strongly encouraged to +do so; following these rules will greatly increase the probability +that your patches will be folded in. + +In general, follow this guide unless there's a very good reason not +to. Laziness doesn't count as a good reason. Most, if not all, of +these decisions are entirely arbitrary, but it's important for +readability that we be consistent. (If you notice an inconsistency, +you should fix it). + +Note that the file ``.dir-locals.el`` in the libmaple root directory +already ensures that many of these standards are followed by default +in Emacs (but not on Windows, where it would need to be named +``_dir_locals.el``, and no way, man). There's also some elisp +scattered about this file which will provide you additional help. + +Vim customizations to do the same thing would be nice! + +.. contents:: Contents + :local: + +License +------- + +.. highlight:: scheme + +Put an MIT license at the beginning of the file (look at any of our +source files for an example). Copyright should go either to you or to +LeafLabs, LLC. + +Emacs: if you don't like seeing the license, you should use elide-head +(which will hide it for you). You can use the following:: + + (require 'elide-head) + (setq programming-mode-hooks '(c-mode-hook c++-mode-hook)) + (add-to-list 'elide-head-headers-to-hide + '("The MIT License" . "DEALINGS IN\n [*] THE SOFTWARE")) + (add-to-list 'elide-head-headers-to-hide + '("The MIT License" . "DEALINGS IN THE\n...SOFTWARE")) + (dolist (hook programming-mode-hooks) + (add-hook hook (lambda () (elide-head)))) + +Whitespace +---------- + +- 4 space indents (set in ``.dir-locals.el``). + +- Unix newlines. Some exceptions are currently grandfathered in; these + will go away in time. + +- No tab characters (set in ``.dir-locals.el``). + +- No trailing whitespace. For help getting this (and no tab + characters) done automatically in Emacs, you can use + `code-fascism.el <https://github.com/mbolivar/code-fascism>`_. + +- Files end in exactly one newline. The presence of a newline at EOF + is already done by ``c-require-final-newline`` in recent versions of + Emacs. + +- Exactly two newlines separate source paragraphs (you do separate + your code into paragraphs, don't you?). + +- The first line in a function is non-blank. + +.. highlight:: cpp + +- Exactly one space after ``if``, ``else``, ``for``, and ``while``, + before the following ``{`` or ``(``. One space before ``else``, + after the preceding ``}``. For example:: + + // This is good; we like this: + if (foo) { + while (quux) { + bar(); + } + } else { + baz(); + } + + // THIS IS BAD! DON'T DO THIS: + if(foo){ + while(quux){ + bar(); + } + }else{ + baz(); + } + +- Exactly one space in between binary arithmetic, logical, and + comparison operators and their operands. Examples:: + + // This is good: + int x = a + b * (c - d); + if (x != 0 && a > 7) { + SerialUSB.println(x); + } + + // THIS IS BAD! + int x = a+b*(c-d); + if (x!=0 && a>7) { + SerialUSB.println(x); + } + + // This is good: + uint32 adc_data = ADC1_BASE->DR; + SerialUSB.println(adc_data); + + // THIS IS BAD! + uint32 adc_data = ADC1_BASE -> DR; + SerialUSB . println(adc_data); + +- No space between a unary operator and its operand. Examples:: + + // Good: + x++; + + // BAD! + x ++; + + // Good: + y = -x; + + // BAD! + y = - x; + +- If you need to break up a long line: + + * Prefer to break up long expressions after a binary operator. Example:: + + // Good: + if (some_really_long_conditional_wow_this_really_goes_on_forever || + maybe_something_else_could_happen_too) { + ... + } + + // BAD! + if (some_really_long_conditional_wow_this_really_goes_on_forever + || maybe_something_else_could_happen_too) { + ... + } + + * When breaking up a function's arguments over multiple lines, align + the arguments on subsequent lines with the first argument. + Example:: + + // Good: + return_type value_i_got = function_with_a_really_long_name(argument1, + argument2, + argument3); + + // BAD! + return_type value_i_got = function_with_a_really_long_name(argument1, + argument2, + argument3); + + // BAD! + return_type value_i_got = function_with_a_really_long_name(argument1, + argument2, + argument3); + +- In function invocations, no space in between the function name and + the opening parenthesis. Example:: + + // Good: + SerialUSB.println("Hello, world!"); + + // BAD! + SerialUSB.println ("Hello, world!"); + +- Don't indent C code within a conditionally-compiled ``extern "C"`` + block. Example:: + + // Good: + #ifdef __cplusplus + extern "C"{ + #endif + + void some_c_function(void); + + #ifdef __cplusplus + } // extern "C" + #endif + + // BAD! + #ifdef __cplusplus + extern "C"{ + #endif + + void some_c_function(void); + + #ifdef __cplusplus + } // extern "C" + #endif + + Emacs does the "bad" behavior by default, which can be very + annoying. You can turn this off with :: + + (defun c-mode-inextern-lang-hook () + (setcdr (assq 'inextern-lang c-offsets-alist) '-)) + (add-hook 'c-mode-hook c-mode-inextern-lang-hook) + +Comments +-------- + +.. highlight:: c++ + +- Multi-line comments are pretty flexible. Any of these is fine:: + + /* Comment starts here. + * Continued lines have a '*' before them. + * The comment can end after the last line. + */ + + /* Comment starts here. + * The comment can end on the same line. */ + + /* + * You can also place a newline after the opening "/*". + */ + +- Doxygen comments are multi-line comments that begin with ``/**`` + instead. + +- Single-line comments are up to you. + +Braces +------ + +- Mostly `1TBS + <http://en.wikipedia.org/wiki/Indent_style#Variant:_1TBS>`_. The + only difference is that the opening brace of a function's definition + occurs exactly one space character after the closing parenthesis in + that function's parameter list. Example:: + + void func(void) { + ... + } + +Naming conventions +------------------ + +We'll handle the usual casing/underscore debate as follows. + +- First, ``Dont_Mix_Like_This``, because ``It_Looks_Really_Ugly``, ok? + [There's been some debate about this, and some exceptions are + already grandfathered in, so in order to settle it, let's call this + a "recommendation" instead of "requirement".] + +- Variables: Use underscores to separate words in C identifiers:: + + int some_example_name; + + User-facing C++ variables should be camel cased + (``thisIsAnExample``, ``boardPWMPins``, etc.), for consistency with + the Arduino style. It's probably a good idea for you to case + non-user facing C++ variables in the C style; this will help + disambiguate what's part of the Wirish API and what's not. + +- Classes: Pascal case. So ``ThisIsAClassName``, but ``thisIsNot``, + ``this_is_not``, and ``Dont_You_DareTryANYTHING_STUPID``. + +- Functions: C functions are all lowercase, and words are separated by + underscores. C++ method names are camel cased. + +- Structs: Usually like variables (``adc_dev``, ``adc_reg_map``, + etc.), but it's not crucial. Don't feel obliged to put ``_t`` at + the end of the type name; we don't. + +- Macros and constants: all caps, separated by underscores. C++ + variables with the ``const`` qualifier generally aren't considered + "constants" for the purposes of this rule; i.e., they are cased + according to the rules for variables. We make an exception for + ``PIN_MAP``, because it's the central Wirish data structure. + +- foo.h gets ``#ifdef``\ 'ed to ``_FOO_H_``. + +- Acronyms: The case of letters in an acronym is determined by the + case of the first letter in the acronym, which is determined by + following the above rules. Examples:: + + // Good: + void usb_func() { ... } + void frob_usb_disc() { ... } + class SomethingUSB { + void usbInit(); + void initUSB(); + }; + + // BAD: + class BadUsb { ... }; // say "GoodUSB" instead + void swizzle_USB_disc() { ... } // say "swizzle_usb_disc" instead + +Documentation +------------- + +- Doxygen comments on every user-facing function and type. + Additionally, individually document the fields and enumerator values + of nontrivial user-facing structs and enums. See any register map + type's definition for an example. + +- For libmaple proper, you don't need comments for each register bit + definition, since that's just repeating information better obtained + by reading ST RM0008. + +- Doxygen comments generally only belong on types, functions, + etc. that are part of the public user-facing API. This generally + means that if there's ReST documentation for it under libmaple's + ``docs/source/``, it needs Doxygen comments, and that ReST should + use Breathe to pull that Doxygen comment out. (For more information + on this, see libmaple file ``docs/README``). + + There are some exceptions to this rule since Breathe isn't totally + mature yet and Sphinx's C++ domain is still in flux. In these + cases, document the code "manually" in ReST. + + This should be avoided if at all possible, since it creates a + maintenance burden of documenting things in two places at once, and + makes it easier for documentation to go stale. + + If you do have to document something manually, put a comment in the + source file informing future maintainers about it, so they'll pay + extra attention when making changes. + +- When adding peripheral support, it would be nice if you put + longer-form comments into the libmaple ``notes/`` directory, with a + comment in the corresponding .h file referring to it. See the + :ref:`dac.h <libmaple-dac>` source for an example. + + This lets us keep the source files relatively free of "introductory" + material, while allowing new readers a convenient starting point. + These longer-form notes also have a habit of turning into official, + user-facing documentation. + +- **For libmaple proper**, the convention is to document any + user-facing function at the point where it is defined. In + particular, this means you should document an externally-linked + function defined in a .c file in that .c file, not in the header + file where it is declared to the user. + + **For Wirish**, the convention is to put the documentation in the + header file where the function is declared. + +General Formatting +------------------ + +.. highlight:: scheme + +- Keep it 80-column clean. + + Emacs users: this means that the largest column number is 79. You + should turn on column number mode to help you out:: + + (column-number-mode 1) + + You can get more help from `lineker-mode + <http://www.helsinki.fi/~sjpaavol/programs/lineker.el>`_. Just put + lineker.el somewhere in your load-path, and:: + + (require 'lineker) + (dolist (hook '(c-mode-hook c++-mode-hook)) + (add-hook hook (lambda () (lineker-mode 1)))) + +.. highlight:: cpp + +Language Features +----------------- + +In libmaple proper, aim for C99 compatibility. Some GCC extensions +are OK, but `don't get crazy <http://www.youtube.com/watch?v=jZkdcYlOn5M>`_. + +Explicitly approved GCC extensions: + + * `asm volatile <http://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html>`_ + + * `Nested functions <http://gcc.gnu.org/onlinedocs/gcc/Nested-Functions.html>`_ + +In Wirish, generally be very conservative when using C++ features that +aren't part of C. We are forced to use C++ for Arduino compatibility +(and the general Arduino style of conflating objects and libraries), +but it's an angry beast, and we don't want to provoke it. **The +mantra is "C with classes"**. + +Explicitly approved C++ features: + + * Initializers that aren't constant; e.g. the ``gpio_dev*`` values + in a ``PIN_MAP``. + + * Default arguments: e.g., the timeout argument in + :ref:`lang-waitforbuttonpress`. + +Explicitly forbidden C++ features: + + * Templates + +Conditionally allowed C++ features: + + * Operator overloading: Never allowed when it's just for style. + Probably fine when you're implementing a class that models a + mathematical structure, and you'd like to implement + e.g. ``operator+()``. + diff --git a/docs/source/libmaple/contributing.rst b/docs/source/libmaple/contributing.rst new file mode 100644 index 0000000..724605b --- /dev/null +++ b/docs/source/libmaple/contributing.rst @@ -0,0 +1,113 @@ +.. _libmaple-contributing: + +Contributing to libmaple +======================== + +First of all, thanks! Community contributions are what makes open +source great. + +If your patch is minor (you've found a typo, you've added a new +function, etc.), feel free to just make a `forum post +<http://forums.leaflabs.com>`_ describing your changes. + +If your changes are larger (you wrote a new library, you added support +for a new peripheral, etc.), we'd prefer you submit a pull request on +Github or send us a nicely-formatted patch via email. + +.. contents:: Contents + :local: + +.. _libmaple-faq-patches-preparing: + +Preparing Your Patch +-------------------- + +Before submitting a patch, please make sure it complies with the +:ref:`coding standard <libmaple-coding-standard>`. Consistent style throughout +the source tree is an important implementation objective for us, and a +patch that doesn't comply with the coding standard we've set forth is +likely to be sent back until it follows the standard. + +We would prefer if you release each new file you submit under the `MIT +license <http://www.opensource.org/licenses/mit-license.php>`_. See +e.g. `bkp.h +<https://github.com/leaflabs/libmaple/blob/master/libmaple/bkp.h#L1>`_ +for an example, and the coding standard for more details. Code +released under the `Lesser GPL +<http://www.gnu.org/copyleft/lesser.html>`_ may be accepted for +Wirish, but will almost certainly be rejected for libmaple proper. We +will not accept patches released under the `GPL +<http://www.gnu.org/licenses/gpl.html>`_. + +**We're not against the GPL**! It just doesn't suit our purposes for +libmaple. If you're interested in a GPLed library for ST +microcontrollers, check out `libopenstm32 +<http://www.hermann-uwe.de/blog/libopenstm32-a-free-software-firmware-library-for-stm32-arm-cortex-m3-microcontrollers>`_. +Also note that :ref:`libraries <libraries>` released under the GPL are +fine, we just don't want any core libmaple or Wirish code to be GPLed. + +.. _libmaple-faq-patches-github: + +Submitting Via Github Pull Request (Preferred) +---------------------------------------------- + +The most convenient way for you to contribute patches is to submit a +pull request on `Github <https://github.com>`_. Github provides +excellent code review interfaces, which will make it easy for us at +LeafLabs to communicate with you (and each other) about your patch. +It also makes it easy for us to merge your patch into the libmaple +source tree when the time comes. + +The steps to submit a pull request are as follows: + +1. If you don't already have one, get a `Github account + <https://github.com/plans>`_ (free). + +2. Fork libmaple, then clone your fork to the computer you code on. + Github provides detailed instructions on `forking and cloning a + repository <http://help.github.com/fork-a-repo/>`_. + +3. Push your commits to your Github libmaple fork (see instructions + linked in Step 2 for a step-by-step walkthrough on how to do this). + +4. `Submit a pull request <http://help.github.com/pull-requests/>`_ to + the LeafLabs version of libmaple. + +.. _libmaple-faq-patches-email: + +Submitting Via Email +-------------------- + +If you're unfamiliar with Git or would prefer not to use Github, you +can always send us a patch via email at info@leaflabs.com. We'd love +it if you used the `Linux kernel patch format +<http://linux.yyz.us/patch-format.html>`_, but please at least include +the following information in your email: + +1. How you generated your patch (arguments to ``diff``, etc.) + +2. What git branch/commit or libmaple version your patch applies to + +3. A one-line summary of your changes, along with any other details + you think we should know. + +4. A sign-off line certifying your `developer certificate of origin + <http://elinux.org/Developer_Certificate_Of_Origin>`_. + +That said, we'd really prefer a pull request. If you'd like to learn +more about Git, we recommend the following resources: + +* `The Git Community Book <http://book.git-scm.com/index.html>`_: A + collaboratively edited book on Git. + +* `Pro Git <http://progit.org/book/>`_: despite its title, this is a + fairly beginner-friendly source of information. + +* `Understanding Git Conceptually + <http://www.eecs.harvard.edu/~cduan/technical/git/>`_: a good, + introductory tutorial on Git's fundamental concepts. + +* `Git for Computer Scientists + <http://eagain.net/articles/git-for-computer-scientists/>`_: if + you're comfortable with directed acyclic graphs, this resource + explains Git's functionality in graph-theoretic terms. diff --git a/docs/source/libmaple/overview.rst b/docs/source/libmaple/overview.rst new file mode 100644 index 0000000..9bce564 --- /dev/null +++ b/docs/source/libmaple/overview.rst @@ -0,0 +1,342 @@ +.. highlight:: c + +.. _libmaple-overview: + +Overview +======== + +This page is a general overview of the low-level aspects of libmaple +proper. It provides a general perspective of the library's goals and +design. Examples are given from the libmaple sources. + +.. contents:: Contents + :local: + +Design Goals +------------ + +The central goal of the libmaple project is to provide a pleasant, +consistent set of interfaces for dealing with the various peripherals +on the STM32 line. + +Let's start with the basics. If you're interested in low-level details +on the STM32, then you're going to spend a lot of quality time wading +through `ST RM0008 +<http://www.st.com/stonline/products/literature/rm/13902.pdf>`_. +RM0008 is the single most important tool in your toolbox. It is the +authoritative documentation for the capabilities and low-level +programming interfaces of ST's line of ARM Cortex M3 microcontrollers. + +Perhaps you haven't read it in detail, but maybe you've at least +thumbed through a few of the sections, trying to gain some +understanding of what's going on. If you've done that (and if you +haven't, just take our word for it), then you know that underneath the +covers, *everything* is controlled by messing with bits in the +seemingly endless collections of registers specific to every +peripheral. The `USARTs <http://leaflabs.com/docs/usart.html>`_ have +data registers; (some of the) the `timers +<http://leaflabs.com/docs/timers.html>`_ have capture/compare +registers, the `GPIOs <http://leaflabs.com/docs/gpio.html>`_ have +output data registers, etc. + +For the most part, Wirish does everything it can to hide this truth +from you. That's because when you really just want to get your robot +to fly, your LEDs to blink, or your `FM synthesizer +<https://github.com/Ixox/preen>`_ to, well, `synthesize +<http://xhosxe.free.fr/IxoxFMSynth.mp3>`_, you probably couldn't care +less about messing with registers. + +That's fine! In fact, it's our explicit goal for Wirish to be good +enough that most people never need to know libmaple proper even +exists. We want to make programming our boards as easy as possible, +after all. But the day may come when you want to add a library for an +as-yet unsupported peripheral, or you want to do something we didn't +anticipate, or you'd like to squeeze a little more speed out of a +critical section in your program. Or maybe you're just curious! + +If anything in the above paragraph describes you, then you'll find +that you need a way to translate your knowledge of RM0008 into +software. We imagine (if you're anything like us) you want to spend +the least amount of time you possibly can doing that +translation. Ideally, once you've finished your design, you want some +way to start reading and writing code right away, without having to +bushwhack your way through a thicket of clunky APIs. + +The central abstractions we've chosen to accomplish the above goals +are *register maps* and *devices*. Register maps are just structs +which encapsulate the layout of the IO-mapped memory regions +corresponding to a peripheral's registers. Devices encapsulate a +peripheral's register map as well as any other necessary information +needed to operate on it. Peripheral support routines generally +operate on devices rather than register maps. + +Devices +------- + +At the highest level, you'll be dealing with *devices*, where a +"device" is a general term for any particular piece of hardware you +might encounter. So, for example, an analog to digital converter is a +device. So is a USART. So is a GPIO port. In this section, we'll +consider some hypothetical "xxx" device. + +The first thing you need to know is that the header file for dealing +with xxx devices is, naturally enough, called ``xxx.h``. So if you +want to interface with the :ref:`ADCs <adc>`, just ``#include +"adc.h"``. + +Inside of ``xxx.h``, there will be a declaration for a ``struct +xxx_dev`` type. This type encapsulates all of the information we keep +track of for that xxx. So, for example, in ``adc.h``, there's a +``struct adc_dev``:: + + /** ADC device type. */ + typedef struct adc_dev { + adc_reg_map *regs; /**< Register map */ + rcc_clk_id clk_id; /**< RCC clock information */ + } adc_dev; + +The ADCs aren't particularly complicated. All we keep track of for an +ADC device is a pointer to its register map (which keeps track of all +of its registers' bits; see :ref:`below <libmaple-overview-regmaps>` +for more details), and an identifying piece of information which tells +the RCC (reset and clock control) interface how to turn the ADC on and +reset its registers to their default values. + +The timers on the STM32 line are more involved than the ADCs, so a +``timer_dev`` has to keep track of a bit more information:: + + /** Timer device type */ + typedef struct timer_dev { + timer_reg_map_union regs; + rcc_clk_id clk_id; + timer_type type; + voidFuncPtr handlers[]; + } timer_dev; + +However, as you can see, both ADC and timer devices are named +according to a single scheme, and store similar information. + +``xxx.h`` will also declare pointers to the actual devices you need to +deal with, called ``XXX1``, ``XXX2``, etc. (or just ``XXX``, if +there's only one) [#fgpio]_. For instance, on the Maple's +microcontroller (the STM32F103RBT6), there are two ADCs. +Consequently, in ``adc.h``, there are declarations for dealing with +ADC devices one and two:: + + extern const adc_dev *ADC1; + extern const adc_dev *ADC2; + +In general, each device needs to be initialized before it can be used. +libmaple provides this initialization routine for each peripheral +``xxx``; its name is ``xxx_init()``. These initialization routines +turn on the clock to a device, and restore its register values to +their default settings. Here are a few examples:: + + /* From dma.h */ + void dma_init(dma_dev *dev); + + /* From gpio.h */ + void gpio_init(gpio_dev *dev); + void gpio_init_all(void); + +Note that, sometimes, there will be an additional initialization +routine for all available peripherals of a certain kind. + +Many peripherals also need additional configuration before they can be +used. These functions are usually called something along the lines of +``xxx_enable()``, and often take additional arguments which specify a +particular configuration for the peripheral. Some examples:: + + /* From usart.h */ + void usart_enable(usart_dev *dev); + + /* From i2c.h */ + void i2c_master_enable(i2c_dev *dev, uint32 flags); + +After you've initialized, and potentially enabled, your peripheral, it +is now time to begin using it. The file ``xxx.h`` contains other +convenience functions for dealing with xxx devices. For instance, +here are a few from ``adc.h``:: + + void adc_set_sample_rate(const adc_dev *dev, adc_smp_rate smp_rate); + uint32 adc_read(const adc_dev *dev, uint8 channel); + +We aim to enable libmaple's users to interact with peripherals through +devices as much as possible, rather than having to break the +abstraction and consider individual registers. However, there will +always be a need for low-level access. To allow for that, libmaple +provides *register maps* as a consistent set of names and abstractions +for dealing with registers and their bits. + +.. _libmaple-overview-regmaps: + +Register Maps +------------- + +A *register map* is just a C struct which names and provides access to +a peripheral's registers. These registers are usually mapped to +contiguous regions of memory (though at times unusable or reserved +regions exist between a peripheral's registers). Here's an example +register map, from ``dac.h`` (``__io`` is just libmaple's way of +saying ``volatile`` when referring to register values):: + + /** DAC register map. */ + typedef struct dac_reg_map { + __io uint32 CR; /**< Control register */ + __io uint32 SWTRIGR; /**< Software trigger register */ + __io uint32 DHR12R1; /**< Channel 1 12-bit right-aligned data + holding register */ + __io uint32 DHR12L1; /**< Channel 1 12-bit left-aligned data + holding register */ + __io uint32 DHR8R1; /**< Channel 1 8-bit left-aligned data + holding register */ + __io uint32 DHR12R2; /**< Channel 2 12-bit right-aligned data + holding register */ + __io uint32 DHR12L2; /**< Channel 2 12-bit left-aligned data + holding register */ + __io uint32 DHR8R2; /**< Channel 2 8-bit left-aligned data + holding register */ + __io uint32 DHR12RD; /**< Dual DAC 12-bit right-aligned data + holding register */ + __io uint32 DHR12LD; /**< Dual DAC 12-bit left-aligned data + holding register */ + __io uint32 DHR8RD; /**< Dual DAC 8-bit left-aligned data holding + register */ + __io uint32 DOR1; /**< Channel 1 data output register */ + __io uint32 DOR2; /**< Channel 2 data output register */ + } dac_reg_map; + + +There are two things to notice here. First, if RM0008 names a +register ``DAC_FOO``, then ``dac_reg_map`` has a field named ``FOO``. +So, the Channel 1 12-bit right-aligned data register (RM0008: +DAC_DHR12R1) is the ``DHR12R1`` field in a ``dac_reg_map``. Second, +if RM0008 describes a register as "Foo bar register", the +documentation for the corresponding field has the same description. +This consistency makes it easy to search for a particular register, +and, if you see one used in a source file, to feel sure about what's +going on just based on its name. + +So let's say you've included ``xxx.h``, and you want to mess with some +particular register. What's the name of the ``xxx_reg_map`` variable +you want? That depends on if there's more than one xxx or not. If +there's only one xxx, then libmaple guarantees there will be a +``#define`` that looks like like this:: + + #define XXX_BASE ((xxx_reg_map*)0xDEADBEEF) + +That is, you're guaranteed there will be a pointer to the (only) +``xxx_reg_map`` you want, and it will be called +``XXX_BASE``. (``0xDEADBEEF`` is the register map's *base address*, or +the fixed location in memory where the register map begins). Here's a +concrete example from ``dac.h``:: + + #define DAC_BASE ((dac_reg_map*)0x40007400) + +How can you use these? This is perhaps best explained by example. + +* In order to write 2048 to the channel 1 12-bit left-aligned data + holding register (RM0008: DAC_DHR12L1), you could write:: + + DAC_BASE->DHR12L1 = 2048; + +* In order to read the DAC control register, you could write:: + + uint32 cr = DAC_BASE->CR; + +The microcontroller takes care of converting reads and writes from a +register's IO-mapped memory regions into reads and writes to the +corresponding hardware registers. + +That covers the case where there's a single xxx peripheral. If +there's more than one (say, if there are *n*), then ``xxx.h`` provides +the following:: + + #define XXX1_BASE ((xxx_reg_map*)0xDEADBEEF) + #define XXX2_BASE ((xxx_reg_map*)0xF00DF00D) + ... + #define XXXn_BASE ((xxx_reg_map*)0x13AF1AB5) + +Here's a concrete example from ``adc.h``:: + + /** ADC1 register map base pointer. */ + #define ADC1_BASE ((adc_reg_map*)0x40012400) + /** ADC2 register map base pointer. */ + #define ADC2_BASE ((adc_reg_map*)0x40012800) + /** ADC3 register map base pointer. */ + #define ADC3_BASE ((adc_reg_map*)0x40013C00) + +In order to read from the ADC1's regular data register (where the +results of ADC conversion are stored), you might write:: + + uint32 converted_result = ADC1->DR; + +Register Bit Definitions +------------------------ + +In ``xxx.h``, there will also be a variety of #defines for dealing +with interesting bits in the xxx registers, called *register bit +definitions*. These are named according to the scheme +``XXX_REG_FIELD``, where "``REG``" refers to the register, and +"``FIELD``" refers to the bit or bits in ``REG`` that are special. + +.. TODO image of the bit layout of a DMA_CCR register + +Again, this is probably best explained by example. Each Direct Memory +Access (DMA) controller's register map has a certain number of channel +configuration registers (RM0008: DMA_CCRx). In each of these channel +configuration registers, bit 14 is called the ``MEM2MEM`` bit, and +bits 13 and 12 are the priority level (``PL``) bits. Here are the +register bit definitions for those fields:: + + /* From dma.h */ + + #define DMA_CCR_MEM2MEM_BIT 14 + #define DMA_CCR_MEM2MEM BIT(DMA_CCR_MEM2MEM_BIT) + #define DMA_CCR_PL (0x3 << 12) + #define DMA_CCR_PL_LOW (0x0 << 12) + #define DMA_CCR_PL_MEDIUM (0x1 << 12) + #define DMA_CCR_PL_HIGH (0x2 << 12) + #define DMA_CCR_PL_VERY_HIGH (0x3 << 12) + +Thus, to check if the ``MEM2MEM`` bit is set in DMA controller 1's +channel configuration register 2 (RM0008: DMA_CCR2), you can write:: + + if (DMA1_BASE->CCR2 & DMA_CCR_MEM2MEM) { + /* MEM2MEM is set */ + } + +Certain register values occupy multiple bits. For example, the +priority level (PL) of a DMA channel is determined by bits 13 and 12 +of the corresponding channel configuration register. As shown above, +libmaple provides several register bit definitions for masking out the +individual PL bits and determining their meaning. For example, to +check the priority level of a DMA transfer, you can write:: + + switch (DMA1_BASE->CCR2 & DMA_CCR_PL) { + case DMA_CCR_PL_LOW: + /* handle low priority case */ + case DMA_CCR_PL_MEDIUM: + /* handle medium priority case */ + case DMA_CCR_PL_HIGH: + /* handle high priority case */ + case DMA_CCR_PL_VERY_HIGH: + /* handle very high priority case */ + } + +Of course, before doing that, you should check to make sure there's +not already a device-level function for performing the same task! + +What Next? +---------- + +After you've read this page, you can proceed to the :ref:`libmaple API +listing <libmaple-apis>`. From there, you can read documentation and +follow links to the current source code for those files on `libmaple's +Github page <https://github.com/leaflabs/libmaple>`_. + +.. rubric:: Footnotes + +.. [#fgpio] For consistency with RM0008, GPIO ports are given letters + instead of numbers (``GPIOA`` and ``GPIOB`` instead of + ``GPIO1`` and ``GPIO2``, etc.). diff --git a/docs/source/libraries.rst b/docs/source/libraries.rst index 7623963..44a72f7 100644 --- a/docs/source/libraries.rst +++ b/docs/source/libraries.rst @@ -10,13 +10,13 @@ .. Note: if you port an Arduino library and document it here, be sure .. to update compatibility.rst to reflect that fact. -This page briefly summarizes the Arduino libraries that have been -ported to Maple. You can use a library from within a sketch by going -to Sketch > Import Library... from within the IDE, then choosing the -library you want. +This page lists the extra libraries that are part of the :ref:`Maple +IDE <ide>` (along with the rest of :ref:`libmaple <libmaple>`). You +can use a library from within a sketch by going to Sketch > Import +Library... from within the IDE, then choosing the library you want. -Any incompatibilities between the Maple and Arduino versions are noted -in the description of the library. +Any incompatibilities between the Maple and Arduino versions of a +library are noted in the library's documentation. .. contents:: Contents :local: @@ -25,70 +25,47 @@ in the description of the library. :hidden: libs/servo.rst + libs/wire.rst -.. _libraries-servo: - -Servo ------ - -The Servo library is provided for convenient control of RC -servomotors. For more information, see the :ref:`Servo <libs-servo>` -reference. +.. admonition:: **Looking for Something Else?** -**Compatibility Note** + - See the :ref:`language` for information on the core functions + used for programming a Maple board. -The Servo class provides a public interface identical to the Arduino -version's documented functionality (as of Arduino 0021), so in most -cases, this library will be a drop-in replacement. + - If you're looking for something from the C standard library (like + ``atoi()``, for instance): the :ref:`CodeSourcery GCC compiler + <arm-gcc>` used to compile your programs is configured to link + against `newlib <http://sourceware.org/newlib/>`_, and allows the + use of any of its header files. However, dynamic memory allocation + (``malloc()``, etc.) is not available. -However, there are some differences, essentially at the level of -implementation details. + - If you're looking for low-level hardware support libraries, see + the :ref:`libmaple Reference <libmaple>`. -The major difference is that while the Arduino implementation drives -the servos with "bit-banged" :ref:`PWM <pwm>`, the Maple -implementation uses :ref:`timers <timers>` to drive the PWM directly. - -Consequently, **the Maple implementation only allows Servo instances -to** :ref:`attach <libs-servo-attach>` **to pins that support PWM**. - -To determine if a pin supports PWM (15 Maple pins do), you can either -check if "PWM" appears next to its number on the Maple silkscreen, or -consult the :ref:`pwmWrite() <lang-pwmwrite>` documentation. - -RC Servos expect a pulse approximately every 20ms. In the Maple -implementation, :ref:`periods <lang-hardwaretimer-setperiod>` are set -for entire timers, rather than individual channels. Thus, -``attach()``\ ing a Servo to a pin can interfere with other pins -associated with the same timer\ [#fard-servo]_. +.. _libraries-servo: -Because of this, we recommend connecting multiple servomotors to pins -which share a timer, in order to keep as many timers free for other -purposes as possible. Consult the :ref:`table provided in the timers -reference <timers-pin-channel-map>` to match up pins and timer -channels. +Servo +----- -Another difference: although it is not publicly documented to do so, -the Arduino implementation of `attach() -<http://arduino.cc/en/Reference/ServoAttach>`_ returns the timer -channel associated with the newly-attached pin, or 0 on failure (as of -Arduino 0021). The Maple implementation returns true on success, and -false on failure (and this is its documented behavior). +The :ref:`Servo <libs-servo>` library is provided for convenient +control of RC servomotors. .. _libraries-liquid-crystal: LiquidCrystal ------------- -.. TODO 0.0.10 make our own LiquidCrystal docs +.. TODO [0.1.0] LiquidCrystal docs under libs/liquidcrystal.rst The LiquidCrystal library allows Maple to control LCD screens. For more information, see the `Arduino LiquidCrystal documentation <http://www.arduino.cc/en/Reference/LiquidCrystal>`_. -**Compatibility Note** +**Arduino Compatibility** At this time, no incompatibilities between the Maple and Arduino -versions are known. Any observed differences should be considered +versions are known (although the Maple version should perform +significantly faster). Any observed differences should be considered bugs, and reported on the forums. .. _libraries-wire: @@ -96,99 +73,8 @@ bugs, and reported on the forums. Wire ---- -We provide a soft (bit-banged) implementation of the `Wire I2C library -<http://arduino.cc/en/Reference/WireBegin>`_. - -**Compatibility Note** - -This implementation is synchronous, and thus supports only a subset of -the full Wire interface (however, the functionality which is supported -is fully compatible with Arduino). For now, please use the function -reference which follows when developing projects using our -implementation. - -Please note that the current implementation only supports master mode -using a bit-banged (software) protocol. Future enhancements will use -the hardware i2c peripheral on the stm32 as well as the DMA for -performance. Support for slave, smBUS, and multimaster modes are also -slated for inclusion in the enhanced Wire port. - -.. TODO 0.0.10 Wire docs in the cpp domain in own page under /libs/ - -Wire Function Reference -^^^^^^^^^^^^^^^^^^^^^^^ - -``Wire.begin()`` - Joins the i2c bus as master, using pin 20 as SDA and pin 21 as SCL - (this is compatible with the pin settings on the Arduino Mega). - -``Wire.begin(sda, scl)`` - Like ``Wire.begin()``, but with the given pins as SDA and - SCL. - -``Wire.beginTransmission(slave_address)`` - Set up a transmission to a slave device with the given (7-bit) - address. Bytes subsequently queued for transmission (using - ``Wire.send``) will be sent to ``slave_address`` when ``void - Wire.endTransmission()`` is called. - -``void Wire.send(byte)`` - Queues the given byte (``uint8`` or ``int``) to the slave address - previously specified by a call to ``Wire.beginTransmission``. At - most 32 bytes may be queued in a single transmission. - -``Wire.send(string)`` - Queues a given string (``char*``) for transmission. The characters - of the string are individually queued for transmission as - bytes. At most 32 bytes may be queued in a single transmission. - -``Wire.send(buffer, length)`` - Queues a byte buffer ``buffer`` (``uint8*`` or ``int*``), with - ``length`` elements, for transmission. At most 32 bytes may be - queued in a single transmission. - -``Wire.endTransmission()`` - Ends a transmission (begun by ``Wire.beginTransmission(uint8)``), - and actually sends the bytes queued by calls to Wire.send. - - The return value is one of the following status codes: - - * ``SUCCESS``: All bytes were transmitted successfully. - - * ``EDATA``: More than 32 bytes were queued for transmission. No - bytes are actually sent when this happens. - - * ``ENACKADDR``: Did not receive ACK on transmit of address. No - bytes are actually sent when this happens. - - * ``ENACKTRNS``: Did not receive ACK during transmit of data. Some - bytes may have been sent when this happens; however, the - transmission is aborted after the first byte of data which is - not ACKed by the slave device. - - * ``EOTHER``: Other error occurred. - -``Wire.requestFrom(address, num_bytes)`` - Requests ``num_bytes`` bytes from 7-bit slave address - address. Returns the actual number of bytes read. These bytes may - subsequently be read one at a time using ``Wire.receive()``. - - Note: if ``num_bytes`` exceeds the size of the transmit/receive - buffer (currently 32), it will be truncated to 32. - -``Wire.receive()`` - Get and return the next byte read during the previous call to - ``Wire.requestFrom(uint8, int)``. You can check how many bytes are - left to read using ``uint8 Wire.available()``. - -``Wire.available()`` - Returns the number of bytes which are still available for reading - (with ``Wire.receive()``) from the last call to - ``Wire.requestFrom(uint8, int)``. - -.. rubric:: Footnotes +.. FIXME [0.1.0] Update with hard Wire implementation info -.. [#fard-servo] The Arduino implementation also captures timer - channels in groups as more Servo objects are attached, but the - details of which channels have their periods reset when are - slightly different. +We currently provide a soft (bit-banged) implementation of the +:ref:`Wire <libs-wire>` I2C library. A hardware version is planned +for Maple IDE release 0.1.0. diff --git a/docs/source/libs/servo.rst b/docs/source/libs/servo.rst index f92fd91..891f151 100644 --- a/docs/source/libs/servo.rst +++ b/docs/source/libs/servo.rst @@ -2,9 +2,10 @@ .. _libs-servo: -======= - Servo -======= +Servo +===== + +.. FIXME [0.0.10] this is out of date This documents the Servo library for controlling RC servomotors. It is implemented as a thin layer over the built-in :ref:`timer @@ -14,7 +15,8 @@ You can use this library in the :ref:`IDE <ide>` by choosing the Servo item under the Sketch > Import Library... menu. If you are using the :ref:`Unix toolchain <unix-toolchain>`, the -library is located in ``$LIB_MAPLE_HOME/libraries/Servo/``. +library is located in the ``/libraries/Servo/`` :ref:`libmaple` +directory. Servo Class Reference --------------------- @@ -106,3 +108,54 @@ servomotor attached to pin 9, you could write :: microseconds. This will be clamped to lie in the [``min``, ``max``\ ] pulse width range set during :ref:`attach() <libs-servo-attach>`. + +Arduino Compatibility +--------------------- + +The Servo class provides a public interface identical to the Arduino +version's documented functionality (as of Arduino 0021), so in most +cases, this library will be a drop-in replacement. + +However, there are some differences, essentially at the level of +implementation details. + +The major difference is that while the Arduino implementation drives +the servos with "bit-banged" :ref:`PWM <pwm>`, the Maple +implementation uses :ref:`timers <timers>` to drive the PWM directly. + +Consequently, **the Maple implementation only allows Servo instances +to** :ref:`attach <libs-servo-attach>` **to pins that support PWM**. + +To determine if a pin supports PWM (15 Maple pins do), you can either +check if "PWM" appears next to its number on the Maple silkscreen, or +consult the :ref:`pwmWrite() <lang-pwmwrite>` documentation. + +RC Servos expect a pulse approximately every 20ms. In the Maple +implementation, :ref:`periods <lang-hardwaretimer-setperiod>` are set +for entire timers, rather than individual channels. Thus, +``attach()``\ ing a Servo to a pin can interfere with other pins +associated with the same timer\ [#fard-servo]_. + +Because of this, we recommend connecting multiple servomotors to pins +which share a timer, in order to keep as many timers free for other +purposes as possible. Consult your board's :ref:`Timer Pin Map +<gpio-pin-maps>` to match up pins and timer channels. + +Another difference: although it is not publicly documented to do so, +the Arduino implementation of `attach() +<http://arduino.cc/en/Reference/ServoAttach>`_ returns the timer +channel associated with the newly-attached pin, or 0 on failure (as of +Arduino 0021). The Maple implementation returns :ref:`true +<lang-constants-true>` on success, and :ref:`false +<lang-constants-false>` on failure (and this is its documented +behavior). + +We currently provide a soft (bit-banged) implementation of the +:ref:`Wire <libs-wire>` I2C library. + +.. rubric:: Footnotes + +.. [#fard-servo] The Arduino implementation also captures timer + channels in groups as more Servo objects are attached, but the + details of which channels have their periods reset when are + slightly different. diff --git a/docs/source/libs/wire.rst b/docs/source/libs/wire.rst new file mode 100644 index 0000000..2c5bed9 --- /dev/null +++ b/docs/source/libs/wire.rst @@ -0,0 +1,104 @@ +.. highlight:: cpp + +.. _libs-wire: + +Wire +==== + +.. TODO [0.1.0] Format this correctly, using Breathe + +This page documents the Wire library for the :ref:`i2c` protocol. You +can use this library in the :ref:`Maple IDE <ide>` by choosing the +Wire item under the Sketch > Import Library... menu. + +If you are using the :ref:`Unix toolchain <unix-toolchain>`, the +library is located in the ``/libraries/Wire/`` :ref:`libmaple` +directory. + +Wire Function Reference +----------------------- + +``Wire.begin()`` + Joins the i2c bus as master, using pin 20 as SDA and pin 21 as SCL + (this is compatible with the pin settings on the Arduino Mega). + +``Wire.begin(sda, scl)`` + Like ``Wire.begin()``, but with the given pins as SDA and + SCL. + +``Wire.beginTransmission(slave_address)`` + Set up a transmission to a slave device with the given (7-bit) + address. Bytes subsequently queued for transmission (using + ``Wire.send``) will be sent to ``slave_address`` when ``void + Wire.endTransmission()`` is called. + +``void Wire.send(byte)`` + Queues the given byte (``uint8`` or ``int``) to the slave address + previously specified by a call to ``Wire.beginTransmission``. At + most 32 bytes may be queued in a single transmission. + +``Wire.send(string)`` + Queues a given string (``char*``) for transmission. The characters + of the string are individually queued for transmission as + bytes. At most 32 bytes may be queued in a single transmission. + +``Wire.send(buffer, length)`` + Queues a byte buffer ``buffer`` (``uint8*`` or ``int*``), with + ``length`` elements, for transmission. At most 32 bytes may be + queued in a single transmission. + +``Wire.endTransmission()`` + Ends a transmission (begun by ``Wire.beginTransmission(uint8)``), + and actually sends the bytes queued by calls to Wire.send. + + The return value is one of the following status codes: + + * ``SUCCESS``: All bytes were transmitted successfully. + + * ``EDATA``: More than 32 bytes were queued for transmission. No + bytes are actually sent when this happens. + + * ``ENACKADDR``: Did not receive ACK on transmit of address. No + bytes are actually sent when this happens. + + * ``ENACKTRNS``: Did not receive ACK during transmit of data. Some + bytes may have been sent when this happens; however, the + transmission is aborted after the first byte of data which is + not ACKed by the slave device. + + * ``EOTHER``: Other error occurred. + +``Wire.requestFrom(address, num_bytes)`` + Requests ``num_bytes`` bytes from 7-bit slave address + address. Returns the actual number of bytes read. These bytes may + subsequently be read one at a time using ``Wire.receive()``. + + Note: if ``num_bytes`` exceeds the size of the transmit/receive + buffer (currently 32), it will be truncated to 32. + +``Wire.receive()`` + Get and return the next byte read during the previous call to + ``Wire.requestFrom(uint8, int)``. You can check how many bytes are + left to read using ``uint8 Wire.available()``. + +``Wire.available()`` + Returns the number of bytes which are still available for reading + (with ``Wire.receive()``) from the last call to + ``Wire.requestFrom(uint8, int)``. + +Arduino Compatibility +--------------------- + +.. FIXME [0.1.0] Replace this section when i2c Wire wrapper is done + +This implementation is synchronous, and thus supports only a subset of +the full Wire interface (however, the functionality which is supported +is fully compatible with Arduino). For now, please use the function +reference which follows when developing projects using our +implementation. + +Please note that the current implementation only supports master mode +using a bit-banged (software) protocol. For now, use of the hardware +:ref:`i2c` peripheral is only available through :ref:`libmaple-i2c`. + + diff --git a/docs/source/maple-ide-install.rst b/docs/source/maple-ide-install.rst index 92d1a96..beba54d 100644 --- a/docs/source/maple-ide-install.rst +++ b/docs/source/maple-ide-install.rst @@ -17,7 +17,8 @@ us directly`_\ ! Download -------- -Choose the correct version for your operating system: +The current release of the Maple IDE is version 0.0.9. Choose the +correct version for your operating system: .. list-table:: :widths: 15 30 50 @@ -28,24 +29,28 @@ Choose the correct version for your operating system: - IDE Package * - Windows - Tested on 32bit Windows XP - - `maple-ide-LATEST-windowsxp32.zip <http://static.leaflabs.com/pub/leaflabs/maple-ide/maple-ide-LATEST-windowsxp32.zip>`_ (about 75mb) + - `maple-ide-0.0.9-windowsxp32.zip <http://static.leaflabs.com/pub/leaflabs/maple-ide/maple-ide-0.0.9-windowsxp32.zip>`_ (about 75mb) * - Linux - Tested on Ubuntu 9.10 (64bit) and 10.04 (32bit) - - `maple-ide-LATEST-linux32.tgz <http://static.leaflabs.com/pub/leaflabs/maple-ide/maple-ide-LATEST-linux32.tgz>`_ (about 30mb) + - `maple-ide-0.0.9-linux32.tgz <http://static.leaflabs.com/pub/leaflabs/maple-ide/maple-ide-0.0.9-linux32.tgz>`_ (about 30mb) - `maple-ide-LATEST-linux64.tgz <http://static.leaflabs.com/pub/leaflabs/maple-ide/maple-ide-LATEST-linux64.tgz>`_ (about 30mb) + `maple-ide-0.0.9-linux64.tgz <http://static.leaflabs.com/pub/leaflabs/maple-ide/maple-ide-0.0.9-linux64.tgz>`_ (about 30mb) * - Mac OSX - Tested on Snow Leopard 10.6 (64bit and 32bit) - - `maple-ide-LATEST-macosx-10_6.dmg <http://static.leaflabs.com/pub/leaflabs/maple-ide/maple-ide-LATEST-macosx-10_6.dmg>`_ (about 40mb) + - `maple-ide-0.0.9-macosx-10_6.dmg <http://static.leaflabs.com/pub/leaflabs/maple-ide/maple-ide-0.0.9-macosx-10_6.dmg>`_ (about 40mb) The package bundles together a compiler, an upload utility, a software library, and a simple GUI text editor. All this software is `free and -open <http://fsf.org/>`_; we are grateful to the `Arduino +open <http://www.fsf.org/>`_; we are grateful to the `Arduino <http://arduino.cc/>`_, `CodeSourcery -<http://www.codesourcery.com/>`_, `GNU <http://gnu.org/>`_, and +<http://www.codesourcery.com/>`_, `GNU <http://www.gnu.org/>`_, and `OpenMoko <http://openmoko.com/>`_ developers, as well as many others, who allow us to reuse their software. +**Looking for something older?** `Source archives and binaries +<http://static.leaflabs.com/pub/leaflabs/maple-ide/>`_ are available +for previously-released versions. + Installation ------------ @@ -65,8 +70,8 @@ some drivers. Sorry! changes in Windows 7 mean that you won't be able to install the IDE without disabling driver signing on your computer. We're working on resolving this situation. For now, `users on the forum have - reported some workarounds - <http://forums.leaflabs.com/topic.php?id=73>`_. + reported a workaround + <http://forums.leaflabs.com/topic.php?id=73#post-788>`_. First, install DFU drivers (for uploading code to your Maple) using the following steps. diff --git a/docs/source/maple-quickstart.rst b/docs/source/maple-quickstart.rst index 899f720..c7596ce 100644 --- a/docs/source/maple-quickstart.rst +++ b/docs/source/maple-quickstart.rst @@ -6,7 +6,7 @@ Maple Quickstart Guide ======================== -.. TODO update the images since we changed "to FLASH" -> "to Flash" +.. TODO [0.1.0] Update the images; we've changed "to FLASH" -> "to Flash" You'll need a `Maple board <http://leaflabs.com/store/>`_, a `Mini-B USB cable <http://www.google.com/products?q=mini-b+usb+cable>`_, a @@ -73,12 +73,14 @@ window, and then a confirmation message will appear: Upload that program! -------------------- +.. FIXME [0.1.0 Maple-specific image; add one for Native] + Now it's time to plug in your Maple. Use a Mini-B cable, making sure -that the :ref:`power source jumper <hardware-maple-powering>` is on -the USB header first. We ship the Maple with the power source jumper -configured that way, so you shouldn't have to do anything. For -reference, it should look like this (don't worry if a jumper is -hanging half off of the CHRG header): +that the :ref:`power source jumper <maple-powering>` is on the USB +header first. We ship Maples with the power source jumper configured +that way, so you shouldn't have to do anything. For reference, it +should look like this (don't worry if a jumper is hanging half off of +the CHRG header): .. image:: /_static/img/plugged-in-maple.png :align: center diff --git a/docs/source/pwm.rst b/docs/source/pwm.rst index 1a8f4df..34ad508 100644 --- a/docs/source/pwm.rst +++ b/docs/source/pwm.rst @@ -19,29 +19,14 @@ filtering) generate audio waveforms. 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 <timers>` for more information. - -Note that unlike the Arduino, the Maple does not have PWM -functionality on pin D10; all other pins are :ref:`compatible -<compatibility>`. - -The following table shows which timer can generate which PWM -outputs. See the :ref:`pin mapping table <pin-mapping-mega-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 +.. FIXME [0.1.0] More information about how timer channels drive PWM + +Each PWM output is driven by an output channel 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 +<timers>` for more information. See your board's :ref:`pin mapping +tables <gpio-pin-maps>` to track down the correspondence +between timer channels and GPIO pins. Background ---------- diff --git a/docs/source/specs.rst b/docs/source/specs.rst deleted file mode 100644 index 4972a83..0000000 --- a/docs/source/specs.rst +++ /dev/null @@ -1,29 +0,0 @@ -.. _specs: - -.. _specs-Technical-Specifications: - -================================ -Maple's Technical Specifications -================================ - - * STM32 F103RB: a **32-bit** ARM Cortex M3 microprocessor - * Clock Speed: **72 MHz** - * Operating Voltage: 3.3V - * Input Voltage (recommended): 3.0V-12V - * 39 Digital I/O Pins (:ref:`GPIO <gpio>`) - * 16 Analog Input Pins - * 12-bit **ADC** resolution (:ref:`ADC <adc>`) - * 15 **PWM** pins at 16-bit resolution (:ref:`PWM <pwm>`) - * Dedicated **USB** port for programming and communications (:ref:`USB<usb>`) - * External **JTAG** interface (:ref:`USB <jtag>`) - * **128 Flash** and **20KB SRAM** - * 64 Channel nested vector interrupt handler (including external interrupt on GPIO’s) - * Integrated **SPI** (:ref:`SPI<spi>`) - * Integrated **I2C** (:ref:`I2C<i2c>`) - * 7 Channels of Direct Memory Access (DMA) - * 3 **USART** divices (:ref:`USART <usart>`) - * Four 4-channel Timers (:ref:`Timers <timers>`) - * Supplies up to 800mA @ 3.3v - * Support for low power and sleep modes (<500uA) - * Dimensions are 2.05″x2.1″ - diff --git a/docs/source/spi.rst b/docs/source/spi.rst index ba43eef..dd9f1f5 100644 --- a/docs/source/spi.rst +++ b/docs/source/spi.rst @@ -8,23 +8,23 @@ The Serial Peripheral Interface Bus (SPI) is a serial data transfer protocol useful for interacting with a wide variety of hardware peripherals. -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. - The public libmaple API for managing the SPI ports is the -:ref:`HardwareSpi <lang-hardwarespi>` class. +:ref:`HardwareSPI <lang-hardwarespi>` class. Recommended Reading ------------------- * `Wikipedia Article on Serial Peripheral Interface Bus (SPI) <http://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus>`_ + * `Arduino reference on SPI <http://www.arduino.cc/playground/Code/Spi>`_ + * `Hardcore SPI on Arduino <http://klk64.com/arduino-spi/>`_ by kik64 -* STMicro documentation for STM32F103RB microcontroller: - * `Datasheet <http://www.st.com/stonline/products/literature/ds/13587.pdf>`_ (pdf) - * `Reference Manual <http://www.st.com/stonline/products/literature/rm/13902.pdf>`_ (pdf) +* ST Documentation: + + * Reference Manual `RM0008 + <http://www.st.com/stonline/products/literature/rm/13902.pdf>`_ + (PDF), Chapter 25, "Serial Peripheral Interface" diff --git a/docs/source/timers.rst b/docs/source/timers.rst index 56dd686..9163e69 100644 --- a/docs/source/timers.rst +++ b/docs/source/timers.rst @@ -5,6 +5,8 @@ Timers ====== +.. FIXME [0.0.10] links to systick.h in a few places on this page + 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 @@ -43,164 +45,76 @@ event" interrupt is generated. You can configure the Maple to notify you when this takes place, by registering an interrupt handler, which is a function that will be called when the update event occurs. -libmaple Reference +By default, different compare values only change the relative offsets +between events on a single timer ("phase"). They don't control the +frequency with which they occur. However, a common trick is to +increment the compare value manually in the interrupt handler so that +the event will fire again after the increment period. There can be a +different increment value for each channel, so this trick allows +events to be programmed at 4 different rates on a single timer. Note +that function call overheads mean that the smallest increment rate is +at least a few microseconds. + +Function Reference ------------------ -The libmaple API for interacting with timers is documented at the -:ref:`HardwareTimer reference <lang-hardwaretimer>`. +* :ref:`HardwareTimer <lang-hardwaretimer>` Caveats ------- +Working with timers and interrupts can be tricky; they are a somewhat +"advanced" topic. The following subsections explain some common +problems associated with using timers and timer interrupts. + +In general: start simple, test with :ref:`ASSERT() <language-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, if those other interrupts are important for +your program. + .. _timers-pwm-conflicts: -**PWM Conflicts:** Because PWM functionality on a given pin depends on +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 following table to match up timer -channels and Maple header pin numbers: - -.. _timers-pin-channel-map: - -.. csv-table:: - :header: Timer, Ch. 1 pin, Ch. 2 pin, Ch. 3 pin, Ch. 4 pin - - ``Timer1``, 6, 7, 8, -- - ``Timer2``, 2, 3, 1, 0 - ``Timer3``, 12, 11, 27, 28 - ``Timer4``, 5, 9, 14, 24 - -**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 pseudorandom 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 - <troubleshooting-perpetual-bootloader>` before uploading a new - program to it (or somehow causing your program to re-enable serial - over USB using :ref:`SerialUSB.begin() <lang-serialusb-begin>`). - - 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() <language-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. - -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()``. - -Code Examples -------------- - -LED blink -^^^^^^^^^ - -:: - - #define LED_RATE 500000 // in microseconds; should give 0.5Hz toggles - - void handler_led(void); - - void setup() - { - // Set up the LED to blink - pinMode(BOARD_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) { - toggleLED(); - } - -Racing Counters -^^^^^^^^^^^^^^^ - -:: - - void handler_count1(void); - void handler_count2(void); - - int count1 = 0; - int count2 = 0; - - void setup() - { - // Set up BUT for input - pinMode(BOARD_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(BOARD_BUTTON_PIN)) { - Timer4.pause(); - } else { - Timer4.resume(); - } - delay(1); - } - } - - void handler1(void) { - count1++; - } - void handler2(void) { - count2++; - } +the same program. Refer to your board's :ref:`Timer Pin Map +<gpio-pin-maps>` to match up timer channels and 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. + +Jitter +^^^^^^ + +Other interrupts can and will get called before or during the timer +interrupt routines, causing pseudorandom delays and other +frustrations. + +Disabling the :ref:`USB <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 +<troubleshooting-perpetual-bootloader>` before uploading a new program +to it (or somehow causing your program to re-enable serial over USB +using :ref:`SerialUSB.begin() <lang-serialusb-begin>`). + +The SysTick peripheral another way to perform periodic or delayed +events. Its separate timer does not conflict with any other +peripherals, but the associated 1 kHz interrupt can jitter the general +purpose timer interrupts. The SysTick peripheral can be disabled by +calling :ref:`systick_disable() <libmaple-systick_disable>`, and +re-enabled using :ref:`systick_resume() <libmaple-systick_resume>`. +However, be aware that calling ``systick_disable()`` will stop the +values coming from :ref:`lang-micros` and :ref:`lang-millis` from +increasing. diff --git a/docs/source/troubleshooting.rst b/docs/source/troubleshooting.rst index 9146ebe..bf1b129 100644 --- a/docs/source/troubleshooting.rst +++ b/docs/source/troubleshooting.rst @@ -40,17 +40,17 @@ My 5v peripheral doesn't work! (I2C, SPI, USART, etc) ----------------------------------------------------- Yup, the Maple is a 3.3v board. You may need to use a level -converter. See the :ref:`compatibility <compatibility>`, :ref:`GPIO -<gpio>`, or other :ref:`hardware specific documentation -<index-hardware>` for more information. +converter. See the :ref:`Arduino Compatibility +<arduino-compatibility>`, :ref:`GPIO <gpio>`, or other :ref:`hardware +specific documentation <index-hardware>` for more information. The reset and D38/serial buttons don't seem to work reliably! ------------------------------------------------------------- -A few rev3 boards shipped in May-June 2010 may have had unreliable -buttons; see the :ref:`errata page <errata>` for details. `We're -happy to replace these for you <http://leaflabs.com/contact>`_\ ! - +A few Maple Rev3 boards shipped in May-June 2010 may have had +unreliable buttons; see the :ref:`Maple Errata +<maple-rev3-bad-buttons>` for details. `We're happy to replace these +for you <http://leaflabs.com/contact>`_\ ! .. _troubleshooting-ide-install: @@ -100,8 +100,6 @@ a board selected. The work-around is to restart the IDE. Mysterious! A classic! Make sure you have selected a board from the pulldown menu. -.. TODO: remove when Python version is released - ``undefined reference to setup()/loop()`` ----------------------------------------- @@ -133,8 +131,6 @@ There is an intermittent bug with the temporary directory build system that on occasion will lose many of the ``#include``\ d libmaple files. If you recompile everything, it should be fine. -.. TODO remove when the Python version is released - .. _troubleshooting-upload: ======================== diff --git a/docs/source/unix-toolchain.rst b/docs/source/unix-toolchain.rst index d49b642..8b81e75 100644 --- a/docs/source/unix-toolchain.rst +++ b/docs/source/unix-toolchain.rst @@ -7,20 +7,23 @@ =========================== This is a tutorial for using the Maple with a standard Unix toolchain -(make, gcc, etc.). It's not necessary to do this in order to program -the Maple; you can always :ref:`install the Maple IDE -<maple-ide-install>` instead. +(``make``, ``gcc``, etc.). It's not necessary to do this in order to +program the Maple; you can always :ref:`install the Maple IDE +<maple-ide-install>` instead. This document is intended for users who +are comfortable using C or C++ and would like to use :ref:`libmaple` +directly. You'll need a Maple board, a Mini-B USB cable, a functional computer, -and root access to that computer. This guide assumes you've had -success with the IDE on your machine and that you are fairly -comfortable with the Unix command line; some previous experience with -editing your shell startup script (.bashrc, .tcshrc, etc.) and using -`make <http://www.gnu.org/software/make/>`_ is recommended. For -generic installation/setup issues, the :ref:`IDE installation -<maple-ide-install>` and :ref:`troubleshooting` pages may be -helpful. If all else fails, try our `forum`_, or `contact us -directly`_\ ! +and root (or Administrator) access to that computer. This guide +assumes you've had success with the IDE on your machine and that you +are fairly comfortable with the Unix command line. Some previous +experience with editing your shell startup script (.bashrc, .tcshrc, +etc.) and using `GCC <http://gcc.gnu.org/>`_ and `make +<http://www.gnu.org/software/make/>`_ is recommended. + +For generic installation and setup issues, see the :ref:`IDE +installation <maple-ide-install>` and :ref:`troubleshooting` pages. If +all else fails, try our `forum`_, or `contact us directly`_\ ! We currently have instructions for 32- and 64-bit Linux and OS X Snow Leopard. If you're on another Unix platform, Windows, or an earlier @@ -54,36 +57,40 @@ First I'll give the commands to run, then explain:: $ sudo aptitude install build-essential git-core wget screen dfu-util \ openocd python python-serial -You'll want to install a bunch of developer "basics" like -:command:`make`, :command:`tar`, etc. A good catch-all for these -tools is the "build-essential" meta-package on most Debian platforms: -installing this fake package will pull in dozens of useful tools -without bogging your system down too much. ``git-core`` is the name of -the git package; `Git <http://git-scm.com/>`_ is a distributed code -versioning system we use to track changes in our source -code. :command:`wget` is a simple tool to download files over http -from the command line, and is optional (you could pull in the required -downloads using a browser). :command:`screen` is a really cool virtual -terminal program; in the context of Maple, we use it to connect to -serial port devices. - -:command:`dfu-util` is a tool from the `OpenMoko`_ project that we use -to upload programs to the Maple over USB. +You'll want to install a bunch of developer "basics" like ``make``, +``tar``, etc. A good catch-all for these tools is the +``build-essential`` meta-package on most Debian platforms: installing +this fake package will pull in dozens of useful tools without bogging +your system down too much. + +`Git <http://git-scm.com/>`_ is a distributed code versioning system +we use to track changes in our source code; ``git-core`` is the +corresponding package. + +``wget`` is a simple tool to download files over http from the command +line; installing it is optional (you could pull in the required +downloads using a browser). + +``screen`` is a screen manager; in the context of Maple, we use it to +connect to serial port devices. + +``dfu-util`` is a tool from the `OpenMoko`_ project that we use to +upload programs to the Maple over USB. .. _OpenMoko: http://openmoko.com/ -:command:`openocd` is a `JTAG +``openocd`` is a `JTAG <http://en.wikipedia.org/wiki/Joint_Test_Action_Group>`_ control program used in conjunction with an ARM JTAG device to do in circuit debugging (pause/resume program execution, upload and download code, -read out register status, etc). (optional) +read out register status, etc). It's also optional. Lastly, our reset script (which sends control signals over the USB-serial connection to restart and enter the bootloader) is written -in Python and requires the `PySerial -<http://pyserial.sourceforge.net/>`_ library (the ``python-serial`` -package; this could also be installed with `easy_install -<http://peak.telecommunity.com/DevCenter/EasyInstall>`_). +in `Python <http://python.org>`_, and requires the `PySerial +<http://pyserial.sourceforge.net/>`_ library available in the +``python-serial`` package. This can also be installed with +`easy_install <http://peak.telecommunity.com/DevCenter/EasyInstall>`_. **2. Fetch libmaple and Compiler Toolchain** :: @@ -95,15 +102,15 @@ package; this could also be installed with `easy_install $ export PATH=$PATH:~/libmaple/arm/bin # or wherever these tools ended up This step is fairly straightforward: do a git clone of the `libmaple -repository <http://github.com/leaflabs/libmaple>`_ to some directory, +repository <https://github.com/leaflabs/libmaple>`_ to some directory, then download and extract the ARM compiler toolchain. The :file:`arm/bin/` directory will need to be added to ``PATH``; you can check that this worked by entering ``arm-none-`` and hitting tab -to auto-complete (bash should show a bunch of results). Regardless of -where you put the toolchain, make sure to preserve its internal -directory layout, as the binaries make relative path calls and -references. +to auto-complete (your shell should show a bunch of results). +Regardless of where you put the toolchain, make sure to preserve its +internal directory layout, as the binaries make relative path calls +and references. After you're done, you'll probably want to update your shell startup script so :file:`~/libmaple/arm/bin` stays in your ``PATH``. @@ -118,12 +125,12 @@ From the libmaple directory, :: $ sudo cp support/scripts/45-maple.rules /etc/udev/rules.d/45-maple.rules $ sudo /etc/init.d/udev restart -As a security precaution on linux, unknown USB devices can only be -accessed by the superuser. This udev script identifies the Maple based -on its vendor and product IDs, mounts it to :file:`/dev/maple`, and -grants read/write permissions to the ``plugdev`` group. After -restarting :command:`udev` you'll need to fully unplug or power cycle -any Maples connected to the computer. +As a security precaution on Linux, unknown USB devices can only be +accessed by root. This udev script identifies the Maple based on its +vendor and product IDs, mounts it to :file:`/dev/maple`, and grants +read/write permissions to the ``plugdev`` group. After restarting +``udev`` you'll need to fully unplug or power cycle any Maples +connected to the computer. **So far, so good?** @@ -140,38 +147,40 @@ stated previously, this document assumes a general level of Unix aptitude on the part of the reader; if you're uncomfortable using Terminal (or if you don't know what that means), then you should probably stick with using the `Maple IDE -<http://leaflabs.com/docs/maple-ide/>`_ to develop programs. +<http://leaflabs.com/docs/maple-ide/>`_ to write programs. **1. Collect and Install Tools** -You will need the following tools\ [#fmacports]_ to get started: +You will need the following tools\ [#fpackman]_ to get started: 1. `XCode <http://developer.apple.com/technologies/xcode.html>`_: If you're reading this, you've probably already got this. Provides - compilers and other basic tools of the trade. It's a free download, - but requires registration (gross, we know). + compilers and other basic tools of the trade. While XCode was once + free of charge, Apple has since begun charging for it; if you'd + rather not pay, you can probably get by with just a `make + <http://www.gnu.org/software/make/>`_ binary. 2. `Git <http://git-scm.com/>`_: All of our code is tracked by a - distributed versioning system called git. A `Mac installer + distributed versioning system called Git. A `Mac installer <http://code.google.com/p/git-osx-installer/downloads/list?can=3>`_ is available. - 3. :command:`dfu-util`: A tool from `OpenMoko`_ that we use to upload - programs to the Maple over USB. If you're feeling masochistic, there - are instructions for `building dfu-util from source + 3. ``dfu-util``: A tool from `OpenMoko`_ that we use to upload + programs to the Maple over USB. If you prefer to compile from + source, OpenMoko provides instructions for `building dfu-util <http://wiki.openmoko.org/wiki/Dfu-util#Mac>`_. - However, if you've got better things to do, you can steal a dfu-util - binary from a program called `Openmoko Flasher - <http://www.handheld-linux.com/wiki.php?page=Openmoko%20Flasher>`_. To - do this, first `download Openmoko Flasher + If you're in a hurry, you can steal a dfu-util binary from a program + called `OpenMoko Flasher + <http://www.handheld-linux.com/wiki.php?page=OpenMoko%20Flasher>`_. To + do this, first `download OpenMoko Flasher <http://projects.goldelico.com/p/omflasher/downloads/>`_, then copy - the .app into your :file:`/Applications` folder (or wherever you - like). Let's pretend you saved the .app to the directory + the OpenMoko application into your :file:`/Applications` folder (or + wherever you like). Let's pretend you saved the .app to the directory :file:`/Applications/OpenMoko Flasher.app` - Then the :command:`dfu-util` binary resides in + Then the ``dfu-util`` binary resides in :file:`/Applications/OpenMoko Flasher.app/Contents/Mac OS/dfu-util` @@ -188,8 +197,9 @@ You will need the following tools\ [#fmacports]_ to get started: .app, but you're on your own. To make sure this worked, try plugging in your Maple, making sure - it's in bootloader mode (you can do this by pressing RESET, then - quickly pressing BUT and holding it for several seconds), then + it's in :ref:`perpetual bootloader mode + <troubleshooting-perpetual-bootloader>` (do this by pressing RESET, + then quickly pressing BUT and holding it for several seconds), then running :: $ dfu-util -l @@ -212,8 +222,8 @@ You will need the following tools\ [#fmacports]_ to get started: $ python setup.py build $ sudo python setup.py install - The package is also available via :command:`easy_install`, so if - you're comfortable using that, you could also install it with :: + The package is also available via ``easy_install``, so if you're + comfortable using that, you could also install it with :: $ easy_install pyserial @@ -284,24 +294,24 @@ If it all works out, you should end up seeing something like this:: 21824 200 552 22576 5830 build/maple.out Flash build -Woo! It worked. The ``dec`` field at the end gives the total program -size in bytes. The long listing of object files above the ``Final -Size`` helps to identify bloated code. As you write larger projects, -you may find that they use too much space. If that happens, the -file-by-file listing will help you track down the fatties porking up -your program. +The ``dec`` field at the end gives the total program size in +bytes. The long listing of object files above the ``Final Size`` helps +to identify bloated code. As you write larger projects, you may find +that they use too much space. If that happens, the file-by-file +listing will help you track down the culprits. .. _toolchain-upload: Upload a program ---------------- -Ok, let's blow away the little example program and upload the -interactive test session to your Maple. This will let you interact -textually with the Maple via USB-serial. If you're on Linux, then -before executing :command:`make install`, you'll want to have the udev -rules setup :ref:`as described above <toolchain-udev>`. Plug in your Maple -using the mini-b USB cable; then run :: +Let's blow away the little example program and upload the interactive +test session to your Maple. This will let you interact with the Maple +over a :ref:`USB serial port <usb>`. If you're on Linux, then before +executing ``make install``, you'll want to have the udev rules setup +:ref:`as described above <toolchain-udev>`. + +Plug in your Maple using the Mini-B USB cable; then run :: $ cd ~/libmaple $ cp examples/test-session.cpp main.cpp @@ -312,91 +322,106 @@ using the mini-b USB cable; then run :: A number of things can go wrong at this stage. Simple debugging steps include using :ref:`perpetual bootloader mode <troubleshooting-perpetual-bootloader>`, restarting the Maple a couple -times, :command:`make clean`, etc. If nothing works, the `forum`_ is -your friend. +times, ``make clean``, etc. If nothing works, the `forum`_ is your +friend. .. _toolchain-serialusb: -Communicate over USB-serial interface +Communicate over USB-Serial interface ------------------------------------- -Okay, now that we've flashed the interactive test session to the -Maple, let's test it out. The device for the maple should look -something like :file:`/dev/ttyACMXXX` on Linux or -:file:`/dev/tty.usbmodemXXX` on OS X, but it might have a slightly -different name on your system. To open up a session, run :: +Now let's try out the interactive test session. The serial port +device file should look something like :file:`/dev/ttyACMXXX` on Linux +or :file:`/dev/tty.usbmodemXXX` on OS X, but ``XXX`` will vary +depending on your system. Try using one of these to find out which it +is:: + + # Linux + $ ls /dev/ttyACM* + + # OS X + $ ls /dev/tty.usbmodem* + +To open up a session, run :: $ screen /dev/ttyXXX -If the interactive test program built and uploaded correctly, you -should be able to connect without any errors reported by -:command:`screen`. Type ``h`` or hit the space bar to get a response; -there are a number of commands which demonstrate Maple peripheral -features. As of October 2010, the HardwareSerial library is blocking, -so using any commands which would write to the USART Serial ports will -cause the program to hang. To exit the screen session, type :kbd:`C-a -C-\\` (control-a, followed by control-backslash) on Mac, or :kbd:`C-a -k` (control-a k) on Linux, and type ``y`` when prompted if you're -sure. +If the interactive test program built and uploaded correctly, +``screen`` won't report any errors, and will present you an empty +terminal. Your board is now waiting for you to send it a command. +Type ``h`` to print a list of commands which demonstrate various +features; type any command's letter to run it. + +To exit the screen session, type :kbd:`C-a C-\\` (control-a, followed +by control-backslash) on Mac, or :kbd:`C-a k` (control-a k) on Linux, +and type ``y`` when prompted if you're sure. .. note:: - Using :command:`screen` in this way sometimes messes up your - terminal session on OS X. If your shell starts acting up after you - exit screen, you should be able to fix it with :: + Using ``screen`` sometimes messes up your terminal session on OS X. + If your shell starts acting funny after you exit ``screen``, you + should be able to fix it with :: $ reset && clear + If that doesn't work, just close the Terminal window and open up a + new one. + .. _toolchain-projects: Starting your own projects -------------------------- -.. TODO fix the build-targets.mk mess, note the "library" target - So everything worked, and you want to start your own project? Great! -It's easy. Just set the environment variable ``LIB_MAPLE_HOME`` in -your shell startup script to point to the libmaple repository you -cloned (this tutorial assumes you put it in :file:`~/libmaple`). For -example, if you use bash as your shell, just put this line in your -:file:`~/.bashrc` or :file:`~/.bash_profile`:: - - export LIB_MAPLE_HOME=~/libmaple - -Now, in order to start your own projects, just grab a copy of the -:file:`Makefile` and skeleton :file:`main.cpp` we provided in the -libmaple repository, and you're good to go:: - - $ cd - $ mkdir my-awesome-project - $ cp ~/libmaple/Makefile ~/libmaple/build-targets.mk my-awesome-project - $ cp ~/libmaple/main.cpp.example my-awesome-project/main.cpp - -(TEMPORARY: The file :file:`build-targets.mk` is where the rule to -build the object file for :file:`main.cpp` lives. If you have multiple -source files, you'll probably need to look at it and edit as -appropriate. We're sorry about that and will update the Makefile -structure later to remove this pain point.) Then hack away! You can -:command:`make`, :command:`make clean`, and :command:`make install` -from your new directory :file:`my-awesome-project` just like you did -in the libmaple repository. - -.. note:: - - We update the libmaple repository fairly frequently with bugfixes - and other improvements. In order get access to these in your local - copy of the repository, you should periodically update it with:: - - $ cd $LIB_MAPLE_HOME - $ git pull - - The `commits page - <http://github.com/leaflabs/libmaple/commits/master>`_ for the - github repository is a good place to watch for bleeding-edge - updates; our `blog <http://leaflabs.com/blog/>`_ is the place to - watch for major releases. We keep releases of libmaple and the - Maple IDE in lockstep, so any IDE updates will have corresponding - library updates. +There are two ways to go about it. + +If your project is small, all you have to do is replace +:file:`~/libmaple/main.cpp` with your own code, and you're free to use +``make`` and ``make install`` in the same way you did when you first +:ref:`uploaded a program <toolchain-upload>`. + +If you have a more complicated project, with its own Makefile and +multiple source files, or if you're using an IDE that creates its own +Makefile, you'll probably want to load libmaple from an archive (a +build-time library, not a DLL). + +To create an archive, use the ``library`` Makefile target:: + + $ cd ~/libmaple + $ make library + +This will produce a build-time library in the file +:file:`~/libmaple/build/libmaple.a`. To use it, make sure that you +link against that library, and that the libmaple sources are in your +include path. + +At a minimum, your include path should contain the directories +:file:`~/libmaple/libmaple` and :file:`~/libmaple/wirish/`. If you +want to use one of the officially supported :ref:`libraries +<libraries>`, those live under :file:`~/libmaple/libraries/`. The +main include file for the Wirish library is +:file:`~/libmaple/wirish/wirish.h`. + +Getting Updates +--------------- + +We update libmaple fairly frequently with bugfixes and other +improvements. In order get access to these in your local copy of +the repository, you should periodically update it with:: + + $ cd ~/libmaple + $ git pull + +We keep releases of libmaple and the Maple IDE in lockstep, so any +IDE updates will have corresponding library updates. Our `blog +<http://leaflabs.com/blog/>`_ is the place to watch for major +releases; an `RSS feed <http://leaflabs.com/blog/feed/>`_ is +available. + +You can sign up for a free `Github <https://github.com/plans>`_ +account and `watch libmaple +<https://github.com/leaflabs/libmaple/watchers>`_ to receive +notifications about bleeding-edge development. .. _toolchain-openocd: @@ -405,20 +430,25 @@ Debug with OpenOCD TODO. For now see `this great guide <http://fun-tech.se/stm32/OpenOCD/index.php>`_ from fun-tech.se, and -the ``jtag`` Makefile target. +the ``jtag`` Makefile target. There is also a `JTAG How-To +<http://wiki.leaflabs.com/index.php?title=Maple_JTAG_How_To>`_ page on +our `wiki <http://wiki.leaflabs.com>`_ which you may find useful. .. _toolchain-exuberantly: Go forth exuberantly! --------------------- -Let us know what you come up with! Use #leaflabs on Twitter, post in -the `forum`_, track us down in the real world, whatever. We love -projects! +Let us know what you come up with! Use #leaflabs on `Twitter +<http://twitter.com/leaflabs>`_, post in the `forum`_, join the the +#leafblowers IRC channel on `freenode +<http://freenode.net/irc_servers.shtml>`_, whatever. We love projects! .. rubric:: Footnotes -.. [#fmacports] Some of these software packages might be available on - `MacPorts <http://www.macports.org/>`_. The author had some bad +.. [#fpackman] Some of these software packages might be available on + `MacPorts <http://www.macports.org/>`_ or `Homebrew + <http://mxcl.github.com/homebrew/>`_. The author had some bad experiences with MacPorts a few years ago, though, and hasn't - touched it since. Of course, your mileage may vary. + touched a package manager on OS X since. Of course, your mileage + may vary. diff --git a/docs/source/usart.rst b/docs/source/usart.rst index 3beb3fc..9506cc8 100644 --- a/docs/source/usart.rst +++ b/docs/source/usart.rst @@ -4,23 +4,34 @@ USART ======= -.. contents:: +.. contents:: Contents :local: Hardware/Circuit Design ----------------------- +.. FIXME [0.0.10] UART4 and UART5 +.. FIXME [0.1.0] Maple Native and Mini info and links + The Maple has 3 separate USART devices. 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. Their usage is documented in the :ref:`Serial Ports <lang-serial>` language reference -page. +page. Which pins correspond to the USART TX and RX pins are given on +your board's silkscreen, and also in the board-specific USART pin maps +available here: + +* :ref:`Maple <maple-usart-map>` +* :ref:`Maple RET6 Edition <maple-ret6-usart-map>` + +If you use a particular serial port, you cannot also use its +communication pins for other purposes at the same time. 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 +We have successfully used the USART ports with an FT232R-based +USB-serial converter at up to 115200 baud. Higher speeds should certainly be possible. Recommended Reading diff --git a/docs/source/usb.rst b/docs/source/usb.rst index f502f31..a67d710 100644 --- a/docs/source/usb.rst +++ b/docs/source/usb.rst @@ -2,44 +2,47 @@ .. _usb: -===== - USB -===== +USB +=== -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 STM32 microprocessors include 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 :ref:`IDE +<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). Library support for accessing the emulated terminal is available at the :ref:`SerialUSB <lang-serialusb>` reference. -The SerialUSB channel is used as part of the auto-reset feature of the -IDE to program the board on Maple Rev 3 and Rev 5: a :ref:`magic -sequence of control line toggles and transmitted data -<bootloader-rev3>` 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 -:ref:`SerialUSB.end() <lang-serialusb-end>`). A solution to the -second problem is the use of :ref:`perpetual bootloader mode -<troubleshooting-perpetual-bootloader>`. +The SerialUSB channel is used with the :ref:`Maple bootloader +<bootloader>` to reprogram the board: a :ref:`magic sequence of +control line toggles and transmitted data <bootloader-rev3>` causes a +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 :ref:`SerialUSB.end() <lang-serialusb-end>`). A +solution to the second problem is the use of :ref:`perpetual +bootloader mode <troubleshooting-perpetual-bootloader>`. Recommended Reading ------------------- -* `USB in a NutShell <http://www.beyondlogic.org/usbnutshell/usb1.htm>`_ overview from Beyond Logic +* `USB in a Nutshell <http://www.beyondlogic.org/usbnutshell/usb1.shtml>`_, an overview from Beyond Logic +* `USB made simple <http://www.usbmadesimple.co.uk/>`_, an illustrated series of articles on USB +* The `USB 2.0 Specification <http://www.usb.org/developers/docs/>`_ (`direct link <http://www.usb.org/developers/docs/usb_20_021411.zip>`_) +* `Embedded USB - a brief tutorial <http://www.computer-solutions.co.uk/info/Embedded_tutorials/usb_tutorial.htm>`_ * `Wikipedia article on Universal Serial Bus (USB) <http://en.wikipedia.org/wiki/Universal_Serial_Bus>`_ * Linux Kernel documentation for `USB ACM <http://www.kernel.org/doc/Documentation/usb/acm.txt>`_ and `USB Serial <http://www.kernel.org/doc/Documentation/usb/usb-serial.txt>`_ -* STMicro documentation for STM32F103RB microcontroller: - - * `Datasheet <http://www.st.com/stonline/products/literature/ds/13587.pdf>`_ (pdf) - * `Reference Manual <http://www.st.com/stonline/products/literature/rm/13902.pdf>`_ (pdf) - * `Programming Manual <http://www.st.com/stonline/products/literature/pm/15491.pdf>`_ (pdf; assembly - language and register reference) +* ST documentation: + * Reference Manual `RM0008 + <http://www.st.com/stonline/products/literature/rm/13902.pdf>`_ + (PDF), Chapter 23, "Universal serial bus full-speed device + interface" + * `Programming Manual + <http://www.st.com/stonline/products/literature/pm/15491.pdf>`_ + (PDF; assembly language and register reference) diff --git a/examples/blinky.cpp b/examples/blinky.cpp index 5611987..91d1a47 100644 --- a/examples/blinky.cpp +++ b/examples/blinky.cpp @@ -1,12 +1,9 @@ -// Blinks the LED, pin 13
+// Blinks the built-in LED
#include "wirish.h"
-// Use the pin attached to the built-in LED
-#define PIN BOARD_LED_PIN
-
void setup() {
- pinMode(PIN, OUTPUT);
+ pinMode(BOARD_LED_PIN, OUTPUT);
}
int toggle = 1;
@@ -14,21 +11,21 @@ int toggle = 1; void loop() {
// You could just use toggleLED() instead, but this illustrates
// the use of digitalWrite():
- digitalWrite(PIN, toggle);
+ digitalWrite(BOARD_LED_PIN, toggle);
toggle ^= 1;
delay(100);
}
// Force init to be called *first*, i.e. before static object allocation.
-// Otherwise, statically allocated object that need libmaple may fail.
- __attribute__(( constructor )) void premain() {
+// Otherwise, statically allocated objects that need libmaple may fail.
+__attribute__((constructor)) void premain() {
init();
}
int main(void) {
setup();
- while (1) {
+ while (true) {
loop();
}
return 0;
diff --git a/examples/debug-dtrrts.cpp b/examples/debug-dtrrts.cpp index f9f8b96..3829208 100644 --- a/examples/debug-dtrrts.cpp +++ b/examples/debug-dtrrts.cpp @@ -1,16 +1,11 @@ -// Sample main.cpp file. Blinks an LED, sends a message out USART2 -// and turns on PWM on pin 2 +// Test sketch for figuring out DTR/RTS behavior on different platforms. #include "wirish.h" #include "usb.h" -#define LED_PIN 13 -#define PWM_PIN 2 - -void setup() -{ +void setup() { /* Set up the LED to blink */ - pinMode(LED_PIN, OUTPUT); + pinMode(BOARD_LED_PIN, OUTPUT); /* Send a message out USART2 */ Serial2.begin(9600); @@ -18,12 +13,10 @@ void setup() } -int toggle = 0; - void loop() { - toggle ^= 1; - digitalWrite(LED_PIN, toggle); + toggleLED(); delay(100); + Serial2.print("DTR: "); Serial2.print(usbGetDTR(), DEC); Serial2.print("\tRTS: "); @@ -31,13 +24,12 @@ void loop() { } // Force init to be called *first*, i.e. before static object allocation. -// Otherwise, statically allocated object that need libmaple may fail. - __attribute__(( constructor )) void premain() { +// Otherwise, statically allocated objects that need libmaple may fail. +__attribute__((constructor)) void premain() { init(); } -int main(void) -{ +int main(void) { setup(); while (1) { diff --git a/examples/fsmc-stress-test.cpp b/examples/fsmc-stress-test.cpp new file mode 100644 index 0000000..12e8650 --- /dev/null +++ b/examples/fsmc-stress-test.cpp @@ -0,0 +1,216 @@ +/* + + A low-level stress test of SRAM functionality. Uses slow-ish timing + by default (DATAST = ADDSET = 0xF). + + Copyright 2011 LeafLabs, LLC. + + This code is released into the public domain. + + */ + +#include <stdio.h> +#include <stddef.h> + +#include "wirish.h" +#include "rcc.h" +#include "fsmc.h" + +// -- SRAM config ------------------------------------------------------------- + +// Timing configuration +#define DATAST 0xF +#define ADDSET 0xF + +// Number of SRAM chips to test +#define N 1 + +// How much of each to test +#define MEM_SIZE 0x3FFF + +// Their start addresses in FSMC bank 1 +__io uint16 *const starts[N] = { + // (__io uint16 *const)FSMC_NOR_PSRAM_REGION1, + // (__io uint16 *const)FSMC_NOR_PSRAM_REGION2, + (__io uint16 *const)FSMC_NOR_PSRAM_REGION3, + // (__io uint16 *const)FSMC_NOR_PSRAM_REGION4, +}; + +// Corresponding FSMC configuration registers +__io uint32 *const bcrs[N] = { + // &FSMC_NOR_PSRAM1_BASE->BCR, + // &FSMC_NOR_PSRAM2_BASE->BCR, + &FSMC_NOR_PSRAM3_BASE->BCR, + // &FSMC_NOR_PSRAM4_BASE->BCR, +}; + +// Corresponding FSMC timing registers +__io uint32 *const btrs[N] = { + // &FSMC_NOR_PSRAM1_BASE->BTR, + // &FSMC_NOR_PSRAM2_BASE->BTR, + &FSMC_NOR_PSRAM3_BASE->BTR, + // &FSMC_NOR_PSRAM4_BASE->BTR, +}; + +// -- Pseudorandom number generation ----------------------------------------- + +const uint32 seed = 0xDEADBEEF; + +uint32 num_rand_calls = 0; + +uint32 rand(long n) { + num_rand_calls++; + return random(n); +} + +// -- Printing ---------------------------------------------------------------- + +// For snprintf() +char snprintf_buf[200]; + +#define ERR(fmt, ...) do { \ + snprintf(snprintf_buf, sizeof snprintf_buf, \ + "ERROR: " fmt " (seed %d, ncalls %d, line %d)", \ + __VA_ARGS__, seed, num_rand_calls, __LINE__); \ + Serial1.println(snprintf_buf); \ + } while (0) + +// -- setup()/loop() ---------------------------------------------------------- + +void setup() { + fsmc_sram_init_gpios(); + rcc_clk_enable(RCC_FSMC); + + for (int i = 0; i < N; i++) { + *bcrs[i] = (FSMC_BCR_WREN | + FSMC_BCR_MTYP_SRAM | + FSMC_BCR_MWID_16BITS | + FSMC_BCR_MBKEN); + *btrs[i] = (DATAST << 8) | ADDSET; + } + + Serial1.begin(115200); + randomSeed(seed); +} + +// stress_test() and simple_roundtrip() are the available test routines +bool stress_test(void); +bool simple_roundtrip(void); + +void loop() { + uint32 count = 0; + bool ok = true; + bool (*test)(void) = stress_test; + + while (true) { + count++; + bool result = test(); + ok = ok && result; + if (ok) { + snprintf(snprintf_buf, sizeof snprintf_buf, + "everything ok so far, timestamp %d ms", millis()); + Serial1.println(snprintf_buf); + } + } +} + +// -- Test routines ----------------------------------------------------------- + +bool random_trips(); +bool sequential_trips(); + +bool stress_test(void) { + static int i = 0; + i = !i; + + switch (i) { + case 0: + return random_trips(); + default: + return sequential_trips(); + } +} + +bool simple_roundtrip(void) { + uint16 wval = 0xAB; + + for (int i = 0; i < N; i++) { + __io uint16 *addr = starts[i] + 4; + snprintf(snprintf_buf, sizeof snprintf_buf, "round-trip 0x%x at %p", + wval, addr); + Serial1.println(snprintf_buf); + + *addr = wval; + uint16 rval = *addr; + + if (rval != wval) { + ERR("wrote 0x%x, read 0x%x, timestamp %d", wval, rval, millis()); + return false; + } else { + snprintf(snprintf_buf, sizeof snprintf_buf, "got back 0x%x", rval); + Serial1.println(snprintf_buf); + } + } + + return true; +} + +bool random_trips(void) { + for (int n = 0; n < N; n++) { + __io uint16 *const start = starts[n]; + + for (int i = 0; i < 1000; i++) { + uint32 offset = rand(MEM_SIZE); + uint32 wval = rand(0xFFFF); + + *(start + offset) = wval; + uint32 rval = *(start + offset); + + if (rval != wval) { + ERR("wrote 0x%x to 0x%x, read 0x%x", wval, offset, rval); + return false; + } + } + } + return true; +} + +bool sequential_trips(void) { + static const uint32 seq_length = 300; + + for (int n = 0; n < N; n++) { + __io uint16 *const start = starts[n]; + + for (int i = 0; i < 100; i++) { + uint32 start_offset = rand(MEM_SIZE - seq_length); + + for (uint32 w = 0; w < seq_length; w++) { + uint32 offset = start_offset + w; + + *(start + offset) = w; + uint32 r = *(start + offset); + + if (w != r) { + ERR("wrote 0x%x to 0x%x, read 0x%x", w, offset, r); + return false; + } + } + } + } + return true; +} + +// ---------------------------------------------------------------------------- + +__attribute__((constructor)) void premain() { + init(); +} + +int main(void) { + setup(); + while (true) { + loop(); + } + return 0; +} + diff --git a/examples/qa-slave-shield.cpp b/examples/qa-slave-shield.cpp index b395cca..2da1c04 100644 --- a/examples/qa-slave-shield.cpp +++ b/examples/qa-slave-shield.cpp @@ -1,53 +1,64 @@ -// slave mode for QA shield +// Slave mode for Quality Assurance test #include "wirish.h" -#define LED_PIN 13 -#define NUM_GPIO 38 // not the number of the max... +#define INTER_TOGGLE_DELAY_NORMAL 5 +#define INTER_TOGGLE_DELAY_SLOW 80 -int i; +void interToggleDelay(void); -void setup() -{ - /* Set up the LED to blink */ - pinMode(LED_PIN, OUTPUT); - digitalWrite(LED_PIN, 1); +void setup() { + pinMode(BOARD_LED_PIN, OUTPUT); + pinMode(BOARD_BUTTON_PIN, INPUT); - for(i=0; i<NUM_GPIO; i++) { - if(i==13) { continue; } + // All unused pins start out low. + for (int i = 0; i < BOARD_NR_GPIO_PINS; i++) { + if (boardUsesPin(i)) + continue; pinMode(i, OUTPUT); - digitalWrite(i,0); + digitalWrite(i, LOW); } - //delay(5000); SerialUSB.println("OK, starting..."); - } void loop() { - digitalWrite(LED_PIN,1); + toggleLED(); delay(100); - digitalWrite(LED_PIN,0); - - for(i=0; i<NUM_GPIO; i++) { - if(i==13) { continue; } - digitalWrite(i,1); - delay(5); - digitalWrite(i,0); - delay(5); + toggleLED(); + + for (int i = 0; i < BOARD_NR_GPIO_PINS; i++) { + if (boardUsesPin(i)) + continue; + + // Bring just this pin high. + digitalWrite(i, HIGH); + // Give the master time to detect if any other pins also went high. + interToggleDelay(); + // Bring this pin back low again; all pins should now be low. + digitalWrite(i, LOW); + // Give the master time to detect if any pins are still high. + interToggleDelay(); } } +void interToggleDelay(void) { + if (digitalRead(BOARD_BUTTON_PIN)) { // don't pay the debouncing time + delay(INTER_TOGGLE_DELAY_SLOW); + } else { + delay(INTER_TOGGLE_DELAY_NORMAL); + } + } + // Force init to be called *first*, i.e. before static object allocation. -// Otherwise, statically allocated object that need libmaple may fail. - __attribute__(( constructor )) void premain() { +// Otherwise, statically allocated objects that need libmaple may fail. +__attribute__((constructor)) void premain() { init(); } -int main(void) -{ +int main(void) { setup(); - while (1) { + while (true) { loop(); } return 0; diff --git a/examples/spi_master.cpp b/examples/spi_master.cpp index 9f6d81b..af3e709 100644 --- a/examples/spi_master.cpp +++ b/examples/spi_master.cpp @@ -1,75 +1,75 @@ -/* *****************************************************************************
+/******************************************************************************
* The MIT License
*
* Copyright (c) 2010 LeafLabs LLC.
*
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
*
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- * ****************************************************************************/
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *****************************************************************************/
/**
- * @brief Sample main.cpp file. Sends "Hello world!" out SPI1.
+ * @brief Sample main.cpp file. Sends "Hello world!" out SPI1.
*
- * SPI1 is set up to be a master transmitter at 4.5MHz, little endianness,
- * and SPI mode 0.
- *
- * Pin 10 is used as Chip Select
+ * SPI1 is set up to be a master transmitter at 4.5MHz, little
+ * endianness, and SPI mode 0.
*
+ * Pin 10 is used as slave select.
*/
#include "wirish.h"
-#define CS 10
+#define NSS 10
byte buf[] = "Hello world!";
HardwareSPI spi1(1);
void setup() {
- /* Set up chip select as output */
- pinMode(CS, OUTPUT);
+ /* Set up chip select as output */
+ pinMode(NSS, OUTPUT);
- /* CS is usually active low, so initialize it high */
- digitalWrite(CS, HIGH);
+ /* NSS is usually active LOW, so initialize it HIGH */
+ digitalWrite(NSS, HIGH);
- /* Initialize SPI */
+ /* Initialize SPI */
spi1.begin(SPI_4_5MHZ, LSBFIRST, 0);
}
void loop() {
- /* Send message */
- digitalWrite(CS, LOW);
- spi1.send(buf, sizeof buf);
- digitalWrite(CS,HIGH);
+ /* Send message */
+ digitalWrite(NSS, LOW);
+ spi1.write(buf, sizeof buf);
+ digitalWrite(NSS, HIGH);
delay(1000);
}
// Force init to be called *first*, i.e. before static object allocation.
-// Otherwise, statically allocated object that need libmaple may fail.
- __attribute__(( constructor )) void premain() {
+// Otherwise, statically allocated objects that need libmaple may fail.
+__attribute__((constructor)) void premain() {
init();
}
-int main(void)
-{
+int main(void) {
setup();
- while (1) {
+ while (true) {
loop();
}
return 0;
diff --git a/examples/test-bkp.cpp b/examples/test-bkp.cpp new file mode 100644 index 0000000..f5957b7 --- /dev/null +++ b/examples/test-bkp.cpp @@ -0,0 +1,80 @@ +#include <stdio.h> // for snprintf() + +#include "wirish.h" +#include "bkp.h" +#include "iwdg.h" + +void print_bkp_contents(); +void write_to_bkp(uint16 val); + +#define comm Serial2 + +void setup() { + pinMode(BOARD_BUTTON_PIN, INPUT); + + comm.begin(9600); + comm.println("*** Beginning BKP test"); + + comm.println("Init..."); + bkp_init(); + comm.println("Done."); + + print_bkp_contents(); + write_to_bkp(10); + print_bkp_contents(); + + comm.println("Enabling backup writes."); + bkp_enable_writes(); + write_to_bkp(20); + print_bkp_contents(); + + comm.println("Disabling backup writes."); + bkp_disable_writes(); + write_to_bkp(30); + print_bkp_contents(); + + comm.println("Done testing backup registers; press button to enable " + "independent watchdog (in order to cause a reset)."); + waitForButtonPress(0); + iwdg_init(IWDG_PRE_4, 1); + comm.println(); +} + +void loop() { +} + +void print_bkp_contents() { + comm.println("Backup data register contents:"); + char buf[100]; + for (int i = 1; i <= BKP_NR_DATA_REGS; i++) { + snprintf(buf, sizeof buf, "DR%d: %d ", i, bkp_read(i)); + comm.print(buf); + if (i % 5 == 0) comm.println(); + } + comm.println(); +} + +void write_to_bkp(uint16 val) { + comm.print("Attempting to write "); + comm.print(val); + comm.println(" to backup registers..."); + for (int i = 1; i <= BKP_NR_DATA_REGS; i++) { + bkp_write(i, val); + } + comm.println("Done."); +} + +__attribute__((constructor)) void premain() { + init(); +} + +int main(void) { + init(); + setup(); + + while (1) { + loop(); + } + return 0; +} + diff --git a/examples/test-dac.cpp b/examples/test-dac.cpp index 3a699e2..40ae5d5 100644 --- a/examples/test-dac.cpp +++ b/examples/test-dac.cpp @@ -1,45 +1,51 @@ -
-#include "wirish.h"
-#include "fsmc.h"
-#include "rcc.h"
-#include "gpio.h"
-#include "dac.h"
-
-uint16 count = 0;
-
-void setup() {
-
- pinMode(BOARD_LED_PIN, OUTPUT);
- digitalWrite(BOARD_LED_PIN,1);
-
- Serial1.begin(9600);
- Serial1.println("**** Beginning DAC test");
-
- Serial1.print("Init... ");
- dac_init();
- Serial1.println("Done.");
-}
-
-void loop() {
- toggleLED();
- delay(100);
-
- count += 100;
- if(count > 4095) {
- count = 0;
- }
-
- dac_write(1, 2048);
- dac_write(2, count);
-}
-
-int main(void) {
- init();
- setup();
-
- while (1) {
- loop();
- }
- return 0;
-}
-
+/* + * Simple DAC test. + * + * Author: Marti Bolivar <mbolivar@leaflabs.com> + * + * This file is released into the public domain. + */ + +#include "wirish.h" +#include "dac.h" + +uint16 count = 0; + +void setup() { + pinMode(BOARD_LED_PIN, OUTPUT); + digitalWrite(BOARD_LED_PIN, HIGH); + + Serial1.begin(9600); + Serial1.println("**** Beginning DAC test"); + + Serial1.print("Init... "); + dac_init(DAC, DAC_CH1 | DAC_CH2); + Serial1.println("Done."); +} + +void loop() { + toggleLED(); + delay(100); + + count += 100; + if (count > 4095) { + count = 0; + } + + dac_write_channel(DAC, 1, 4095 - count); + dac_write_channel(DAC, 2, count); +} + +__attribute__((constructor)) void premain() { + init(); +} + +int main(void) { + setup(); + + while (true) { + loop(); + } + return 0; +} + diff --git a/examples/test-fsmc.cpp b/examples/test-fsmc.cpp index f4fd068..7db3bb7 100644 --- a/examples/test-fsmc.cpp +++ b/examples/test-fsmc.cpp @@ -1,119 +1,125 @@ +#include <stddef.h> // for ptrdiff_t
#include "wirish.h"
#include "fsmc.h"
-#define LED_PIN 23 // hack for maple native
-#define DISC_PIN 14 // hack for USB on native
-
-// System control block registers
-#define SCB_BASE (SCB_Reg*)(0xE000ED00)
-
-// This stuff should ultimately get moved to util.h or scb.h or w/e.
-// Also in interactive test program and the HardFault IRQ handler.
-typedef struct {
- volatile uint32 CPUID;
- volatile uint32 ICSR;
- volatile uint32 VTOR;
- volatile uint32 AIRCR;
- volatile uint32 SCR;
- volatile uint32 CCR;
- volatile uint32 SHPR1;
- volatile uint32 SHPR2;
- volatile uint32 SHPR3;
- volatile uint32 SHCRS;
- volatile uint32 CFSR;
- volatile uint32 HFSR;
- uint32 pad1;
- volatile uint32 MMAR;
- volatile uint32 BFAR;
-} SCB_Reg;
-
-SCB_Reg *scb;
-
-uint16 *ptr;
-int toggle = 0;
-int count = 0;
+#ifndef BOARD_maple_native
+#error "Sorry, this example only works on Maple Native."
+#endif
+
+// Start of FSMC SRAM bank 1
+static uint16 *const sram_start = (uint16*)0x60000000;
+// End of Maple Native SRAM chip address space (512K 16-bit words)
+static uint16 *const sram_end = (uint16*)0x60080000;
+
+void test_single_write(void);
+void test_all_addresses(void);
void setup() {
- uint32 id;
- scb = (SCB_Reg*)SCB_BASE;
-
- pinMode(LED_PIN, OUTPUT);
- pinMode(DISC_PIN, OUTPUT);
- digitalWrite(DISC_PIN,1);
- digitalWrite(LED_PIN,1);
-
- Serial1.begin(9600);
- Serial1.println("Hello World!");
-
- Serial1.print("Init... ");
- fsmc_native_sram_init();
- Serial1.println("Done.");
-
- // Start of channel1 SRAM bank (through to 0x63FFFFFF, though only a chunk
- // of this is valid)
- ptr = (uint16*)(0x60000000);
-
- Serial1.print("Writing... ");
- *ptr = 0x1234;
- Serial1.println("Done.");
-
- Serial1.print("Reading... ");
- id = *ptr;
- Serial1.print("Done: "); // shouldn't be 0xFFFFFFFF
- Serial1.println(id,BIN);
-
- Serial1.println("Dumping System Control Block Registers");
- Serial1.print("CPUID: ");
- id = scb->CPUID;
- Serial1.println(id,BIN);
- Serial1.print("ICSR: ");
- id = scb->ICSR;
- Serial1.println(id,BIN);
- Serial1.print("CFSR: ");
- id = scb->CFSR;
- Serial1.println(id,BIN);
- Serial1.print("HFSR: ");
- id = scb->HFSR;
- Serial1.println(id,BIN);
- Serial1.print("MMAR: ");
- id = scb->MMAR;
- Serial1.println(id,BIN);
- Serial1.print("BFAR: ");
- id = scb->BFAR;
- Serial1.println(id,BIN);
-
- Serial1.println("Now testing all memory addresses... (will hardfault at the end)");
- delay(3000);
+ pinMode(BOARD_LED_PIN, OUTPUT);
+ digitalWrite(BOARD_LED_PIN, HIGH);
+
+ Serial1.begin(115200);
+ Serial1.println("*** Beginning RAM chip test");
+
+ test_single_write();
+ test_all_addresses();
+
+ Serial1.println("Tests pass, finished.");
}
void loop() {
- digitalWrite(LED_PIN, toggle);
- toggle ^= 1;
- delay(1);
-
- for(int i = 0; i<100; i++) { // modify this to speed things up
- count++;
- ptr++;
- //delay(10); // tweak this to test SRAM resiliance over time
- *ptr = (0x0000FFFF & count);
- if(*ptr != (0x0000FFFF & count)) {
- Serial1.println("ERROR: mismatch, halting");
- while(1) { }
+}
+
+void test_single_write() {
+ uint16 *ptr = sram_start;
+ uint16 tmp;
+
+ Serial1.print("Writing 0x1234... ");
+ *ptr = 0x1234;
+ Serial1.println("Done.");
+
+ Serial1.print("Reading... ");
+ tmp = *ptr;
+ Serial1.print("Done: 0x");
+ Serial1.println(tmp, HEX);
+
+ if (tmp != 0x1234) {
+ Serial1.println("Mismatch; abort.");
+ ASSERT(0);
+ }
+}
+
+void test_all_addresses() {
+ uint32 start, end;
+ uint16 count = 0;
+ uint16 *ptr;
+
+ Serial1.println("Now writing all memory addresses (unrolled loop)");
+ // Turn off the USB interrupt, as it interferes most with timing
+ // (don't turn off SysTick, or we won't get micros()).
+ SerialUSB.end();
+ start = micros();
+ for (ptr = sram_start; ptr < sram_end;) {
+ *ptr++ = count++;
+ *ptr++ = count++;
+ *ptr++ = count++;
+ *ptr++ = count++;
+ *ptr++ = count++;
+ *ptr++ = count++;
+ *ptr++ = count++;
+ *ptr++ = count++;
+ *ptr++ = count++;
+ *ptr++ = count++;
+ *ptr++ = count++;
+ *ptr++ = count++;
+ *ptr++ = count++;
+ *ptr++ = count++;
+ *ptr++ = count++;
+ *ptr++ = count++;
+ }
+ end = micros();
+ SerialUSB.begin();
+ Serial1.print("Done. Elapsed time (us): ");
+ Serial1.println(end - start);
+
+ Serial1.println("Validating writes.");
+ for (ptr = sram_start, count = 0; ptr < sram_end; ptr++, count++) {
+ uint16 value = *ptr;
+ if (value != count) {
+ Serial1.print("mismatch: 0x");
+ Serial1.print((uint32)ptr);
+ Serial1.print(" = 0x");
+ Serial1.print(value, HEX);
+ Serial1.print(", should be 0x");
+ Serial1.print(count, HEX);
+ Serial1.println(".");
+ ASSERT(0);
}
- }
-
- Serial1.print((uint32)(ptr),HEX);
- Serial1.print(": ");
- Serial1.println(*ptr,BIN);
+ }
+ Serial1.println("Done; all writes seem valid.");
+
+ ptrdiff_t nwrites = sram_end - sram_start;
+ double us_per_write = double(end-start) / double(nwrites);
+ Serial1.print("Number of writes = ");
+ Serial1.print(nwrites);
+ Serial1.print("; avg. time per write = ");
+ Serial1.print(us_per_write);
+ Serial1.print(" us (");
+ Serial1.print(1 / us_per_write);
+ Serial1.println(" MHz)");
+}
+
+__attribute__((constructor)) void premain() {
+ init();
}
int main(void) {
- init();
- setup();
+ setup();
+
+ while (true) {
+ loop();
+ }
- while (1) {
- loop();
- }
- return 0;
+ return 0;
}
diff --git a/examples/test-print.cpp b/examples/test-print.cpp new file mode 100644 index 0000000..ff3c219 --- /dev/null +++ b/examples/test-print.cpp @@ -0,0 +1,181 @@ +/* + * print-test.cpp + * + * Tests the various Print methods. (For USBSerial; assuming that + * writing a single character works, this should generalize to + * HardwareSerial). + * + * This file is released into the public domain. + */ + +#include "wirish.h" +#undef min +#undef max + +// For snprintf() +#include <stdio.h> +// The <limits.h> that comes with newlib is missing LLONG_MAX, etc. +#include <limits> + +using namespace std; + +#define BUF_SIZE 100 +char buf[BUF_SIZE]; + +void test_numbers(void); +void test_base_arithmetic(void); +void test_floating_point(void); + +void print_separator(void); + +void setup() { + while (!SerialUSB.available()) + continue; + SerialUSB.read(); +} + +void loop() { + SerialUSB.println("Testing Print methods."); + print_separator(); + + test_numbers(); + print_separator(); + + test_base_arithmetic(); + print_separator(); + + test_floating_point(); + print_separator(); + + SerialUSB.println("Test finished."); + while (true) { + continue; + } +} + +void test_numbers(void) { + SerialUSB.println("Numeric types:"); + + SerialUSB.println("unsigned char: "); + // prevent Print from treating it as an (extended) ASCII character: + SerialUSB.println((uint32)numeric_limits<unsigned char>::max()); + + SerialUSB.print("int: "); + SerialUSB.print(numeric_limits<int>::min()); + SerialUSB.print(" -- "); + SerialUSB.println(numeric_limits<int>::max()); + + SerialUSB.print("unsigned int: "); + SerialUSB.print(numeric_limits<unsigned int>::max()); + SerialUSB.println(); + + SerialUSB.print("long: "); + SerialUSB.print(numeric_limits<long>::min()); + SerialUSB.print(" -- "); + SerialUSB.println(numeric_limits<long>::max()); + + SerialUSB.print("long long: "); + SerialUSB.print(numeric_limits<long long>::min()); + SerialUSB.print(" -- "); + SerialUSB.println(numeric_limits<long long>::max()); + + SerialUSB.print("unsigned long long: "); + SerialUSB.println(numeric_limits<unsigned long long>::max()); +} + +void test_base_arithmetic(void) { + SerialUSB.println("Base arithmetic:"); + + SerialUSB.print("Binary: "); + SerialUSB.print(numeric_limits<unsigned long>::max(), BIN); + SerialUSB.print(", "); + SerialUSB.println(numeric_limits<unsigned long long>::max(), BIN); + + SerialUSB.print("Octal: "); + SerialUSB.print(numeric_limits<unsigned long>::max(), OCT); + SerialUSB.print(", "); + SerialUSB.println(numeric_limits<unsigned long long>::max(), OCT); + + SerialUSB.print("Decimal: "); + SerialUSB.print(numeric_limits<unsigned long>::max(), DEC); + SerialUSB.print(", "); + SerialUSB.println(numeric_limits<unsigned long long>::max(), DEC); + + SerialUSB.print("Hexadecimal: "); + SerialUSB.print(numeric_limits<unsigned long>::max(), HEX); + SerialUSB.print(", "); + SerialUSB.println(numeric_limits<unsigned long long>::max(), HEX); +} + +void test_floating_point(void) { + double dmax = numeric_limits<double>::max(); + + SerialUSB.println("Floating point:"); + + SerialUSB.print("sizeof double: "); + SerialUSB.println(sizeof(double)); + SerialUSB.print("println(-5.67): "); + SerialUSB.println(-5.67); + SerialUSB.print("println((double)(LLONG_MAX - 10)): "); + SerialUSB.println((double)(numeric_limits<long long>::max() - 10)); + SerialUSB.print("println((double)(LLONG_MAX - 10)) from snprintf(): "); + snprintf(buf, BUF_SIZE, "%.2f", + (double)(numeric_limits<long long>::max() - 10)); + SerialUSB.println(buf); + SerialUSB.print("println((double)LLONG_MAX / 2): "); + SerialUSB.println((double)(numeric_limits<long long>::max()) / 2); + SerialUSB.print("println((double)LLONG_MAX / 2) from snprintf(): "); + snprintf(buf, BUF_SIZE, "%.2f", + (double)(numeric_limits<long long>::max()) / 2); + SerialUSB.println(buf); + SerialUSB.print("DBL_MAX: "); + SerialUSB.println(dmax); + SerialUSB.print("DBL_MAX from snprintf(): "); + snprintf(buf, BUF_SIZE, "%g", dmax); + SerialUSB.println(buf); + SerialUSB.print("-DBL_MAX / 2: "); + SerialUSB.println(-dmax / 2.0); + SerialUSB.print("-DBL_MAX / 2 from snprintf(): "); + snprintf(buf, BUF_SIZE, "%g", -dmax / 2.0); + SerialUSB.println(buf); + SerialUSB.print("Double epsilon, round error: "); + SerialUSB.print(numeric_limits<double>::epsilon()); + SerialUSB.print(", "); + SerialUSB.println(numeric_limits<double>::round_error()); + + SerialUSB.println(); + + float fmax = numeric_limits<float>::max(); + + SerialUSB.print("sizeof float: "); + SerialUSB.println(sizeof(float)); + SerialUSB.print("println(-5.67f): "); + SerialUSB.println(-5.67f); + SerialUSB.print("Float max: "); + SerialUSB.println(fmax); + SerialUSB.print("Float epsilon, round error: "); + SerialUSB.print(numeric_limits<float>::epsilon()); + SerialUSB.print(", "); + SerialUSB.println(numeric_limits<float>::round_error()); +} + +void print_separator(void) { + SerialUSB.println(); + SerialUSB.println(" ** "); + SerialUSB.println(); +} + +// Force init to be called *first*, i.e. before static object allocation. +// Otherwise, statically allocated objects that need libmaple may fail. +__attribute__((constructor)) void premain() { + init(); +} + +int main(void) { + setup(); + + while (1) { + loop(); + } + return 0; +} diff --git a/examples/test-ring-buffer-insertion.cpp b/examples/test-ring-buffer-insertion.cpp new file mode 100644 index 0000000..e86372a --- /dev/null +++ b/examples/test-ring-buffer-insertion.cpp @@ -0,0 +1,114 @@ +/* + * Simple ring_buffer test. + * + * Does a basic test of functionality on rb_full_count(), rb_reset(), + * rb_push_insert(), and rb_safe_insert(). + * + * To test: + * + * - Connect a serial monitor to SerialUSB + * - Press any key + * + * This file is released into the public domain. + */ + +#include "wirish.h" + +#include "ring_buffer.h" + +#define BUF_SIZE 64 +ring_buffer ring_buf; +ring_buffer *rb; +uint8 rb_buffer[BUF_SIZE]; + +void test_rb_push_insert(int num_bytes_to_insert); +void test_rb_safe_insert(int num_bytes_to_insert); +void test_rb_insertion_function(int num_bytes_to_insert, + int (*insertion_fn)(ring_buffer*, uint8), + const char insertion_fn_name[]); +void print_rb_contents(void); + +void setup() { + rb = &ring_buf; + rb_init(rb, BUF_SIZE, rb_buffer); + + while (!SerialUSB.available()) + ; + + SerialUSB.println("Beginning test."); + SerialUSB.println(); +} + +void loop() { + test_rb_push_insert(63); + SerialUSB.println("------------------------------"); + test_rb_push_insert(64); + SerialUSB.println("------------------------------"); + test_rb_safe_insert(63); + SerialUSB.println("------------------------------"); + test_rb_safe_insert(64); + SerialUSB.println("------------------------------"); + + SerialUSB.println(); + SerialUSB.println("Test finished."); + while (true) + ; +} + +void test_rb_push_insert(int num_bytes_to_insert) { + test_rb_insertion_function(num_bytes_to_insert, + rb_push_insert, + "rb_push_insert()"); +} + +void test_rb_safe_insert(int num_bytes_to_insert) { + test_rb_insertion_function(num_bytes_to_insert, + rb_safe_insert, + "rb_safe_insert()"); +} + +void test_rb_insertion_function(int num_bytes_to_insert, + int (*insertion_fn)(ring_buffer *, uint8), + const char insertion_fn_name[]) { + SerialUSB.println("resetting ring buffer."); + rb_reset(rb); + print_rb_contents(); + + SerialUSB.print(insertion_fn_name); + SerialUSB.print("-ing "); + SerialUSB.print(num_bytes_to_insert); + SerialUSB.println(" bytes."); + for (uint8 i = 1; i <= num_bytes_to_insert; i++) + insertion_fn(rb, i); + + uint16 count = rb_full_count(rb); + SerialUSB.print("rb_full_count(rb) = "); + SerialUSB.println(count); + + print_rb_contents(); +} + +void print_rb_contents() { + uint16 count = rb_full_count(rb); + SerialUSB.print("ring buffer contents: "); + for (uint16 i = 0; i < count; i++) { + SerialUSB.print((int)rb_remove(rb)); + if (i < count - 1) SerialUSB.print(", "); + } + SerialUSB.println(); +} + +// Force init to be called *first*, i.e. before static object allocation. +// Otherwise, statically allocated objects that need libmaple may fail. +__attribute__((constructor)) void premain() { + init(); +} + +int main(void) { + setup(); + + while (true) { + loop(); + } + return 0; +} diff --git a/examples/test-serial-flush.cpp b/examples/test-serial-flush.cpp index d7fbf7a..adc9c3e 100644 --- a/examples/test-serial-flush.cpp +++ b/examples/test-serial-flush.cpp @@ -1,44 +1,37 @@ -// Tests the "flush" Serial function +/* + * Tests the "flush" Serial function. + */ #include "wirish.h" -void setup() -{ - /* Send a message out USART2 */ - Serial2.begin(9600); - Serial2.println("Hello world!"); +void setup() { + Serial1.begin(9600); + Serial1.println("Hello world!"); } -int toggle = 0; - void loop() { - - Serial2.println("This is the first line."); - Serial2.end(); - Serial2.println("This is the second line."); - - Serial2.begin(9600); - Serial2.println("Waiting for multiple input..."); - while(Serial2.available() < 5) { } - Serial2.println(Serial2.read()); - Serial2.println(Serial2.read()); - Serial2.flush(); - if(Serial2.available()) { - Serial2.println("FAIL! Still had junk in the buffer..."); + Serial1.println("Waiting for multiple input..."); + while (Serial1.available() < 5) + ; + Serial1.println(Serial1.read()); + Serial1.println(Serial1.read()); + Serial1.flush(); + + if (Serial1.available()) { + Serial1.println("FAIL! Still had junk in the buffer..."); } } // Force init to be called *first*, i.e. before static object allocation. -// Otherwise, statically allocated object that need libmaple may fail. - __attribute__(( constructor )) void premain() { +// Otherwise, statically allocated objects that need libmaple may fail. +__attribute__((constructor)) void premain() { init(); } -int main(void) -{ +int main(void) { setup(); - while (1) { + while (true) { loop(); } return 0; diff --git a/examples/test-serialusb.cpp b/examples/test-serialusb.cpp index 9d0a862..15ab913 100644 --- a/examples/test-serialusb.cpp +++ b/examples/test-serialusb.cpp @@ -1,81 +1,74 @@ -// Sample main.cpp file. Blinks an LED, sends a message out USART2 -// and turns on PWM on pin 2 +// Tests SerialUSB functionality. #include "wirish.h" #include "usb.h" -#define LED_PIN 13 -#define BUT_PIN 38 - -uint32 state = 0; #define QUICKPRINT 0 #define BIGSTUFF 1 #define NUMBERS 2 #define SIMPLE 3 #define ONOFF 4 +uint32 state = 0; -void setup() -{ +void setup() { /* Set up the LED to blink */ - pinMode(LED_PIN, OUTPUT); - - /* Set up the Button */ - pinMode(BUT_PIN, INPUT_PULLUP); + pinMode(BOARD_LED_PIN, OUTPUT); + /* Set up Serial2 for use as a debug channel */ Serial2.begin(9600); - Serial2.println("Hello world! This is the debug channel."); + Serial2.println("This is the debug channel. Press any key."); + while (!Serial2.available()) + ; + Serial2.read(); } -int toggle = 0; - uint8 c1 = '-'; void loop() { - toggle ^= 1; - digitalWrite(LED_PIN, toggle); + toggleLED(); delay(1000); - if(digitalRead(BUT_PIN)) { - while(digitalRead(BUT_PIN)) {}; + if (Serial2.available()) { + Serial2.read(); state++; } - - switch(state) { + + switch (state) { case QUICKPRINT: - for(int i = 0; i<30; i++) { - usbSendBytes(&c1,1); + for (int i = 0; i < 30; i++) { + usbSendBytes(&c1, 1); SerialUSB.print('.'); SerialUSB.print('|'); } - Serial2.println(SerialUSB.pending(),DEC); + Serial2.println(SerialUSB.pending(), DEC); SerialUSB.println(); break; case BIGSTUFF: - SerialUSB.println("01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"); - SerialUSB.println((uint32)123456789,DEC); + SerialUSB.println("0123456789012345678901234567890123456789" + "0123456789012345678901234567890123456789" + "012345678901234567890"); + SerialUSB.println((int64)123456789, DEC); SerialUSB.println(3.1415926535); - Serial2.println(SerialUSB.pending(),DEC); + Serial2.println(SerialUSB.pending(), DEC); break; case NUMBERS: SerialUSB.println("Numbers! -----------------------------"); Serial2.println("Numbers! -----------------------------"); SerialUSB.println('1'); Serial2.println('1'); - SerialUSB.println(1,DEC); - Serial2.println(1,DEC); - SerialUSB.println(-1,DEC); - Serial2.println(-1,DEC); + SerialUSB.println(1, DEC); + Serial2.println(1, DEC); + SerialUSB.println(-1, DEC); + Serial2.println(-1, DEC); SerialUSB.println(3.14159265); Serial2.println(3.14159265); - SerialUSB.println(3.14159265,9); - Serial2.println(3.14159265,9); - SerialUSB.println(123456789,DEC); - Serial2.println(123456789,DEC); - SerialUSB.println(-123456789,DEC); - Serial2.println(-123456789,DEC); - SerialUSB.println(65535,HEX); - Serial2.println(65535,HEX); + SerialUSB.println(123456789, DEC); + Serial2.println(123456789, DEC); + SerialUSB.println(-123456789, DEC); + Serial2.println(-123456789, DEC); + SerialUSB.println(65535, HEX); + Serial2.println(65535, HEX); break; case SIMPLE: Serial2.println("Trying write('a')"); @@ -92,8 +85,8 @@ void loop() { SerialUSB.print("hij\n\r"); SerialUSB.write(' '); SerialUSB.println(); - Serial2.println("Trying println(123456789,DEC)"); - SerialUSB.println(123456789); + Serial2.println("Trying println(123456789, DEC)"); + SerialUSB.println(123456789, DEC); Serial2.println("Trying println(3.141592)"); SerialUSB.println(3.141592); Serial2.println("Trying println(\"DONE\")"); @@ -103,12 +96,12 @@ void loop() { Serial2.println("Shutting down..."); SerialUSB.println("Shutting down..."); SerialUSB.end(); - Serial2.println("Waiting 4seconds..."); + Serial2.println("Waiting 4 seconds..."); delay(4000); Serial2.println("Starting up..."); SerialUSB.begin(); SerialUSB.println("Hello World!"); - Serial2.println("Waiting 4seconds..."); + Serial2.println("Waiting 4 seconds..."); delay(4000); state++; break; @@ -118,18 +111,16 @@ void loop() { } // Force init to be called *first*, i.e. before static object allocation. -// Otherwise, statically allocated object that need libmaple may fail. - __attribute__(( constructor )) void premain() { +// Otherwise, statically allocated objects that need libmaple may fail. +__attribute__((constructor)) void premain() { init(); } -int main(void) -{ +int main(void) { setup(); - while (1) { + while (true) { loop(); } return 0; } - diff --git a/examples/test-servo.cpp b/examples/test-servo.cpp new file mode 100644 index 0000000..b6b8cd5 --- /dev/null +++ b/examples/test-servo.cpp @@ -0,0 +1,152 @@ +/* + * Basic Servo library test program. + * + * Setup: + * + * - Connect a potentiometer to POT_PIN (default pin 15) + * - Connect an oscilloscope to SERVO_PIN1 (default pin 5) and + * SERVO_PIN2 (default pin 6). + * - Connect a serial monitor to SerialUSB + * + * The potentiometer controls the target angle for each of two Servo + * objects, one with angles in [-90, 90], and another in [0, 180]. + * Servo pulse width range is [1000, 2000]. + * + * Serial2 will tell you what inputs it's giving to each servo object, + * and some information it gets back. Pressing the button + * detaches/reattaches the Servo objects. + * + * Tests you should perform: + * + * - Check calculated pulse widths for each servo's target angle + * - Check that calculated pulse widths match actual pulse widths + * - Check that the period of the pulse train is roughly 20 ms + * - Check that the pulses stop when detached, and resume when reattached + * - Check that Servo::write() and Servo::read() round-trip properly + * + * This file is released into the public domain. + */ + +#include <stdio.h> + +#include "wirish.h" + +#include "libraries/Servo/Servo.h" + +#define POT_PIN 15 + +#define MIN_PW 1000 +#define MAX_PW 2000 + +#define SERVO_PIN1 5 +#define MIN_ANGLE1 0 +#define MAX_ANGLE1 180 + +#define SERVO_PIN2 6 +#define MIN_ANGLE2 (-90) +#define MAX_ANGLE2 90 + +Servo servo1; +Servo servo2; + +#define BUF_SIZE 100 +char buf[BUF_SIZE]; + +#define print_buf(fmt, ...) do { \ + snprintf(buf, BUF_SIZE, fmt, __VA_ARGS__); \ + Serial2.println(buf); } while (0) + +int averageAnalogReads(int); +void attach(); +void detach(); + +void setup() { + pinMode(POT_PIN, INPUT_ANALOG); + pinMode(BOARD_BUTTON_PIN, INPUT); + pinMode(BOARD_LED_PIN, OUTPUT); + + Serial2.begin(9600); + + servo1.attach(SERVO_PIN1, MIN_PW, MAX_PW, MIN_ANGLE1, MAX_ANGLE1); + servo2.attach(SERVO_PIN2, MIN_PW, MAX_PW, MIN_ANGLE2, MAX_ANGLE2); + + ASSERT(servo1.attachedPin() == SERVO_PIN1); + ASSERT(servo2.attachedPin() == SERVO_PIN2); +} + +void loop() { + delay(250); + toggleLED(); + + if (isButtonPressed()) { + if (servo1.attached()) detach(); + else attach(); + } + + if (!servo1.attached()) return; + + int32 average = averageAnalogReads(250); + int16 angle1 = (int16)map(average, 0, 4095, MIN_ANGLE1, MAX_ANGLE1); + int16 angle2 = (int16)map(average, 0, 4095, MIN_ANGLE2, MAX_ANGLE2); + + print_buf("pot reading = %d, angle 1 = %d, angle 2 = %d.", + average, angle1, angle2); + + servo1.write(angle1); + servo2.write(angle2); + + int16 read1 = servo1.read(); + int16 read2 = servo2.read(); + + print_buf("write/read angle 1: %d/%d, angle 2: %d/%d", + angle1, read1, angle2, read2); + + ASSERT(abs(angle1 - read1) <= 1); + ASSERT(abs(angle2 - read2) <= 1); + + print_buf("pulse width 1: %d, pulse width 2: %d", + servo1.readMicroseconds(), servo2.readMicroseconds()); + + Serial2.println("\n--------------------------\n"); +} + +int32 averageAnalogReads(int n) { + uint64 total = 0; + + for (int i = 0; i < n; i++) { + total += analogRead(POT_PIN); + } + + return (int32)(total / n); +} + +void attach() { + Serial2.println("attaching"); + servo1.attach(SERVO_PIN1); + servo2.attach(SERVO_PIN2); + ASSERT(servo1.attachedPin() == SERVO_PIN1); + ASSERT(servo2.attachedPin() == SERVO_PIN2); +} + +void detach() { + Serial2.println("detaching"); + servo1.detach(); + servo2.detach(); + ASSERT(!servo1.attached()); + ASSERT(!servo2.attached()); +} + +// Force init to be called *first*, i.e. before static object allocation. +// Otherwise, statically allocated objects that need libmaple may fail. +__attribute__((constructor)) void premain() { + init(); +} + +int main(void) { + setup(); + + while (true) { + loop(); + } + return 0; +} diff --git a/examples/test-session.cpp b/examples/test-session.cpp index 845547d..959c1b8 100644 --- a/examples/test-session.cpp +++ b/examples/test-session.cpp @@ -1,112 +1,93 @@ // Interactive Test Session for LeafLabs Maple // Copyright (c) 2010 LeafLabs LLC. // -// Useful for testing Maple features and troubleshooting. Select a COMM port -// (SerialUSB or Serial2) before compiling and then enter 'h' at the prompt -// for a list of commands. +// Useful for testing Maple features and troubleshooting. +// Communicates over SerialUSB. #include "wirish.h" -#define LED_PIN BOARD_LED_PIN -#define PWM_PIN 3 - -// choose your weapon -#define COMM SerialUSB -//#define COMM Serial2 -//#define COMM Serial3 - - +// ASCII escape character #define ESC ((uint8)27) -int rate = 0; - -#if defined(BOARD_maple) -const uint8 pwm_pins[] = - {0, 1, 2, 3, 5, 6, 7, 8, 9, 11, 12, 14, 24, 25, 27, 28}; -const uint8 adc_pins[] = - {0, 1, 2, 10, 11, 12, 13, 15, 16, 17, 18, 19, 20, 27, 28}; +// Default USART baud rate +#define BAUD 9600 -#elif defined(BOARD_maple_mini) -const uint8 pwm_pins[] = {3, 4, 5, 8, 9, 10, 11, 15, 16, 25, 26, 27}; -const uint8 adc_pins[] = {3, 4, 5, 6, 7, 8, 9, 10, 11, 33}; // NB: 33 is LED +uint8 gpio_state[BOARD_NR_GPIO_PINS]; -#elif defined(BOARD_maple_native) -const uint8 pwm_pins[] = {12, 13, 14, 15, 22, 23, 24, 25, 37, 38, 45, - 46, 47, 48, 49, 50, 53, 54}; -const uint8 adc_pins[] = {6, 7, 8, 9, 10, 11, - /* the following are on ADC3, which lacks support: - 39, 40, 41, 42, 43, 45, */ - 46, 47, 48, 49, 50, 51, 52, 53, 54}; - -#else -#error "Board type has not been selected correctly" - -#endif - -uint8 gpio_state[NR_GPIO_PINS]; - -const char* const dummy_dat = ("qwertyuiopasdfghjklzxcvbnmmmmmm,./1234567890-=" - "qwertyuiopasdfghjklzxcvbnm,./1234567890"); +const char* dummy_data = ("qwertyuiopasdfghjklzxcvbnmmmmmm,./1234567890-=" + "qwertyuiopasdfghjklzxcvbnm,./1234567890"); +// Commands void cmd_print_help(void); void cmd_adc_stats(void); void cmd_stressful_adc_stats(void); void cmd_everything(void); void cmd_serial1_serial3(void); +void cmd_serial1_echo(void); void cmd_gpio_monitoring(void); void cmd_sequential_adc_reads(void); void cmd_gpio_qa(void); -void cmd_sequential_gpio_writes(void); +void cmd_sequential_gpio_toggling(void); void cmd_gpio_toggling(void); +void cmd_sequential_debug_gpio_toggling(void); +void cmd_debug_gpio_toggling(void); +void cmd_but_test(void); void cmd_sequential_pwm_test(void); -void cmd_pwm_sweep(void); void cmd_servo_sweep(void); +void cmd_board_info(void); +// Helper functions void measure_adc_noise(uint8 pin); void fast_gpio(int pin); -void do_serials(HardwareSerial **serials, int n, unsigned baud); +void serial_baud_test(HardwareSerial **serials, int n, unsigned baud); +void serial_echo_test(HardwareSerial *serial, unsigned baud); void init_all_timers(uint16 prescale); +void enable_usarts(void); +void disable_usarts(void); +void print_board_array(const char* msg, const uint8 arr[], int len); + +// -- setup() and loop() ------------------------------------------------------ void setup() { // Set up the LED to blink pinMode(BOARD_LED_PIN, OUTPUT); // Start up the serial ports - Serial1.begin(9600); - Serial2.begin(9600); - Serial3.begin(9600); - - // Send a message out over COMM interface - COMM.println(" "); - COMM.println(" __ __ _ _"); - COMM.println(" | \\/ | __ _ _ __ | | ___| |"); - COMM.println(" | |\\/| |/ _` | '_ \\| |/ _ \\ |"); - COMM.println(" | | | | (_| | |_) | | __/_|"); - COMM.println(" |_| |_|\\__,_| .__/|_|\\___(_)"); - COMM.println(" |_|"); - COMM.println(" by leaflabs"); - COMM.println(""); - COMM.println(""); - COMM.println("Maple interactive test program (type '?' for help)"); - COMM.println("----------------------------------------------------------"); - COMM.print("> "); + Serial1.begin(BAUD); + Serial2.begin(BAUD); + Serial3.begin(BAUD); + + // Send a message out over SerialUSB interface + SerialUSB.println(" "); + SerialUSB.println(" __ __ _ _"); + SerialUSB.println(" | \\/ | __ _ _ __ | | ___| |"); + SerialUSB.println(" | |\\/| |/ _` | '_ \\| |/ _ \\ |"); + SerialUSB.println(" | | | | (_| | |_) | | __/_|"); + SerialUSB.println(" |_| |_|\\__,_| .__/|_|\\___(_)"); + SerialUSB.println(" |_|"); + SerialUSB.println(" by leaflabs"); + SerialUSB.println(""); + SerialUSB.println(""); + SerialUSB.println("Maple interactive test program (type '?' for help)"); + SerialUSB.println("----------------------------------------------------------"); + SerialUSB.print("> "); } void loop () { toggleLED(); - delay(100); + delay(250); - while(COMM.available()) { - uint8 input = COMM.read(); - COMM.println(input); + while (SerialUSB.available()) { + uint8 input = SerialUSB.read(); + SerialUSB.println(input); switch(input) { case '\r': break; case ' ': - COMM.println("spacebar, nice!"); + SerialUSB.println("spacebar, nice!"); break; case '?': @@ -128,8 +109,12 @@ void loop () { cmd_serial1_serial3(); break; + case 'E': + cmd_serial1_echo(); + break; + case '.': - while(!COMM.available()) { + while (!SerialUSB.available()) { Serial1.print("."); Serial2.print("."); Serial3.print("."); @@ -150,33 +135,45 @@ void loop () { break; case 'W': - while(!COMM.available()) { - Serial1.print(dummy_dat); - Serial2.print(dummy_dat); - Serial3.print(dummy_dat); + while (!SerialUSB.available()) { + Serial1.print(dummy_data); + Serial2.print(dummy_data); + Serial3.print(dummy_data); } break; case 'U': - COMM.println("Dumping data to USB. Press any key."); - while(!COMM.available()) { - SerialUSB.print(dummy_dat); + SerialUSB.println("Dumping data to USB. Press any key."); + while (!SerialUSB.available()) { + SerialUSB.print(dummy_data); } break; case 'g': - cmd_sequential_gpio_writes(); + cmd_sequential_gpio_toggling(); break; case 'G': cmd_gpio_toggling(); break; + case 'j': + cmd_sequential_debug_gpio_toggling(); + break; + + case 'J': + cmd_debug_gpio_toggling(); + break; + + case 'B': + cmd_but_test(); + break; + case 'f': - COMM.println("Wiggling D4 as fast as possible in bursts. " + SerialUSB.println("Wiggling D4 as fast as possible in bursts. " "Press any key."); - pinMode(4,OUTPUT); - while(!COMM.available()) { + pinMode(4, OUTPUT); + while (!SerialUSB.available()) { fast_gpio(4); delay(1); } @@ -186,19 +183,19 @@ void loop () { cmd_sequential_pwm_test(); break; - case 'P': - cmd_pwm_sweep(); - break; - case '_': - COMM.println("Delaying for 5 seconds..."); + SerialUSB.println("Delaying for 5 seconds..."); delay(5000); break; + // Be sure to update cmd_print_help() if you implement these: + case 't': // TODO + SerialUSB.println("Unimplemented."); break; case 'T': // TODO + SerialUSB.println("Unimplemented."); break; case 's': @@ -206,26 +203,30 @@ void loop () { break; case 'd': - COMM.println("Pulling down D4, D22. Press any key."); - pinMode(22,INPUT_PULLDOWN); - pinMode(4,INPUT_PULLDOWN); - while(!COMM.available()) { + SerialUSB.println("Pulling down D4, D22. Press any key."); + pinMode(22, INPUT_PULLDOWN); + pinMode(4, INPUT_PULLDOWN); + while (!SerialUSB.available()) { continue; } - COMM.println("Pulling up D4, D22. Press any key."); - pinMode(22,INPUT_PULLUP); - pinMode(4,INPUT_PULLUP); - while(!COMM.available()) { + SerialUSB.println("Pulling up D4, D22. Press any key."); + pinMode(22, INPUT_PULLUP); + pinMode(4, INPUT_PULLUP); + while (!SerialUSB.available()) { continue; } - COMM.read(); - pinMode(4,OUTPUT); + SerialUSB.read(); + pinMode(4, OUTPUT); break; + // Be sure to update cmd_print_help() if you implement these: + case 'i': // TODO + SerialUSB.println("Unimplemented."); break; case 'I': // TODO + SerialUSB.println("Unimplemented."); break; case 'r': @@ -236,403 +237,662 @@ void loop () { cmd_sequential_adc_reads(); break; + case 'b': + cmd_board_info(); + break; + case '+': cmd_gpio_qa(); break; default: // ------------------------------- - COMM.print("Unexpected: "); - COMM.print(input); - COMM.println(", press h for help."); + SerialUSB.print("Unexpected byte: 0x"); + SerialUSB.print((int)input, HEX); + SerialUSB.println(", press h for help."); } - COMM.print("> "); + SerialUSB.print("> "); } } -void cmd_print_help(void) { - COMM.println(""); - //COMM.println("Command Listing\t(# means any digit)"); - COMM.println("Command Listing"); - COMM.println("\t?: print this menu"); - COMM.println("\th: print this menu"); - COMM.println("\tw: print Hello World on all 3 USARTS"); - COMM.println("\tn: measure noise and do statistics"); - COMM.println("\tN: measure noise and do statistics with background stuff"); - COMM.println("\ta: show realtime ADC info"); - COMM.println("\t.: echo '.' until new input"); - COMM.println("\tu: print Hello World on USB"); - COMM.println("\t_: do as little as possible for a couple seconds (delay)"); - COMM.println("\tp: test all PWM channels sequentially"); - COMM.println("\tW: dump data as fast as possible on all 3 USARTS"); - COMM.println("\tU: dump data as fast as possible on USB"); - COMM.println("\tg: toggle all GPIOs sequentialy"); - COMM.println("\tG: toggle all GPIOs at the same time"); - COMM.println("\tf: toggle GPIO D4 as fast as possible in bursts"); - COMM.println("\tP: simultaneously test all PWM channels with different " - "speeds/sweeps"); - COMM.println("\tr: Monitor and print GPIO status changes"); - COMM.println("\ts: output a sweeping servo PWM on all PWM channels"); - COMM.println("\tm: output data on USART1 and USART3 with various rates"); - COMM.println("\t+: test shield mode (for QA, will disrupt Serial2!)"); - - COMM.println("Unimplemented:"); - COMM.println("\te: do everything all at once until new input"); - COMM.println("\tt: output a 1khz squarewave on all GPIOs"); - COMM.println("\tT: output a 1hz squarewave on all GPIOs"); - COMM.println("\ti: print out a bunch of info about system state"); - COMM.println("\tI: print out status of all headers"); -} - -void measure_adc_noise(uint8 pin) { // TODO - uint16 data[100]; - float mean = 0; - //float stddev = 0; - float delta = 0; - float M2 = 0; - pinMode(pin, INPUT_ANALOG); +// -- Commands ---------------------------------------------------------------- - // variance algorithm from knuth; see wikipedia - // checked against python - for(int i = 0; i<100; i++) { - data[i] = analogRead(pin); - delta = data[i] - mean; - mean = mean + delta/(i+1); - M2 = M2 + delta*(data[i] - mean); - } - - //sqrt is broken? - //stddev = sqrt(variance); - COMM.print("header: D"); COMM.print(pin,DEC); - COMM.print("\tn: "); COMM.print(100,DEC); - COMM.print("\tmean: "); COMM.print(mean); - COMM.print("\tvariance: "); COMM.println(M2/99.0); - pinMode(pin, OUTPUT); +void cmd_print_help(void) { + SerialUSB.println(""); + SerialUSB.println("Command Listing"); + SerialUSB.println("\t?: print this menu"); + SerialUSB.println("\th: print this menu"); + SerialUSB.println("\tw: print Hello World on all 3 USARTS"); + SerialUSB.println("\tn: measure noise and do statistics"); + SerialUSB.println("\tN: measure noise and do statistics with background " + "stuff"); + SerialUSB.println("\ta: show realtime ADC info"); + SerialUSB.println("\t.: echo '.' until new input"); + SerialUSB.println("\tu: print Hello World on USB"); + SerialUSB.println("\t_: do as little as possible for a couple seconds " + "(delay)"); + SerialUSB.println("\tp: test all PWM channels sequentially"); + SerialUSB.println("\tW: dump data as fast as possible on all 3 USARTS"); + SerialUSB.println("\tU: dump data as fast as possible on USB"); + SerialUSB.println("\tg: toggle GPIOs sequentially"); + SerialUSB.println("\tG: toggle GPIOs at the same time"); + SerialUSB.println("\tj: toggle debug port GPIOs sequentially"); + SerialUSB.println("\tJ: toggle debug port GPIOs simultaneously"); + SerialUSB.println("\tB: test the built-in button"); + SerialUSB.println("\tf: toggle pin 4 as fast as possible in bursts"); + SerialUSB.println("\tr: monitor and print GPIO status changes"); + SerialUSB.println("\ts: output a sweeping servo PWM on all PWM channels"); + SerialUSB.println("\tm: output data on USART1 and USART3 at various " + "baud rates"); + SerialUSB.println("\tE: echo data on USART1 at various baud rates"); + SerialUSB.println("\tb: print information about the board."); + SerialUSB.println("\t+: test shield mode (for quality assurance testing)"); + + SerialUSB.println("Unimplemented:"); + SerialUSB.println("\te: do everything all at once until new input"); + SerialUSB.println("\tt: output a 1khz squarewave on all GPIOs"); + SerialUSB.println("\tT: output a 1hz squarewave on all GPIOs"); + SerialUSB.println("\ti: print out a bunch of info about system state"); + SerialUSB.println("\tI: print out status of all headers"); } void cmd_adc_stats(void) { - COMM.println("Taking ADC noise stats..."); + SerialUSB.println("Taking ADC noise stats."); digitalWrite(BOARD_LED_PIN, 0); - for(uint32 i = 0; i<sizeof(adc_pins); i++) { + for (uint32 i = 0; i < BOARD_NR_ADC_PINS; i++) { delay(5); - measure_adc_noise(adc_pins[i]); + measure_adc_noise(boardADCPins[i]); } } void cmd_stressful_adc_stats(void) { - COMM.println("Taking ADC noise stats under duress..."); - digitalWrite(BOARD_LED_PIN, 0); - for(uint32 i = 0; i<sizeof(adc_pins); i++) { - // spool up PWM - for(uint32 j = 2; j<(uint32)sizeof(pwm_pins); j++) { - if(adc_pins[i] != pwm_pins[j]) { - pinMode(pwm_pins[j],PWM); - pwmWrite(pwm_pins[j], 1000 + i); + SerialUSB.println("Taking ADC noise stats under duress."); + + for (uint32 i = 0; i < BOARD_NR_ADC_PINS; i++) { + for (uint32 j = 0; j < BOARD_NR_PWM_PINS; j++) { + if (boardADCPins[i] != boardPWMPins[j]) { + pinMode(boardPWMPins[j], PWM); + pwmWrite(boardPWMPins[j], 1000 + i); } } - SerialUSB.print(dummy_dat); - SerialUSB.print(dummy_dat); - measure_adc_noise(adc_pins[i]); - for(uint32 j = 2; j<(uint32)sizeof(pwm_pins); j++) { - if(adc_pins[i] != pwm_pins[j]) { - pinMode(pwm_pins[j],OUTPUT); - digitalWrite(pwm_pins[j],0); + + Serial1.print(dummy_data); + + measure_adc_noise(boardADCPins[i]); + + for (uint32 j = 0; j < BOARD_NR_PWM_PINS; j++) { + if (boardADCPins[i] != boardPWMPins[j]) { + pinMode(boardPWMPins[j], OUTPUT); + digitalWrite(boardPWMPins[j], LOW); } } } } void cmd_everything(void) { // TODO + // Be sure to update cmd_print_help() if you implement this. + // print to usart // print to usb // toggle gpios // enable pwm - COMM.println("(unimplemented)"); -} - -void fast_gpio(int maple_pin) { - GPIO_Port *port = PIN_MAP[maple_pin].port; - uint32 pin = PIN_MAP[maple_pin].pin; - - gpio_write_bit(port, pin, 1); gpio_write_bit(port, pin, 0); - gpio_write_bit(port, pin, 1); gpio_write_bit(port, pin, 0); - gpio_write_bit(port, pin, 1); gpio_write_bit(port, pin, 0); - gpio_write_bit(port, pin, 1); gpio_write_bit(port, pin, 0); - gpio_write_bit(port, pin, 1); gpio_write_bit(port, pin, 0); - gpio_write_bit(port, pin, 1); gpio_write_bit(port, pin, 0); - gpio_write_bit(port, pin, 1); gpio_write_bit(port, pin, 0); - gpio_write_bit(port, pin, 1); gpio_write_bit(port, pin, 0); - gpio_write_bit(port, pin, 1); gpio_write_bit(port, pin, 0); - gpio_write_bit(port, pin, 1); gpio_write_bit(port, pin, 0); - gpio_write_bit(port, pin, 1); gpio_write_bit(port, pin, 0); - gpio_write_bit(port, pin, 1); gpio_write_bit(port, pin, 0); - gpio_write_bit(port, pin, 1); gpio_write_bit(port, pin, 0); + SerialUSB.println("Unimplemented."); } void cmd_serial1_serial3(void) { HardwareSerial *serial_1_and_3[] = {&Serial1, &Serial3}; - COMM.println("Testing 57600 baud on USART1 and USART3. Press any key."); - do_serials(serial_1_and_3, 2, 57600); - COMM.read(); + SerialUSB.println("Testing 57600 baud on USART1 and USART3. " + "Press any key to stop."); + serial_baud_test(serial_1_and_3, 2, 57600); + SerialUSB.read(); - COMM.println("Testing 115200 baud on USART1 and USART3. Press any key."); - do_serials(serial_1_and_3, 2, 115200); - COMM.read(); + SerialUSB.println("Testing 115200 baud on USART1 and USART3. " + "Press any key to stop."); + serial_baud_test(serial_1_and_3, 2, 115200); + SerialUSB.read(); - COMM.println("Testing 9600 baud on USART1 and USART3. Press any key."); - do_serials(serial_1_and_3, 2, 9600); - COMM.read(); + SerialUSB.println("Testing 9600 baud on USART1 and USART3. " + "Press any key to stop."); + serial_baud_test(serial_1_and_3, 2, 9600); + SerialUSB.read(); - COMM.println("Resetting USART1 and USART3..."); - Serial1.begin(9600); - Serial3.begin(9600); + SerialUSB.println("Resetting USART1 and USART3..."); + Serial1.begin(BAUD); + Serial3.begin(BAUD); } -void do_serials(HardwareSerial **serials, int n, unsigned baud) { - for (int i = 0; i < n; i++) { - serials[i]->begin(9600); - } - while (!COMM.available()) { - for (int i = 0; i < n; i++) { - serials[i]->println(dummy_dat); - if (serials[i]->available()) { - serials[i]->println(serials[i]->read()); - delay(1000); - } - } - } +void cmd_serial1_echo(void) { + SerialUSB.println("Testing serial echo at various baud rates. " + "Press any key for next baud rate, or ESC to quit " + "early."); + while (!SerialUSB.available()) + ; + + if (SerialUSB.read() == ESC) return; + SerialUSB.println("Testing 115200 baud on USART1."); + serial_echo_test(&Serial1, 115200); + + if (SerialUSB.read() == ESC) return; + SerialUSB.println("Testing 57600 baud on USART1."); + serial_echo_test(&Serial1, 57600); + + if (SerialUSB.read() == ESC) return; + SerialUSB.println("Testing 9600 baud on USART1."); + serial_echo_test(&Serial1, 9600); } void cmd_gpio_monitoring(void) { - COMM.println("Monitoring GPIO read state changes. Press any key."); - digitalWrite(BOARD_LED_PIN, 0); - // make sure to skip the TX/RX headers - for(int i = 2; i<NR_GPIO_PINS; i++) { + SerialUSB.println("Monitoring pin state changes. Press any key to stop."); + + for (int i = 0; i < BOARD_NR_GPIO_PINS; i++) { + if (boardUsesPin(i)) + continue; pinMode(i, INPUT_PULLDOWN); gpio_state[i] = (uint8)digitalRead(i); } - while(!COMM.available()) { - for(int i = 2; i<NR_GPIO_PINS; i++) { + + while (!SerialUSB.available()) { + for (int i = 0; i < BOARD_NR_GPIO_PINS; i++) { + if (boardUsesPin(i)) + continue; + uint8 current_state = (uint8)digitalRead(i); - if(current_state != gpio_state[i]) { - COMM.print("State change on header D"); - COMM.print(i,DEC); - if(current_state) COMM.println(":\tHIGH"); - else COMM.println(":\tLOW"); + if (current_state != gpio_state[i]) { + SerialUSB.print("State change on pin "); + SerialUSB.print(i, DEC); + if (current_state) { + SerialUSB.println(":\tHIGH"); + } else { + SerialUSB.println(":\tLOW"); + } gpio_state[i] = current_state; } } } - for(int i = 2; i<NR_GPIO_PINS; i++) { + + for (int i = 0; i < BOARD_NR_GPIO_PINS; i++) { + if (boardUsesPin(i)) + continue; pinMode(i, OUTPUT); } } void cmd_sequential_adc_reads(void) { - COMM.print("Sequentially reading each ADC port."); - COMM.println("Press any key for next port, or ESC to stop."); - digitalWrite(LED_PIN, 0); - // make sure to skip the TX/RX headers - for(uint32 i = 2; i<sizeof(adc_pins); i++) { - COMM.print("Reading on header D"); - COMM.print(adc_pins[i], DEC); - COMM.println("..."); - pinMode(adc_pins[i], INPUT_ANALOG); - while(!COMM.available()) { - int sample = analogRead(adc_pins[i]); - COMM.print(adc_pins[i],DEC); - COMM.print("\t"); - COMM.print(sample,DEC); - COMM.print("\t"); - COMM.print("|"); - for(int j = 0; j<4096; j+= 100) { - if(sample >= j) COMM.print("#"); - else COMM.print(" "); + SerialUSB.print("Sequentially reading most ADC ports."); + SerialUSB.println("Press any key for next port, or ESC to stop."); + + for (uint32 i = 0; i < BOARD_NR_ADC_PINS; i++) { + if (boardUsesPin(boardADCPins[i])) + continue; + + SerialUSB.print("Reading pin "); + SerialUSB.print(boardADCPins[i], DEC); + SerialUSB.println("..."); + pinMode(boardADCPins[i], INPUT_ANALOG); + while (!SerialUSB.available()) { + int sample = analogRead(boardADCPins[i]); + SerialUSB.print(boardADCPins[i], DEC); + SerialUSB.print("\t"); + SerialUSB.print(sample, DEC); + SerialUSB.print("\t"); + SerialUSB.print("|"); + for (int j = 0; j < 4096; j += 100) { + if (sample >= j) { + SerialUSB.print("#"); + } else { + SerialUSB.print(" "); + } } - COMM.print("| "); - for(int j = 0; j<12; j++) { - if(sample & (1 << (11-j))) COMM.print("1"); - else COMM.print("0"); + SerialUSB.print("| "); + for (int j = 0; j < 12; j++) { + if (sample & (1 << (11 - j))) { + SerialUSB.print("1"); + } else { + SerialUSB.print("0"); + } } - COMM.println(""); + SerialUSB.println(""); + } + pinMode(boardADCPins[i], OUTPUT); + digitalWrite(boardADCPins[i], 0); + if (SerialUSB.read() == ESC) + break; + } +} + +bool test_single_pin_is_high(int high_pin, const char* err_msg) { + bool ok = true; + for (int i = 0; i < BOARD_NR_GPIO_PINS; i++) { + if (boardUsesPin(i)) continue; + + if (digitalRead(i) == HIGH && i != high_pin) { + SerialUSB.println(); + SerialUSB.print("\t*** FAILURE! pin "); + SerialUSB.print(i, DEC); + SerialUSB.print(' '); + SerialUSB.println(err_msg); + ok = false; + } + } + return ok; +} + +bool wait_for_low_transition(uint8 pin) { + uint32 start = millis(); + while (millis() - start < 2000) { + if (digitalRead(pin) == LOW) { + return true; } - pinMode(adc_pins[i], OUTPUT); - digitalWrite(adc_pins[i], 0); - if((uint8)COMM.read() == ESC) break; } + return false; } void cmd_gpio_qa(void) { - COMM.println("Doing QA testing for most GPIO pins..."); - digitalWrite(BOARD_LED_PIN, 0); - for(int i = 0; i<NR_GPIO_PINS; i++) { + bool all_pins_ok = true; + const int not_a_pin = -1; + SerialUSB.println("Doing QA testing for unused GPIO pins."); + + for (int i = 0; i < BOARD_NR_GPIO_PINS; i++) { + if (boardUsesPin(i)) continue; + pinMode(i, INPUT); - gpio_state[i] = 0; } - COMM.println("Waiting to start..."); - while(digitalRead(0) != 1 && !COMM.available()) { - continue; - } - for(int i=0; i<38; i++) { - if(i == BOARD_LED_PIN) { - COMM.println("Not checking LED"); + + SerialUSB.println("Waiting to start."); + ASSERT(!boardUsesPin(0)); + while (digitalRead(0) == LOW) continue; + + for (int i = 0; i < BOARD_NR_GPIO_PINS; i++) { + if (boardUsesPin(i)) { + SerialUSB.print("Skipping pin "); + SerialUSB.println(i, DEC); continue; } - COMM.print("Checking D"); - COMM.print(i,DEC); - while(digitalRead(i) == 0) continue; - for(int j=0; j<NR_GPIO_PINS; j++) { - if(digitalRead(j) && j!=i) { - COMM.print(": FAIL ########################### D"); - COMM.println(j, DEC); - break; - } + bool pin_ok = true; + SerialUSB.print("Checking pin "); + SerialUSB.print(i, DEC); + while (digitalRead(i) == LOW) continue; + + pin_ok = pin_ok && test_single_pin_is_high(i, "is also HIGH"); + + if (!wait_for_low_transition(i)) { + SerialUSB.println("Transition to low timed out; something is " + "very wrong. Aborting test."); + return; } - while(digitalRead(i) == 1) continue; - for(int j=0; j<NR_GPIO_PINS; j++) { - if(digitalRead(j) && j!=i) { - COMM.print(": FAIL ########################### D"); - COMM.println(j, DEC); - break; - } + + pin_ok = pin_ok && test_single_pin_is_high(not_a_pin, "is still HIGH"); + + if (pin_ok) { + SerialUSB.println(": ok"); } - COMM.println(": Ok!"); + + all_pins_ok = all_pins_ok && pin_ok; + } + + if (all_pins_ok) { + SerialUSB.println("Finished; test passes."); + } else { + SerialUSB.println("**** TEST FAILS *****"); } - for(int i = 0; i<NR_GPIO_PINS; i++) { + + for (int i = 0; i < BOARD_NR_GPIO_PINS; i++) { + if (boardUsesPin(i)) continue; + pinMode(i, OUTPUT); - digitalWrite(i, 0); + digitalWrite(i, LOW); + gpio_state[i] = 0; } } -void cmd_sequential_gpio_writes(void) { - COMM.print("Sequentially toggling all pins except D0, D1. "); - COMM.println("Anything for next, ESC to stop."); - digitalWrite(BOARD_LED_PIN, 0); - // make sure to skip the TX/RX headers - for(uint32 i = 2; i<NR_GPIO_PINS; i++) { - COMM.print("GPIO write out on header D"); - COMM.print(i, DEC); - COMM.println("..."); +void cmd_sequential_gpio_toggling(void) { + SerialUSB.println("Sequentially toggling all unused pins. " + "Press any key for next pin, ESC to stop."); + + for (uint32 i = 0; i < BOARD_NR_GPIO_PINS; i++) { + if (boardUsesPin(i)) + continue; + + SerialUSB.print("Toggling pin "); + SerialUSB.print((int)i, DEC); + SerialUSB.println("..."); + pinMode(i, OUTPUT); do { togglePin(i); - } while(!COMM.available()); - digitalWrite(i, 0); - if((uint8)COMM.read() == ESC) break; + } while (!SerialUSB.available()); + + digitalWrite(i, LOW); + if (SerialUSB.read() == ESC) + break; } } void cmd_gpio_toggling(void) { - COMM.println("Toggling all GPIOs simultaneously. Press any key."); - digitalWrite(BOARD_LED_PIN, 0); - // make sure to skip the TX/RX headers - for(uint32 i = 2; i<NR_GPIO_PINS; i++) { + SerialUSB.println("Toggling all unused pins simultaneously. " + "Press any key to stop."); + + for (uint32 i = 0; i < BOARD_NR_GPIO_PINS; i++) { + if (boardUsesPin(i)) + continue; pinMode(i, OUTPUT); } - while(!COMM.available()) { - for(uint32 i = 2; i<NR_GPIO_PINS; i++) { + + while (!SerialUSB.available()) { + for (uint32 i = 0; i < BOARD_NR_GPIO_PINS; i++) { + if (boardUsesPin(i)) + continue; togglePin(i); } } - for(uint32 i = 2; i<NR_GPIO_PINS; i++) { - digitalWrite(i, 0); + + for (uint32 i = 0; i < BOARD_NR_GPIO_PINS; i++) { + if (boardUsesPin(i)) + continue; + digitalWrite(i, LOW); } } -void cmd_sequential_pwm_test(void) { - COMM.println("Sequentially testing PWM on all possible headers " - "except D0 and D1."); - COMM.println("Press any key for next, ESC to stop."); - digitalWrite(BOARD_LED_PIN, 0); - // make sure to skip the TX/RX headers - for(uint32 i = 2; i<sizeof(pwm_pins); i++) { - COMM.print("PWM out on header D"); - COMM.print(pwm_pins[i], DEC); - COMM.println("..."); - pinMode(pwm_pins[i], PWM); - pwmWrite(pwm_pins[i], 16000); - while(!COMM.available()) { delay(10); } - pinMode(pwm_pins[i], OUTPUT); - digitalWrite(pwm_pins[i], 0); - if((uint8)COMM.read() == ESC) break; - } -} - -void cmd_pwm_sweep(void) { - COMM.println("Testing all PWM ports with a sweep. Press any key."); - digitalWrite(BOARD_LED_PIN, 0); - // make sure to skip the TX/RX pins - for(uint32 i = 2; i<sizeof(pwm_pins); i++) { - pinMode(pwm_pins[i], PWM); - pwmWrite(pwm_pins[i], 4000); +uint8 debugGPIOPins[] = {BOARD_JTMS_SWDIO_PIN, + BOARD_JTCK_SWCLK_PIN, + BOARD_JTDI_PIN, + BOARD_JTDO_PIN, + BOARD_NJTRST_PIN}; + +#define N_DEBUG_PINS 5 + +void cmd_sequential_debug_gpio_toggling(void) { + SerialUSB.println("Toggling all debug (JTAG/SWD) pins sequentially. " + "This will permanently disable debug port " + "functionality."); + disableDebugPorts(); + + for (int i = 0; i < N_DEBUG_PINS; i++) { + pinMode(debugGPIOPins[i], OUTPUT); } - while(!COMM.available()) { - rate += 20; - if(rate > 65500) rate = 0; - for(uint32 i = 2; i<sizeof(pwm_pins); i++) { - pwmWrite(pwm_pins[i], rate); + + for (int i = 0; i < N_DEBUG_PINS; i++) { + int pin = debugGPIOPins[i]; + SerialUSB.print("Toggling pin "); + SerialUSB.print(pin, DEC); + SerialUSB.println("..."); + + pinMode(pin, OUTPUT); + do { + togglePin(pin); + } while (!SerialUSB.available()); + + digitalWrite(pin, LOW); + if (SerialUSB.read() == ESC) + break; + } + + for (int i = 0; i < N_DEBUG_PINS; i++) { + digitalWrite(debugGPIOPins[i], 0); + } +} + +void cmd_debug_gpio_toggling(void) { + SerialUSB.println("Toggling debug GPIO simultaneously. " + "This will permanently disable JTAG and Serial Wire " + "debug port functionality. " + "Press any key to stop."); + disableDebugPorts(); + + for (uint32 i = 0; i < N_DEBUG_PINS; i++) { + pinMode(debugGPIOPins[i], OUTPUT); + } + + while (!SerialUSB.available()) { + for (uint32 i = 0; i < N_DEBUG_PINS; i++) { + togglePin(debugGPIOPins[i]); } - delay(1); } - for(uint32 i = 2; i<sizeof(pwm_pins); i++) { - pinMode(pwm_pins[i], OUTPUT); + + for (uint32 i = 0; i < N_DEBUG_PINS; i++) { + digitalWrite(debugGPIOPins[i], LOW); + } +} + +void cmd_but_test(void) { + SerialUSB.println("Press the button to test. Press any key to stop."); + pinMode(BOARD_BUTTON_PIN, INPUT); + + while (!SerialUSB.available()) { + if (isButtonPressed()) { + uint32 tstamp = millis(); + SerialUSB.print("Button press detected, timestamp: "); + SerialUSB.println(tstamp); + } + } + SerialUSB.read(); +} + +void cmd_sequential_pwm_test(void) { + SerialUSB.println("Sequentially testing PWM on all unused pins. " + "Press any key for next pin, ESC to stop."); + + for (uint32 i = 0; i < BOARD_NR_PWM_PINS; i++) { + if (boardUsesPin(i)) + continue; + + SerialUSB.print("PWM out on header D"); + SerialUSB.print(boardPWMPins[i], DEC); + SerialUSB.println("..."); + pinMode(boardPWMPins[i], PWM); + pwmWrite(boardPWMPins[i], 16000); + + while (!SerialUSB.available()) { + delay(10); + } + + pinMode(boardPWMPins[i], OUTPUT); + digitalWrite(boardPWMPins[i], 0); + if (SerialUSB.read() == ESC) + break; } } void cmd_servo_sweep(void) { - COMM.println("Testing all PWM headers with a servo sweep. Press any key."); - COMM.println(); - digitalWrite(BOARD_LED_PIN, 0); + SerialUSB.println("Testing all PWM headers with a servo sweep. " + "Press any key to stop."); + SerialUSB.println(); + + disable_usarts(); init_all_timers(21); - // make sure to skip the TX/RX headers - for(uint32 i = 2; i<sizeof(pwm_pins); i++) { - pinMode(pwm_pins[i], PWM); - pwmWrite(pwm_pins[i], 4000); + + for (uint32 i = 0; i < BOARD_NR_PWM_PINS; i++) { + if (boardUsesPin(i)) + continue; + pinMode(boardPWMPins[i], PWM); + pwmWrite(boardPWMPins[i], 4000); } + // 1.25ms = 4096counts = 0deg // 1.50ms = 4915counts = 90deg // 1.75ms = 5734counts = 180deg - rate = 4096; - while(!COMM.available()) { + int rate = 4096; + while (!SerialUSB.available()) { rate += 20; - if(rate > 5734) rate = 4096; - for(uint32 i = 2; i<sizeof(pwm_pins); i++) { - pwmWrite(pwm_pins[i], rate); + if (rate > 5734) + rate = 4096; + for (uint32 i = 0; i < BOARD_NR_PWM_PINS; i++) { + if (boardUsesPin(i)) + continue; + pwmWrite(boardPWMPins[i], rate); } delay(20); } - for(uint32 i = 2; i<sizeof(pwm_pins); i++) { - pinMode(pwm_pins[i], OUTPUT); + + for (uint32 i = 0; i < BOARD_NR_PWM_PINS; i++) { + if (boardUsesPin(i)) + continue; + pinMode(boardPWMPins[i], OUTPUT); } init_all_timers(1); - Serial2.begin(9600); - COMM.println("(reset serial port)"); + enable_usarts(); +} + +void cmd_board_info(void) { // TODO print more information + SerialUSB.println("Board information"); + SerialUSB.println("================="); + + SerialUSB.print("* Clock speed (MHz): "); + SerialUSB.println(CYCLES_PER_MICROSECOND); + + SerialUSB.print("* BOARD_LED_PIN: "); + SerialUSB.println(BOARD_LED_PIN); + + SerialUSB.print("* BOARD_BUTTON_PIN: "); + SerialUSB.println(BOARD_BUTTON_PIN); + + SerialUSB.print("* GPIO information (BOARD_NR_GPIO_PINS = "); + SerialUSB.print(BOARD_NR_GPIO_PINS); + SerialUSB.println("):"); + print_board_array("ADC pins", boardADCPins, BOARD_NR_ADC_PINS); + print_board_array("PWM pins", boardPWMPins, BOARD_NR_PWM_PINS); + print_board_array("Used pins", boardUsedPins, BOARD_NR_USED_PINS); +} + +// -- Helper functions -------------------------------------------------------- + +void measure_adc_noise(uint8 pin) { + uint16 data[100]; + float mean = 0; + float delta = 0; + float M2 = 0; + pinMode(pin, INPUT_ANALOG); + + // Variance algorithm from Welford, via Knuth, by way of Wikipedia: + // http://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#On-line_algorithm + for (int i = 0; i < 100; i++) { + data[i] = analogRead(pin); + delta = data[i] - mean; + mean = mean + delta / (i + 1); + M2 = M2 + delta * (data[i] - mean); + } + + SerialUSB.print("header: D"); + SerialUSB.print(pin, DEC); + SerialUSB.print("\tn: "); + SerialUSB.print(100, DEC); + SerialUSB.print("\tmean: "); + SerialUSB.print(mean); + SerialUSB.print("\tvariance: "); + SerialUSB.println(M2 / 99.0); + pinMode(pin, OUTPUT); +} + +void fast_gpio(int maple_pin) { + gpio_dev *dev = PIN_MAP[maple_pin].gpio_device; + uint32 bit = PIN_MAP[maple_pin].gpio_bit; + + gpio_write_bit(dev, bit, 1); + gpio_toggle_bit(dev, bit); + gpio_toggle_bit(dev, bit); + gpio_toggle_bit(dev, bit); + gpio_toggle_bit(dev, bit); + gpio_toggle_bit(dev, bit); + gpio_toggle_bit(dev, bit); + gpio_toggle_bit(dev, bit); + gpio_toggle_bit(dev, bit); + gpio_toggle_bit(dev, bit); + gpio_toggle_bit(dev, bit); + gpio_toggle_bit(dev, bit); + gpio_toggle_bit(dev, bit); + gpio_toggle_bit(dev, bit); + gpio_toggle_bit(dev, bit); + gpio_toggle_bit(dev, bit); + gpio_toggle_bit(dev, bit); + gpio_toggle_bit(dev, bit); + gpio_toggle_bit(dev, bit); + gpio_toggle_bit(dev, bit); + gpio_toggle_bit(dev, bit); + gpio_toggle_bit(dev, bit); + gpio_toggle_bit(dev, bit); + gpio_toggle_bit(dev, bit); + gpio_toggle_bit(dev, bit); + gpio_toggle_bit(dev, bit); +} + +void serial_baud_test(HardwareSerial **serials, int n, unsigned baud) { + for (int i = 0; i < n; i++) { + serials[i]->begin(baud); + } + while (!SerialUSB.available()) { + for (int i = 0; i < n; i++) { + serials[i]->println(dummy_data); + if (serials[i]->available()) { + serials[i]->println(serials[i]->read()); + delay(1000); + } + } + } +} + +void serial_echo_test(HardwareSerial *serial, unsigned baud) { + serial->begin(baud); + while (!SerialUSB.available()) { + if (!serial->available()) + continue; + serial->print(serial->read()); + } +} + +static uint16 init_all_timers_prescale = 0; + +static void set_prescale(timer_dev *dev) { + timer_set_prescaler(dev, init_all_timers_prescale); } void init_all_timers(uint16 prescale) { - timer_init(TIMER1, prescale); - timer_init(TIMER2, prescale); - timer_init(TIMER3, prescale); -#if NR_TIMERS >= 4 - timer_init(TIMER4, prescale); -#elif NR_TIMERS >= 8 // TODO test this on maple native - timer_init(TIMER5, prescale); - timer_init(TIMER6, prescale); - timer_init(TIMER7, prescale); - timer_init(TIMER8, prescale); + init_all_timers_prescale = prescale; + timer_foreach(set_prescale); +} + +void enable_usarts(void) { + Serial1.begin(BAUD); + Serial2.begin(BAUD); + Serial3.begin(BAUD); +#if defined(STM32_HIGH_DENSITY) && !defined(BOARD_maple_RET6) + Serial4.begin(BAUD); + Serial5.begin(BAUD); #endif } +void disable_usarts(void) { + Serial1.end(); + Serial2.end(); + Serial3.end(); +#if defined(STM32_HIGH_DENSITY) && !defined(BOARD_maple_RET6) + Serial4.end(); + Serial5.end(); +#endif +} + +void print_board_array(const char* msg, const uint8 arr[], int len) { + SerialUSB.print("\t"); + SerialUSB.print(msg); + SerialUSB.print(" ("); + SerialUSB.print(len); + SerialUSB.print("): "); + for (int i = 0; i < len; i++) { + SerialUSB.print(arr[i], DEC); + if (i < len - 1) SerialUSB.print(", "); + } + SerialUSB.println(); +} + +// -- premain() and main() ---------------------------------------------------- // Force init to be called *first*, i.e. before static object allocation. -// Otherwise, statically allocated object that need libmaple may fail. -__attribute__(( constructor )) void premain() { +// Otherwise, statically allocated objects that need libmaple may fail. +__attribute__((constructor)) void premain() { init(); } -int main(void) -{ +int main(void) { setup(); while (1) { diff --git a/examples/test-spi-roundtrip.cpp b/examples/test-spi-roundtrip.cpp new file mode 100644 index 0000000..8fe97b9 --- /dev/null +++ b/examples/test-spi-roundtrip.cpp @@ -0,0 +1,194 @@ +/* + * Polling SPI loopback test. + * + * Bob is nowhere to be found, so Alice decides to talk to herself. + * + * Instructions: Connect SPI2 (Alice) to herself (i.e., MISO to MOSI). + * Connect to Alice via SerialUSB. Press any key to start. + * + * Alice will talk to herself for a little while. The sketch will + * report if Alice can't hear anything she says. She'll then start + * talking forever at various frequencies, bit orders, and modes. Use + * an oscilloscope to make sure she's not trying to lie about any of + * those things. + * + * This file is released into the public domain. + * + * Author: Marti Bolivar <mbolivar@leaflabs.com> + */ + +#include "wirish.h" + +HardwareSPI alice(2); + +#define NFREQS 8 +const SPIFrequency spi_freqs[] = { + SPI_140_625KHZ, + SPI_281_250KHZ, + SPI_562_500KHZ, + SPI_1_125MHZ, + SPI_2_25MHZ, + SPI_4_5MHZ, + SPI_9MHZ, + SPI_18MHZ, +}; + +#define TEST_BUF_SIZE 10 +uint8 test_buf[TEST_BUF_SIZE] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + +void bad_assert(const char* file, int line, const char* exp) { + SerialUSB.println(); + SerialUSB.print("ERROR: FAILED ASSERT("); + SerialUSB.print(exp); + SerialUSB.print("): "); + SerialUSB.print(file); + SerialUSB.print(": "); + SerialUSB.println(line); + throb(); +} + +#undef ASSERT +#define ASSERT(exp) \ + if (exp) { \ + } else { \ + bad_assert(__FILE__, __LINE__, #exp); \ + } + +void haveConversation(uint32 bitOrder); +void soliloquies(uint32 bitOrder); + +void setup() { + pinMode(BOARD_LED_PIN, OUTPUT); + while (!SerialUSB.available()) + ; + SerialUSB.read(); +} + +void loop() { + SerialUSB.println("** Having a conversation, MSB first"); + haveConversation(MSBFIRST); + + SerialUSB.println("** Having a conversation, LSB first"); + haveConversation(LSBFIRST); + + SerialUSB.println(); + SerialUSB.println("*** All done! It looks like everything worked."); + SerialUSB.println(); + + SerialUSB.println("** Alice will now wax eloquent in various styles. " + "Press any key for the next configuration."); + soliloquies(MSBFIRST); + soliloquies(LSBFIRST); + + while (true) + ; +} + +void printFrequencyString(SPIFrequency frequency); +void chat(SPIFrequency frequency, uint32 bitOrder, uint32 mode); + +void haveConversation(uint32 bitOrder) { + for (int f = 0; f < NFREQS; f++) { + for (int mode = 0; mode < 4; mode++) { + chat(spi_freqs[f], bitOrder, mode); + delay(10); + } + } +} + +void chat(SPIFrequency frequency, uint32 bitOrder, uint32 mode) { + SerialUSB.print("Having a chat.\tFrequency: "); + printFrequencyString(frequency); + SerialUSB.print(",\tbitOrder: "); + SerialUSB.print(bitOrder == MSBFIRST ? "MSB" : "LSB"); + SerialUSB.print(",\tmode: "); + SerialUSB.print(mode); + SerialUSB.print("."); + + SerialUSB.print(" [1] "); + alice.begin(frequency, bitOrder, mode); + + SerialUSB.print(" [2] "); + uint32 txed = 0; + while (txed < TEST_BUF_SIZE) { + ASSERT(alice.transfer(test_buf[txed]) == test_buf[txed]); + txed++; + } + + SerialUSB.print(" [3] "); + alice.end(); + + SerialUSB.println(" ok."); +} + +void soliloquy(SPIFrequency freq, uint32 bitOrder, uint32 mode); + +void soliloquies(uint32 bitOrder) { + for (int f = 0; f < NFREQS; f++) { + for (int mode = 0; mode < 4; mode++) { + soliloquy(spi_freqs[f], bitOrder, mode); + } + } +} + +void soliloquy(SPIFrequency frequency, uint32 bitOrder, uint32 mode) { + const uint8 repeat = 0xAE; + SerialUSB.print("Alice is giving a soliloquy (repeating 0x"); + SerialUSB.print(repeat, HEX); + SerialUSB.print("). Frequency: "); + printFrequencyString(frequency); + SerialUSB.print(", bitOrder: "); + SerialUSB.print(bitOrder == MSBFIRST ? "big-endian" : "little-endian"); + SerialUSB.print(", SPI mode: "); + SerialUSB.println(mode); + + alice.begin(frequency, bitOrder, mode); + while (!SerialUSB.available()) { + alice.write(repeat); + delayMicroseconds(200); + } + SerialUSB.read(); +} + +void printFrequencyString(SPIFrequency frequency) { + switch (frequency) { + case SPI_18MHZ: + SerialUSB.print("18 MHz"); + break; + case SPI_9MHZ: + SerialUSB.print("9 MHz"); + break; + case SPI_4_5MHZ: + SerialUSB.print("4.5 MHz"); + break; + case SPI_2_25MHZ: + SerialUSB.print("2.25 MHZ"); + break; + case SPI_1_125MHZ: + SerialUSB.print("1.125 MHz"); + break; + case SPI_562_500KHZ: + SerialUSB.print("562.500 KHz"); + break; + case SPI_281_250KHZ: + SerialUSB.print("281.250 KHz"); + break; + case SPI_140_625KHZ: + SerialUSB.print("140.625 KHz"); + break; + } +} + +// Force init to be called *first*, i.e. before static object allocation. +// Otherwise, statically allocated objects that need libmaple may fail. +__attribute__((constructor)) void premain() { + init(); +} + +int main(void) { + setup(); + while (true) { + loop(); + } + return 0; +} diff --git a/examples/test-systick.cpp b/examples/test-systick.cpp index 247892d..78c7307 100644 --- a/examples/test-systick.cpp +++ b/examples/test-systick.cpp @@ -1,59 +1,48 @@ // Tests the SysTick enable/disable functions -// + #include "wirish.h" #include "systick.h" -#define LED_PIN 13 -#define PWM_PIN 2 -#define BUT 38 - -void setup() -{ - /* Set up the LED to blink */ - pinMode(LED_PIN, OUTPUT); - - /* Turn on PWM on pin PWM_PIN */ - pinMode(PWM_PIN, PWM); - pwmWrite(PWM_PIN, 0x8000); - - pinMode(BUT, INPUT_PULLDOWN); +void setup() { + pinMode(BOARD_LED_PIN, OUTPUT); + pinMode(BOARD_BUTTON_PIN, INPUT); } -int toggle = 0; +bool disable = true; long time = 0; void loop() { - toggle ^= 1; - digitalWrite(LED_PIN, toggle); + volatile int i = 0; + toggleLED(); // An artificial delay - int16 i = 1; - float j = 1; - for(i=0; i<6553; i++) { - j = sqrt(j) + 1; - } - - if(digitalRead(BUT)) { - systick_disable(); - } else { - systick_resume(); + for(i = 0; i < 150000; i++) + ; + + if (isButtonPressed()) { + if (disable) { + systick_disable(); + SerialUSB.println("Disabling SysTick"); + } else { + SerialUSB.println("Re-enabling SysTick"); + systick_enable(); + } + disable = !disable; } - //SerialUSB.println(micros()); // there is a bug with this SerialUSB.println(millis()); } // Force init to be called *first*, i.e. before static object allocation. // Otherwise, statically allocated object that need libmaple may fail. - __attribute__(( constructor )) void premain() { +__attribute__((constructor)) void premain() { init(); } -int main(void) -{ +int main(void) { setup(); - while (1) { + while (true) { loop(); } return 0; diff --git a/examples/test-timers.cpp b/examples/test-timers.cpp index ccba251..247cc57 100644 --- a/examples/test-timers.cpp +++ b/examples/test-timers.cpp @@ -1,8 +1,7 @@ -// Program to test the wirish timers implementation +// Program to test the timer.h implementation's essential functionality. #include "wirish.h" - -#define LED_PIN 13 +#include "timer.h" void handler1(void); void handler2(void); @@ -12,7 +11,6 @@ void handler4(void); void handler3b(void); void handler4b(void); -int toggle = 0; int t; int count1 = 0; @@ -28,222 +26,271 @@ uint16 val2 = 10000; uint16 val3 = 10000; uint16 val4 = 10000; -HardwareTimer Timers[] = {Timer1, Timer2, Timer3, Timer4}; +// FIXME [0.1.0] high density timer test (especially basic timers + DAC) +timer_dev *timers[] = {TIMER1, TIMER2, TIMER3, TIMER4}; +voidFuncPtr handlers[] = {handler1, handler2, handler3, handler4}; + +void initTimer(timer_dev *dev); +void setTimerPeriod(timer_dev *dev, uint32 period_us); +void testSetTimerPeriod(uint32 period); +void testTimerChannels(timer_dev *dev); +int timerNumber(timer_dev *dev); -void setup() -{ - /* Set up the LED to blink */ - pinMode(LED_PIN, OUTPUT); +void setup() { + // Set up the LED to blink + pinMode(BOARD_LED_PIN, OUTPUT); // Setup the button as input - pinMode(38, INPUT_PULLUP); - - /* Send a message out USART2 */ - //SerialUSB.begin(9600); - SerialUSB.println("Begining timer test..."); - for(int t=0; t<4; t++) { - Timers[t].setChannel1Mode(TIMER_OUTPUTCOMPARE); - Timers[t].setChannel2Mode(TIMER_OUTPUTCOMPARE); - Timers[t].setChannel3Mode(TIMER_OUTPUTCOMPARE); - Timers[t].setChannel4Mode(TIMER_OUTPUTCOMPARE); - } - - // Wait for user to attach... - delay(2000); + pinMode(BOARD_BUTTON_PIN, INPUT); + + // Send a message out Serial2 + Serial2.begin(115200); + Serial2.println("*** Initializing timers..."); + timer_foreach(initTimer); + Serial2.println("*** Done. Beginning timer test."); } void loop() { - SerialUSB.println("-----------------------------------------------------"); - SerialUSB.println("Testing setCount/getCount"); - SerialUSB.print("Timer1.getCount() = "); SerialUSB.println(Timer1.getCount()); - SerialUSB.println("Timer1.setCount(1234)"); - Timer1.setCount(1234); - SerialUSB.print("Timer1.getCount() = "); SerialUSB.println(Timer1.getCount()); - // This tests whether the pause/resume functions work; when BUT is held - // down Timer4 is in the "pause" state and the timer doesn't increment, so - // the final counts should reflect the ratio of time that BUT was held down - SerialUSB.println("-----------------------------------------------------"); - SerialUSB.println("Testing Pause/Resume; button roughly controls Timer4"); + Serial2.println("-----------------------------------------------------"); + + Serial2.println("Testing timer_get_count()/timer_set_count()"); + Serial2.print("TIMER1 count = "); + Serial2.println(timer_get_count(TIMER1)); + Serial2.println("timer_set_count(TIMER1, 1234)"); + timer_set_count(TIMER1, 1234); + Serial2.print("timer_get_count(TIMER1) = "); + Serial2.println(timer_get_count(TIMER1)); + + Serial2.println("-----------------------------------------------------"); + Serial2.println("Testing pause/resume; button roughly controls TIMER4"); + // when BUT is held down, TIMER4 is in the "pause" state and the + // timer doesn't increment, so the final counts should reflect the + // ratio of time that BUT was held down. count3 = 0; count4 = 0; - 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); - Timer4.setCompare1(1000); - Timer3.attachCompare1Interrupt(handler3b); - Timer4.attachCompare1Interrupt(handler4b); - Timer3.resume(); - Timer4.resume(); - SerialUSB.println("~4 seconds..."); - for(int i = 0; i<4000; i++) { - if(digitalRead(38)) { - Timer4.pause(); + timer_set_mode(TIMER3, TIMER_CH1, TIMER_OUTPUT_COMPARE); + timer_set_mode(TIMER4, TIMER_CH1, TIMER_OUTPUT_COMPARE); + timer_pause(TIMER3); + timer_pause(TIMER4); + timer_set_count(TIMER3, 0); + timer_set_count(TIMER4, 0); + timer_set_reload(TIMER3, 30000); + timer_set_reload(TIMER4, 30000); + timer_set_compare(TIMER3, 1, 1000); + timer_set_compare(TIMER4, 1, 1000); + timer_attach_interrupt(TIMER3, TIMER_CC1_INTERRUPT, handler3b); + timer_attach_interrupt(TIMER4, TIMER_CC1_INTERRUPT, handler4b); + timer_resume(TIMER3); + timer_resume(TIMER4); + + Serial2.println("Testing for ~4 seconds..."); + for(int i = 0; i < 4000; i++) { + if (isButtonPressed()) { + timer_pause(TIMER4); } else { - Timer4.resume(); + timer_resume(TIMER4); } delay(1); } - Timer3.setChannel1Mode(TIMER_DISABLED); - Timer4.setChannel1Mode(TIMER_DISABLED); - SerialUSB.print("Count3: "); SerialUSB.println(count3); - SerialUSB.print("Count4: "); SerialUSB.println(count4); - - // These test the setPeriod auto-configure functionality - SerialUSB.println("-----------------------------------------------------"); - SerialUSB.println("Testing setPeriod"); - Timer4.setChannel1Mode(TIMER_OUTPUTCOMPARE); - Timer4.setCompare1(1); - Timer4.setPeriod(10); - Timer4.pause(); - Timer4.setCount(0); - Timer4.attachCompare1Interrupt(handler4b); - SerialUSB.println("Period 10ms, wait 2 seconds..."); - count4 = 0; - Timer4.resume(); - delay(2000); - Timer4.pause(); - Timer4.setChannel1Mode(TIMER_DISABLED); - SerialUSB.print("Count4: "); SerialUSB.println(count4); - SerialUSB.println("(should be around 2sec/10ms = 200000)"); - Timer4.setChannel1Mode(TIMER_OUTPUTCOMPARE); - Timer4.setCompare1(1); - Timer4.pause(); - Timer4.setPeriod(30000); - Timer4.setCount(0); - Timer4.attachCompare1Interrupt(handler4b); - SerialUSB.println("Period 30000ms, wait 2 seconds..."); - count4 = 0; - Timer4.resume(); - delay(2000); - Timer4.pause(); - Timer4.setChannel1Mode(TIMER_DISABLED); - SerialUSB.print("Count4: "); SerialUSB.println(count4); - SerialUSB.println("(should be around 2sec/30000ms ~ 67)"); - - Timer4.setChannel1Mode(TIMER_OUTPUTCOMPARE); - Timer4.setPeriod(300000); - Timer4.setCompare1(1); - Timer4.pause(); - Timer4.setCount(0); - Timer4.attachCompare1Interrupt(handler4b); - SerialUSB.println("Period 300000ms, wait 2 seconds..."); - count4 = 0; - Timer4.resume(); - delay(2000); - Timer4.pause(); - Timer4.setChannel1Mode(TIMER_DISABLED); - SerialUSB.print("Count4: "); SerialUSB.println(count4); - SerialUSB.println("(should be around 2sec/300000ms ~ 6.7)"); - - Timer4.setChannel1Mode(TIMER_OUTPUTCOMPARE); - Timer4.setPrescaleFactor(33); - Timer4.setOverflow(65454); - Timer4.pause(); - Timer4.setCount(0); - Timer4.setCompare1(1); - Timer4.attachCompare1Interrupt(handler4b); - SerialUSB.println("Period 30000ms, wait 2 seconds..."); + + timer_set_mode(TIMER3, TIMER_CH1, TIMER_DISABLED); + timer_set_mode(TIMER4, TIMER_CH1, TIMER_DISABLED); + + Serial2.print("TIMER3 count: "); + Serial2.println(timer_get_count(TIMER3)); + Serial2.print("TIMER4 count: "); + Serial2.println(timer_get_count(TIMER4)); + + Serial2.println("-----------------------------------------------------"); + Serial2.println("Testing setTimerPeriod()"); + testSetTimerPeriod(10); + testSetTimerPeriod(30000); + testSetTimerPeriod(300000); + testSetTimerPeriod(30000); + + Serial2.println("Sanity check (with hand-coded reload and prescaler for " + "72 MHz timers):"); + timer_set_mode(TIMER4, TIMER_CH1, TIMER_OUTPUT_COMPARE); + timer_set_prescaler(TIMER4, 33); + timer_set_reload(TIMER4, 65454); + timer_pause(TIMER4); + timer_set_count(TIMER4, 0); + timer_set_compare(TIMER4, TIMER_CH1, 1); + timer_attach_interrupt(TIMER4, TIMER_CC1_INTERRUPT, handler4b); + Serial2.println("Period 30000ms, wait 2 seconds..."); count4 = 0; - Timer4.resume(); + timer_resume(TIMER4); delay(2000); - Timer4.pause(); - Timer4.setChannel1Mode(TIMER_DISABLED); - SerialUSB.print("Count4: "); SerialUSB.println(count4); - SerialUSB.println("(should be around 2sec/30000ms ~ 67)"); - - Timer4.setChannel1Mode(TIMER_OUTPUTCOMPARE); - Timer4.setCompare1(1); - Timer4.setPeriod(30000); - Timer4.pause(); - Timer4.setCount(0); - Timer4.attachCompare1Interrupt(handler4b); - SerialUSB.println("Period 30000ms, wait 2 seconds..."); + timer_pause(TIMER4); + timer_set_mode(TIMER4, TIMER_CH1, TIMER_DISABLED); + Serial2.print("TIMER4 count: "); + Serial2.println(count4); + Serial2.println(" (Should be around 2sec/30000ms ~ 67)"); + + // Test all the individual timer channels + timer_foreach(testTimerChannels); +} + +void initTimer(timer_dev *dev) { + switch (dev->type) { + case TIMER_ADVANCED: + case TIMER_GENERAL: + Serial2.print("Initializing timer "); + Serial2.println(timerNumber(dev)); + for (int c = 1; c <= 4; c++) { + timer_set_mode(dev, c, TIMER_OUTPUT_COMPARE); + } + Serial2.println("Done."); + break; + case TIMER_BASIC: + break; + } +} + +void testSetTimerPeriod(uint32 period) { + timer_set_mode(TIMER4, TIMER_CH1, TIMER_OUTPUT_COMPARE); + timer_set_compare(TIMER4, TIMER_CH1, 1); + setTimerPeriod(TIMER4, period); + timer_pause(TIMER4); + timer_set_count(TIMER4, 0); + timer_attach_interrupt(TIMER4, TIMER_CC1_INTERRUPT, handler4b); + Serial2.println("Period "); + Serial2.print(period); + Serial2.print(" ms. Waiting 2 seconds..."); count4 = 0; - Timer4.resume(); + timer_resume(TIMER4); delay(2000); - Timer4.pause(); - Timer4.setChannel1Mode(TIMER_DISABLED); - SerialUSB.print("Count4: "); SerialUSB.println(count4); - SerialUSB.println("(should be around 2sec/30000ms ~ 67)"); - - // This section is to touch every channel of every timer. The output - // ratios should reflect the ratios of the rate variables. Demonstrates - // that over time the actual timing rates get blown away by other system - // interrupts. - for(t=0; t<4; t++) { - toggle ^= 1; digitalWrite(LED_PIN, toggle); - delay(100); - SerialUSB.println("-----------------------------------------------------"); - SerialUSB.print("Testing Timer "); SerialUSB.println(t+1); + timer_pause(TIMER4); + timer_set_mode(TIMER4, TIMER_CH1, TIMER_DISABLED); + Serial2.print("TIMER4 count: "); + Serial2.println(timer_get_count(TIMER4)); + Serial2.print(" (Should be around 2 sec / "); + Serial2.print(period); + Serial2.print(" ms = "); + Serial2.print(double(2) / period * 1000); + Serial2.println(", modulo delays due to interrupts)"); +} + +int timerNumber(timer_dev *dev) { + switch (dev->clk_id) { + case RCC_TIMER1: + return 1; + case RCC_TIMER2: + return 2; + case RCC_TIMER3: + return 3; + case RCC_TIMER4: + return 4; +#ifdef STM32_HIGH_DENSITY + case RCC_TIMER5: + return 5; + case RCC_TIMER6: + return 6; + case RCC_TIMER7: + return 7; + case RCC_TIMER8: + return 8; +#endif + default: + ASSERT(0); + return 0; + } +} + +/* This function touches every channel of a given timer. The output + * ratios should reflect the ratios of the rate variables. It + * demonstrates that, over time, the actual timing rates get blown + * away by other system interrupts. */ +void testTimerChannels(timer_dev *dev) { + t = timerNumber(dev); + toggleLED(); + delay(100); + Serial2.println("-----------------------------------------------------"); + switch (dev->type) { + case TIMER_BASIC: + Serial2.print("NOT testing channels for basic timer "); + Serial2.println(t); + break; + case TIMER_ADVANCED: + case TIMER_GENERAL: + Serial2.print("Testing channels for timer "); + Serial2.println(t); + timer_pause(dev); count1 = count2 = count3 = count4 = 0; - Timers[t].setOverflow(0xFFFF); - Timers[t].setPrescaleFactor(1); - Timers[t].setCompare1(65535); - Timers[t].setCompare2(65535); - Timers[t].setCompare3(65535); - Timers[t].setCompare4(65535); - Timers[t].setChannel1Mode(TIMER_OUTPUTCOMPARE); - Timers[t].setChannel2Mode(TIMER_OUTPUTCOMPARE); - Timers[t].setChannel3Mode(TIMER_OUTPUTCOMPARE); - Timers[t].setChannel4Mode(TIMER_OUTPUTCOMPARE); - Timers[t].attachCompare1Interrupt(handler1); - Timers[t].attachCompare2Interrupt(handler2); - Timers[t].attachCompare3Interrupt(handler3); - Timers[t].attachCompare4Interrupt(handler4); - Timers[t].resume(); + timer_set_reload(dev, 0xFFFF); + timer_set_prescaler(dev, 1); + for (int c = 1; c <= 4; c++) { + timer_set_compare(dev, c, 65535); + timer_set_mode(dev, c, TIMER_OUTPUT_COMPARE); + timer_attach_interrupt(dev, c, handlers[c - 1]); + } + timer_resume(dev); delay(3000); - Timers[t].setChannel1Mode(TIMER_DISABLED); - Timers[t].setChannel2Mode(TIMER_DISABLED); - Timers[t].setChannel3Mode(TIMER_DISABLED); - Timers[t].setChannel4Mode(TIMER_DISABLED); - SerialUSB.print("Count1: "); SerialUSB.println(count1); - SerialUSB.print("Count2: "); SerialUSB.println(count2); - SerialUSB.print("Count3: "); SerialUSB.println(count3); - SerialUSB.print("Count4: "); SerialUSB.println(count4); + for (int c = 1; c <= 4; c++) { + timer_set_mode(dev, c, TIMER_DISABLED); + } + Serial2.print("Channel 1 count: "); Serial2.println(count1); + Serial2.print("Channel 2 count: "); Serial2.println(count2); + Serial2.print("Channel 3 count: "); Serial2.println(count3); + Serial2.print("Channel 4 count: "); Serial2.println(count4); + break; + } +} + +// FIXME [0.1.0] move some incarnation of this into timer.h +void setTimerPeriod(timer_dev *dev, uint32 period_us) { + if (!period_us) { + // FIXME handle this case + ASSERT(0); + return; } + uint32 cycles = period_us * CYCLES_PER_MICROSECOND; + uint16 pre = (uint16)((cycles >> 16) + 1); + timer_set_prescaler(dev, pre); + timer_set_reload(dev, cycles / pre - 1); } void handler1(void) { val1 += rate1; - Timers[t].setCompare1(val1); + timer_set_compare(timers[t], TIMER_CH1, val1); count1++; -} +} + void handler2(void) { val2 += rate2; - Timers[t].setCompare2(val2); + timer_set_compare(timers[t], TIMER_CH2, val2); count2++; -} +} + void handler3(void) { val3 += rate3; - Timers[t].setCompare3(val3); + timer_set_compare(timers[t], TIMER_CH3, val3); count3++; -} +} + void handler4(void) { val4 += rate4; - Timers[t].setCompare4(val4); + timer_set_compare(timers[t], TIMER_CH4, val4); count4++; -} +} void handler3b(void) { count3++; -} +} + void handler4b(void) { count4++; -} +} +__attribute__((constructor)) void premain() { + init(); +} int main(void) { - init(); setup(); - while (1) { + while (true) { loop(); } return 0; diff --git a/examples/test-usart-dma.cpp b/examples/test-usart-dma.cpp new file mode 100644 index 0000000..b9c03f1 --- /dev/null +++ b/examples/test-usart-dma.cpp @@ -0,0 +1,127 @@ +/** + * @file test-usart-dma.cpp + * @author Marti Bolivar <mbolivar@leaflabs.com> + * + * Simple test of DMA used with a USART receiver. + * + * Configures a USART receiver for use with DMA. Received bytes are + * placed into a buffer, with an interrupt firing when the buffer is + * full. At that point, the USART transmitter will print the contents + * of the byte buffer. The buffer is continually filled and refilled + * in this manner. + * + * This example isn't very robust; don't use it in production. In + * particular, since the buffer keeps filling (DMA_CIRC_MODE is set), + * if you keep typing after filling the buffer, you'll overwrite + * earlier bytes; this may happen before those earlier bytes are done + * printing. + * + * This code is released into the public domain. + */ + +#include "dma.h" +#include "usart.h" +#include "gpio.h" + +#include "wirish.h" + +#define BAUD 9600 + +#define USART USART2 +#define USART_HWSER Serial2 +#define USART_DMA_DEV DMA1 +#define USART_RX_DMA_CHANNEL DMA_CH6 +#define USART_TX BOARD_USART2_TX_PIN +#define USART_RX BOARD_USART2_RX_PIN + +#define BUF_SIZE 8 +uint8 rx_buf[BUF_SIZE]; + +dma_irq_cause irq_cause; + +__io uint32 irq_fired = 0; + +void init_usart(void); +void init_dma_xfer(void); +void rx_dma_irq(void); + +void setup(void) { + pinMode(BOARD_LED_PIN, OUTPUT); + + init_dma_xfer(); + init_usart(); +} + +void loop(void) { + toggleLED(); + delay(100); + + dma_channel_reg_map *ch_regs = dma_channel_regs(USART_DMA_DEV, + USART_RX_DMA_CHANNEL); + if (irq_fired) { + USART_HWSER.println("** IRQ **"); + while (true) + ; + } + USART_HWSER.print("["); + USART_HWSER.print(millis()); + USART_HWSER.print("]\tISR bits: 0x"); + uint8 isr_bits = dma_get_isr_bits(USART_DMA_DEV, USART_RX_DMA_CHANNEL); + USART_HWSER.print((int32)isr_bits, HEX); + USART_HWSER.print("\tCCR: 0x"); + USART_HWSER.print((int64)ch_regs->CCR, HEX); + USART_HWSER.print("\tCNDTR: 0x"); + USART_HWSER.print((int64)ch_regs->CNDTR, HEX); + USART_HWSER.print("\tBuffer contents: "); + for (int i = 0; i < BUF_SIZE; i++) { + USART_HWSER.print('\''); + USART_HWSER.print(rx_buf[i]); + USART_HWSER.print('\''); + if (i < BUF_SIZE - 1) USART_HWSER.print(", "); + } + USART_HWSER.println(); + if (isr_bits == 0x7) { + USART_HWSER.println("** Clearing ISR bits."); + dma_clear_isr_bits(USART_DMA_DEV, USART_RX_DMA_CHANNEL); + } + + irq_fired = 0; +} + +/* Configure USART receiver for use with DMA */ +void init_usart(void) { + USART_HWSER.begin(BAUD); + USART->regs->CR3 = USART_CR3_DMAR; +} + +/* Configure DMA transmission */ +void init_dma_xfer(void) { + dma_init(USART_DMA_DEV); + dma_setup_transfer(USART_DMA_DEV, USART_RX_DMA_CHANNEL, + &USART->regs->DR, DMA_SIZE_8BITS, + rx_buf, DMA_SIZE_8BITS, + (DMA_MINC_MODE | DMA_CIRC_MODE | DMA_TRNS_CMPLT + )); + dma_set_num_transfers(USART_DMA_DEV, USART_RX_DMA_CHANNEL, BUF_SIZE); + // Currently not working: + // dma_attach_interrupt(USART_DMA_DEV, USART_RX_DMA_CHANNEL, rx_dma_irq); + dma_enable(USART_DMA_DEV, USART_RX_DMA_CHANNEL); +} + +void rx_dma_irq(void) { +} + +// Force init to be called *first*, i.e. before static object allocation. +// Otherwise, statically allocated objects that need libmaple may fail. +__attribute__((constructor)) void premain() { + init(); +} + +int main(void) { + setup(); + + while (true) { + loop(); + } + return 0; +} diff --git a/examples/vga-leaf.cpp b/examples/vga-leaf.cpp index d1c6d7d..f31dc87 100644 --- a/examples/vga-leaf.cpp +++ b/examples/vga-leaf.cpp @@ -1,21 +1,21 @@ /* - Crude VGA Output + VGA Output - Outputs a red and white leaf to VGA. This implementation is crude and noisy, - but a fun demo. It should run most VGA monitors at 640x480, though it does - not follow the timing spec very carefully. Real twisted or shielded wires, - proper grounding, and not doing this on a breadboard are recommended (but - it seems to work ok without). + Outputs a red and white leaf to VGA. It should run most VGA monitors + at 640x480, though it does not follow the timing spec very + carefully. Real twisted or shielded wires, proper grounding, and not + doing this on a breadboard are recommended (but it seems to work ok + without). - SerialUSB is disabled to get rid of most interrupts (which mess with timing); - the SysTick is probably the source of the remaining flickers. This means that - you have to use perpetual bootloader or the reset button to flash new - programs. + SerialUSB and SysTick are disabled to get rid of the most frequently + occurring interrupts (which mess with timing). This means that you + have to use perpetual bootloader mode or the reset button to flash + new programs. How to wire this to a VGA port: - D5 via ~200ohms to VGA Red (1) - D6 via ~200ohms to VGA Green (2) - D7 via ~200ohms to VGA Blue (3) + D6 via ~200ohms to VGA Red (1) + D7 via ~200ohms to VGA Green (2) + D8 via ~200ohms to VGA Blue (3) D11 to VGA VSync (14) (swapped?) D12 to VGA HSync (13) (swapped?) GND to VGA Ground (5) @@ -24,48 +24,65 @@ See also: - http://pinouts.ru/Video/VGA15_pinout.shtml - http://www.epanorama.net/documents/pc/vga_timing.html - + Created 20 July 2010 By Bryan Newbold for LeafLabs This code is released with no strings attached. - */ -#include "wirish.h" +// FIXME: generalize for Native and Mini -#define LED_PIN 13 +#include "wirish.h" -// Pinouts -#define VGA_R 5 // STM32: B6 -#define VGA_G 6 // STM32: A8 -#define VGA_B 7 // STM32: A9 +// Pinouts -- you also must change the GPIO macros below if you change +// these +#define VGA_R 6 // STM32: A8 +#define VGA_G 7 // STM32: A9 +#define VGA_B 8 // STM32: A10 #define VGA_V 11 // STM32: A6 #define VGA_H 12 // STM32: A7 -// These low level macros make GPIO writes much faster -#define VGA_R_HIGH (GPIOB_BASE)->BSRR = BIT(6) -#define VGA_R_LOW (GPIOB_BASE)->BRR = BIT(6) -#define VGA_G_HIGH (GPIOA_BASE)->BSRR = BIT(8) -#define VGA_G_LOW (GPIOA_BASE)->BRR = BIT(8) -#define VGA_B_HIGH (GPIOA_BASE)->BSRR = BIT(9) -#define VGA_B_LOW (GPIOA_BASE)->BRR = BIT(9) -#define VGA_V_HIGH (GPIOA_BASE)->BSRR = BIT(6) -#define VGA_V_LOW (GPIOA_BASE)->BRR = BIT(6) -#define VGA_H_HIGH (GPIOA_BASE)->BSRR = BIT(7) -#define VGA_H_LOW (GPIOA_BASE)->BRR = BIT(7) +// These low level (and STM32 specific) macros make GPIO writes much +// faster +#define ABSRR ((volatile uint32*)0x40010810) +#define ABRR ((volatile uint32*)0x40010814) + +#define RBIT 8 // (see pinouts) +#define GBIT 9 +#define BBIT 10 + +#define VGA_R_HIGH *ABSRR = BIT(RBIT) +#define VGA_R_LOW *ABRR = BIT(RBIT) +#define VGA_G_HIGH *ABSRR = BIT(GBIT) +#define VGA_G_LOW *ABRR = BIT(GBIT) +#define VGA_B_HIGH *ABSRR = BIT(BBIT) +#define VGA_B_LOW *ABRR = BIT(BBIT) + +#define ON_COLOR BIT(RBIT) +#define OFF_COLOR (BIT(RBIT) | BIT(GBIT) | BIT(BBIT)) + +// set has priority, so clear every bit and set some given bits: +#define VGA_COLOR(c) (*ABSRR = c | \ + BIT(RBIT+16) | BIT(GBIT+16) | BIT(BBIT+16)) + +#define VGA_V_HIGH *ABSRR = BIT(6) +#define VGA_V_LOW *ABRR = BIT(6) +#define VGA_H_HIGH *ABSRR = BIT(7) +#define VGA_H_LOW *ABRR = BIT(7) void isr_porch(void); void isr_start(void); void isr_stop(void); void isr_update(void); -uint8 toggle; uint16 x = 0; // X coordinate uint16 y = 0; // Y coordinate -uint8 v_active = 1; // Are we in the image? +uint16 logo_y = 0; // Y coordinate, mapped into valid logo index (for speed) +bool v_active = true; // Are we in the image? -// 1-bit! -uint8 logo[18][16] = { +const uint8 x_max = 16; +const uint8 y_max = 18; +uint32 logo[y_max][x_max] = { {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}, {0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,}, {0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,}, @@ -85,9 +102,11 @@ uint8 logo[18][16] = { {0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,}, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}, }; +HardwareTimer timer(4); + void setup() { // Setup our pins - pinMode(LED_PIN, OUTPUT); + pinMode(BOARD_LED_PIN, OUTPUT); pinMode(VGA_R, OUTPUT); pinMode(VGA_G, OUTPUT); pinMode(VGA_B, OUTPUT); @@ -99,91 +118,108 @@ void setup() { digitalWrite(VGA_H, HIGH); digitalWrite(VGA_V, HIGH); + // Fill the logo array with color patterns corresponding to its + // truth value. Note that we could get more tricky here, since + // there are 3 bits of color. + for (int y = 0; y < y_max; y++) { + for (int x = 0; x < x_max; x++) { + logo[y][x] = logo[y][x] ? ON_COLOR : OFF_COLOR; + } + } + // This gets rid of the majority of the interrupt artifacts; + // there's still a glitch for low values of y, but let's not worry + // about that. (Probably due to the hackish way vsync is done). SerialUSB.end(); - SystemTick.end(); + systick_disable(); // Configure - Timer4.pause(); // while we configure - Timer4.setPrescaleFactor(1); // Full speed - Timer4.setChannel1Mode(TIMER_OUTPUTCOMPARE); - Timer4.setChannel2Mode(TIMER_OUTPUTCOMPARE); - Timer4.setChannel3Mode(TIMER_OUTPUTCOMPARE); - Timer4.setChannel4Mode(TIMER_OUTPUTCOMPARE); - Timer4.setOverflow(2287); // Total line time - - Timer4.setCompare1(200); - Timer4.attachCompare1Interrupt(isr_porch); - Timer4.setCompare2(300); - Timer4.attachCompare2Interrupt(isr_start); - Timer4.setCompare3(2170); - Timer4.attachCompare3Interrupt(isr_stop); - Timer4.setCompare4(1); // Could be zero I guess - Timer4.attachCompare4Interrupt(isr_update); - - Timer4.setCount(0); // Ready... - Timer4.resume(); // Go! + timer.pause(); // while we configure + timer.setPrescaleFactor(1); // Full speed + timer.setMode(TIMER_CH1, TIMER_OUTPUT_COMPARE); + timer.setMode(TIMER_CH2, TIMER_OUTPUT_COMPARE); + timer.setMode(TIMER_CH3, TIMER_OUTPUT_COMPARE); + timer.setMode(TIMER_CH4, TIMER_OUTPUT_COMPARE); + timer.setOverflow(2287); // Total line time + + timer.setCompare(TIMER_CH1, 200); + timer.attachInterrupt(TIMER_CH1, isr_porch); + timer.setCompare(TIMER_CH2, 300); + timer.attachInterrupt(TIMER_CH2, isr_start); + timer.setCompare(TIMER_CH3, 2170); + timer.attachInterrupt(TIMER_CH3, isr_stop); + timer.setCompare(TIMER_CH4, 1); // Could be zero, I guess + timer.attachInterrupt(TIMER_CH4, isr_update); + + timer.setCount(0); // Ready... + timer.resume(); // Go! } void loop() { - toggle ^= 1; - digitalWrite(LED_PIN, toggle); + toggleLED(); delay(100); // Everything happens in the interrupts! } - // This ISR will end horizontal sync for most of the image and // setup the vertical sync for higher line counts void isr_porch(void) { VGA_H_HIGH; y++; + logo_y = map(y, 0, 478, 0, y_max); // Back to the top - if(y>=523) { - y=1; - v_active = 1; + if (y >= 523) { + y = 1; + logo_y = 0; + v_active = true; return; } // Other vsync stuff below the image - if(y>=492) { + if (y >= 492) { VGA_V_HIGH; return; } - if(y>=490) { + if (y >= 490) { VGA_V_LOW; return; } - if(y>=479) { - v_active = 0; + if (y >= 479) { + v_active = false; return; } } // This is the main horizontal sweep -void isr_start(void) { +void isr_start(void) { // Skip if we're not in the image at all - if(!v_active) { return; } + if (!v_active) { + return; + } // Start Red VGA_R_LOW; VGA_R_HIGH; - // For each "pixel" (really 20 or so screen pixels?) go red or white - for(x=0; x<32; x++) { - if(logo[y/28][x/2]) { - VGA_G_HIGH; - VGA_B_HIGH; - } else { - VGA_G_LOW; - VGA_B_LOW; - } + // For each "pixel", go ON_COLOR or OFF_COLOR + for (x = 0; x < 16; x++) { + // setting the color several times is just an easy way to + // delay, so the image is wider. if you only do the following + // once, you'll be able to make the logo array bigger: + VGA_COLOR(logo[logo_y][x]); + VGA_COLOR(logo[logo_y][x]); + VGA_COLOR(logo[logo_y][x]); + VGA_COLOR(logo[logo_y][x]); + VGA_COLOR(logo[logo_y][x]); + VGA_COLOR(logo[logo_y][x]); } } // End of the horizontal line void isr_stop(void) { - if(!v_active) { return; } + if (!v_active) { + return; + } VGA_R_LOW; VGA_G_LOW; VGA_B_LOW; @@ -194,11 +230,14 @@ void isr_update(void) { VGA_H_LOW; } -int main(void) { +__attribute__((constructor)) void premain() { init(); +} + +int main(void) { setup(); - while (1) { + while (true) { loop(); } return 0; diff --git a/examples/vga-scope.cpp b/examples/vga-scope.cpp index 0265f9c..b5fa8a5 100644 --- a/examples/vga-scope.cpp +++ b/examples/vga-scope.cpp @@ -1,144 +1,204 @@ -// Low-level, non-wirish demonstration of VGA -// -// Connect a microphone or something less to ANALOG_PIN +/* + VGA Oscilloscope demo. + + Connect a microphone or something like it to ANALOG_PIN (0V -- 3.3V + only; 0.2V -- 3.1V will probably look nicer); an attached VGA + monitor will display the signal roughly in real-time. + + The thick blue line corresponds roughly to 0V. + + This is a fairy crude hack, but it's fun to watch/toy around with. + + SerialUSB and SysTick are disabled to get rid of the most frequently + occurring interrupts (which mess with timing). This means that you + have to use perpetual bootloader mode or the reset button to flash + new programs. + + How to wire this to a VGA port: + D6 via ~200ohms to VGA Red (1) + D7 via ~200ohms to VGA Green (2) + D8 via ~200ohms to VGA Blue (3) + D11 to VGA VSync (14) (swapped?) + D12 to VGA HSync (13) (swapped?) + GND to VGA Ground (5) + GND to VGA Sync Ground (10) + + See also: + - http://pinouts.ru/Video/VGA15_pinout.shtml + - http://www.epanorama.net/documents/pc/vga_timing.html + + This code is released into the public domain. + + Authors: + + Bryan Newbold <bnewbold@leaflabs.com> + Marti Bolivar <mbolivar@leaflabs.com> + */ #include "wirish.h" +#include "systick.h" + +// FIXME: generalize for Native and Mini -#define LED_PIN 13 #define ANALOG_PIN 18 -#define VGA_R 5 // B6 -#define VGA_G 6 // A8 -#define VGA_B 7 // A9 -#define VGA_V 11 // A6 -#define VGA_H 12 // A7 -#define VGA_R_HIGH (GPIOB_BASE)->BSRR = BIT(6) -#define VGA_R_LOW (GPIOB_BASE)->BRR = BIT(6) -#define VGA_G_HIGH (GPIOA_BASE)->BSRR = BIT(8) -#define VGA_G_LOW (GPIOA_BASE)->BRR = BIT(8) -#define VGA_B_HIGH (GPIOA_BASE)->BSRR = BIT(9) -#define VGA_B_LOW (GPIOA_BASE)->BRR = BIT(9) -#define VGA_V_HIGH (GPIOA_BASE)->BSRR = BIT(6) -#define VGA_V_LOW (GPIOA_BASE)->BRR = BIT(6) -#define VGA_H_HIGH (GPIOA_BASE)->BSRR = BIT(7) -#define VGA_H_LOW (GPIOA_BASE)->BRR = BIT(7) + +// Pinouts -- you also must change the GPIO macros below if you change +// these +#define VGA_R 6 // STM32: A8 +#define VGA_G 7 // STM32: A9 +#define VGA_B 8 // STM32: A10 +#define VGA_V 11 // STM32: A6 +#define VGA_H 12 // STM32: A7 + +// These low level (and STM32 specific) macros make GPIO writes much +// faster +#define ABSRR ((volatile uint32*)0x40010810) +#define ABRR ((volatile uint32*)0x40010814) + +#define RBIT 8 // (see pinouts) +#define GBIT 9 +#define BBIT 10 + +#define VGA_R_HIGH *ABSRR = BIT(RBIT) +#define VGA_R_LOW *ABRR = BIT(RBIT) +#define VGA_G_HIGH *ABSRR = BIT(GBIT) +#define VGA_G_LOW *ABRR = BIT(GBIT) +#define VGA_B_HIGH *ABSRR = BIT(BBIT) +#define VGA_B_LOW *ABRR = BIT(BBIT) + +#define COLOR_WHITE (BIT(RBIT) | BIT(GBIT) | BIT(BBIT)) +#define COLOR_BLACK 0 +#define COLOR_RED BIT(RBIT) +#define COLOR_GREEN BIT(GBIT) +#define COLOR_BLUE BIT(BBIT) + +#define BORDER_COLOR COLOR_BLUE + +// set has priority, so clear every bit and set some given bits: +#define VGA_COLOR(c) (*ABSRR = c | \ + BIT(RBIT + 16) | BIT(GBIT + 16) | BIT(BBIT + 16)) + +#define VGA_V_HIGH *ABSRR = BIT(6) +#define VGA_V_LOW *ABRR = BIT(6) +#define VGA_H_HIGH *ABSRR = BIT(7) +#define VGA_H_LOW *ABRR = BIT(7) void isr_porch(void); void isr_start(void); void isr_stop(void); void isr_update(void); -void setup() -{ - pinMode(LED_PIN, OUTPUT); +void setup() { + pinMode(BOARD_LED_PIN, OUTPUT); pinMode(ANALOG_PIN, INPUT_ANALOG); - digitalWrite(LED_PIN, 1); + digitalWrite(BOARD_LED_PIN, 1); pinMode(VGA_R, OUTPUT); pinMode(VGA_G, OUTPUT); pinMode(VGA_B, OUTPUT); pinMode(VGA_V, OUTPUT); pinMode(VGA_H, OUTPUT); - /* Send a message out USART2 */ + // Send a message out USART2 Serial2.begin(9600); - Serial2.println("Video time..."); + Serial2.println("Time to kill the radio star..."); // This gets rid of the majority of the interrupt artifacts; - // a SysTick.end() is required as well + // there's still a glitch for low values of y, but let's not worry + // about that. (Probably due to the hackish way vsync is done). SerialUSB.end(); - + systick_disable(); + digitalWrite(VGA_R, 0); digitalWrite(VGA_G, 0); digitalWrite(VGA_B, 0); - digitalWrite(VGA_H,1); - digitalWrite(VGA_V,1); - - timer_set_prescaler(4,0); - timer_set_mode(4, 1, TIMER_OUTPUTCOMPARE); - timer_set_mode(4, 2, TIMER_OUTPUTCOMPARE); - timer_set_mode(4, 3, TIMER_OUTPUTCOMPARE); - timer_set_mode(4, 4, TIMER_OUTPUTCOMPARE); - timer_set_reload(4, 2287); - timer_set_compare_value(4,1,200); - timer_set_compare_value(4,2,300); - timer_set_compare_value(4,3,2170); // 2219 max... - timer_set_compare_value(4,4,1); - timer_attach_interrupt(4,1,isr_porch); - timer_attach_interrupt(4,2,isr_start); - timer_attach_interrupt(4,3,isr_stop); - timer_attach_interrupt(4,4,isr_update); - - timer_set_count(4,0); + digitalWrite(VGA_H, 1); + digitalWrite(VGA_V, 1); + + timer_pause(TIMER4); + timer_set_prescaler(TIMER4, 0); + timer_set_mode(TIMER4, 1, TIMER_OUTPUT_COMPARE); + timer_set_mode(TIMER4, 2, TIMER_OUTPUT_COMPARE); + timer_set_mode(TIMER4, 3, TIMER_OUTPUT_COMPARE); + timer_set_mode(TIMER4, 4, TIMER_OUTPUT_COMPARE); + timer_set_reload(TIMER4, 2287); + timer_set_compare(TIMER4, 1, 200); + timer_set_compare(TIMER4, 2, 250); + timer_set_compare(TIMER4, 3, 2170); // 2219 max... + timer_set_compare(TIMER4, 4, 1); + timer_attach_interrupt(TIMER4, 1, isr_porch); + timer_attach_interrupt(TIMER4, 2, isr_start); + timer_attach_interrupt(TIMER4, 3, isr_stop); + timer_attach_interrupt(TIMER4, 4, isr_update); + + timer_set_count(TIMER4, 0); + timer_resume(TIMER4); } -int toggle = 0; -uint16 x = 0; uint16 y = 0; uint16 val = 0; -uint8 v_active = 1; -GPIO_Port *portb = GPIOB_BASE; +bool v_active = true; +const uint16 x_max = 60; // empirically (and sloppily) determined void isr_porch(void) { VGA_H_HIGH; y++; - if(y>=523) { - y=1; - v_active = 1; + val = map(analogRead(ANALOG_PIN), 0, 4095, 0, x_max); + if (y >= 523) { + y = 1; + v_active = true; return; } - if(y>=492) { + if (y >= 492) { VGA_V_HIGH; return; } - if(y>=490) { + if (y >= 490) { VGA_V_LOW; return; } - if(y>=479) { // 479 - v_active = 0; + if (y >= 479) { + v_active = false; return; } } void isr_start(void) { - if(!v_active) { return; } - VGA_R_HIGH; - VGA_R_HIGH; - VGA_R_HIGH; - VGA_R_LOW; - //delayMicroseconds(2); - //gpio_write_bit(GPIOA_BASE, 8, 1); // VGA_G - for(x=0; x<(val>>6); x++) { - } - VGA_B_HIGH; - VGA_G_HIGH; - VGA_G_LOW; - VGA_B_LOW; - //VGA_R_HIGH; - //val = (val + analogRead(ANALOG_PIN))/2; - val = analogRead(ANALOG_PIN); - + if (!v_active) { + return; + } + VGA_COLOR(BORDER_COLOR); + for (int x = 0; x < val; x++) { + VGA_COLOR(COLOR_BLACK); + } + VGA_COLOR(COLOR_WHITE); + VGA_COLOR(COLOR_BLACK); } + void isr_stop(void) { - if(!v_active) { return; } - VGA_R_LOW; - VGA_G_LOW; - VGA_B_LOW; + if (!v_active) { + return; + } + VGA_COLOR(COLOR_BLACK); } + void isr_update(void) { VGA_H_LOW; } void loop() { - //val = analogRead(ANALOG_PIN); + toggleLED(); + delay(100); } +__attribute__((constructor)) void premain() { + init(); +} int main(void) { - init(); setup(); - while (1) { + while (true) { loop(); } return 0; diff --git a/libmaple/adc.c b/libmaple/adc.c index 3e6818c..73dce0a 100644 --- a/libmaple/adc.c +++ b/libmaple/adc.c @@ -23,85 +23,92 @@ *****************************************************************************/ /** - * @brief Analog to digital converter routines - */ - -#include "libmaple.h" -#include "rcc.h" -#include "adc.h" - -/* The ADC input clock is generated from PCLK2/APB2 divided by a prescaler - * and it must not exceed 14MHz. + * @file adc.c * - * ADC1 and ADC2 are clocked by APB2 - * - * 1) Power on by setting ADON in ADC_CR2 - * Conversion starts when ADON is set for a second time after some - * time t > t_stab. - * - * Up to 16 selected conversion must be selected in ADC_SQRx - * - * Single conversion mode: - * Set the ADON bit in the ADC_CR2 register - * Once the conversion is complete: - * Converted data is stored in ADC_DR - * EOC flag is set - * Interrupt is generated if EOCIE is set - * - * Calibration: - * Calibration is started by setting the CAL bit in the ADC_CR2 register. - * Once calibration is over, the CAL bit is reset by hardware and normal - * conversion can be performed. Calibrate at power-on. - * - * ALIGN in ADC_CR2 selects the alignment of data + * @brief Analog to digital converter routines * * IMPORTANT: maximum external impedance must be below 0.4kOhms for 1.5 * sample conversion time. * - * At 55.5 cycles/sample, the external input impedance < 50kOhms*/ - -void set_adc_smprx(adc_smp_rate smp_rate); + * At 55.5 cycles/sample, the external input impedance < 50kOhms. + * + * See STM32 manual RM0008 for how to calculate this. + */ -void adc_init(adc_smp_rate smp_rate) { - rcc_set_prescaler(RCC_PRESCALER_ADC, RCC_ADCPRE_PCLK_DIV_6); - rcc_clk_enable(RCC_ADC1); - rcc_reset_dev(RCC_ADC1); +#include "libmaple.h" +#include "rcc.h" +#include "adc.h" - ADC_CR1 = 0; - /* Software triggers conversions */ - ADC_CR2 = CR2_EXTSEL_SWSTART | CR2_EXTTRIG; - ADC_SQR1 = 0; +adc_dev adc1 = { + .regs = ADC1_BASE, + .clk_id = RCC_ADC1 +}; +const adc_dev *ADC1 = &adc1; - /* Set the sample conversion time. See note above for impedance - requirements. */ - adc_set_sample_rate(smp_rate); +adc_dev adc2 = { + .regs = ADC2_BASE, + .clk_id = RCC_ADC2 +}; +const adc_dev *ADC2 = &adc2; - /* Enable the ADC */ - CR2_ADON_BIT = 1; +#ifdef STM32_HIGH_DENSITY +adc_dev adc3 = { + .regs = ADC3_BASE, + .clk_id = RCC_ADC3 +}; +const adc_dev *ADC3 = &adc3; +#endif - /* Reset the calibration registers and then perform a reset */ - CR2_RSTCAL_BIT = 1; - while(CR2_RSTCAL_BIT) - ; - - CR2_CAL_BIT = 1; - while(CR2_CAL_BIT) - ; +/** + * @brief Initialize an ADC peripheral. + * + * Initializes the RCC clock line for the given peripheral, using ADC + * prescaler RCC_ADCPRE_PCLK_DIV_6. Resets ADC device registers. + * + * @param dev ADC peripheral to initialize + */ +void adc_init(const adc_dev *dev) { + rcc_set_prescaler(RCC_PRESCALER_ADC, RCC_ADCPRE_PCLK_DIV_6); + rcc_clk_enable(dev->clk_id); + rcc_reset_dev(dev->clk_id); } +/** + * @brief Set external event select for regular group + * @param dev ADC device + * @param event Event used to trigger the start of conversion. + * @see adc_extsel_event + */ +void adc_set_extsel(const adc_dev *dev, adc_extsel_event event) { + uint32 cr2 = dev->regs->CR2; + cr2 &= ~ADC_CR2_EXTSEL; + cr2 |= event; + dev->regs->CR2 = cr2; +} -void adc_disable(void) { - CR2_ADON_BIT = 0; +/** + * @brief Call a function on all ADC devices. + * @param fn Function to call on each ADC device. + */ +void adc_foreach(void (*fn)(const adc_dev*)) { + fn(ADC1); + fn(ADC2); +#ifdef STM32_HIGH_DENSITY + fn(ADC3); +#endif } -/* Turn the given sample rate into values for ADC_SMPRx. (Don't - * precompute in order to avoid wasting space). - * - * Don't call this during conversion! +/** + * @brief Turn the given sample rate into values for ADC_SMPRx. Don't + * call this during conversion. + * @param dev adc device + * @param smp_rate sample rate to set + * @see adc_smp_rate */ -void adc_set_sample_rate(adc_smp_rate smp_rate) { +void adc_set_sample_rate(const adc_dev *dev, adc_smp_rate smp_rate) { uint32 adc_smpr1_val = 0, adc_smpr2_val = 0; int i; + for (i = 0; i < 10; i++) { if (i < 8) { /* ADC_SMPR1 determines sample time for channels [10,17] */ @@ -110,6 +117,44 @@ void adc_set_sample_rate(adc_smp_rate smp_rate) { /* ADC_SMPR2 determines sample time for channels [0,9] */ adc_smpr2_val |= smp_rate << (i * 3); } - ADC_SMPR1 = adc_smpr1_val; - ADC_SMPR2 = adc_smpr2_val; + + dev->regs->SMPR1 = adc_smpr1_val; + dev->regs->SMPR2 = adc_smpr2_val; +} + +/** + * @brief Calibrate an ADC peripheral + * @param dev adc device + */ +void adc_calibrate(const adc_dev *dev) { + __io uint32 *rstcal_bit = bb_perip(&(dev->regs->CR2), 3); + __io uint32 *cal_bit = bb_perip(&(dev->regs->CR2), 2); + + *rstcal_bit = 1; + while (*rstcal_bit) + ; + + *cal_bit = 1; + while (*cal_bit) + ; +} + +/** + * @brief Perform a single synchronous software triggered conversion on a + * channel. + * @param dev ADC device to use for reading. + * @param channel channel to convert + * @return conversion result + */ +uint16 adc_read(const adc_dev *dev, uint8 channel) { + adc_reg_map *regs = dev->regs; + + adc_set_reg_seqlen(dev, 1); + + regs->SQR3 = channel; + regs->CR2 |= ADC_CR2_SWSTART; + while(!(regs->SR & ADC_SR_EOC)) + ; + + return (uint16)(regs->DR & ADC_DR_DATA); } diff --git a/libmaple/adc.h b/libmaple/adc.h index 976986f..c6a67a0 100644 --- a/libmaple/adc.h +++ b/libmaple/adc.h @@ -25,91 +25,335 @@ /** * @file adc.h * - * @brief Analog-to-Digital Conversion (ADC) routines. + * @brief Analog-to-Digital Conversion (ADC) header. */ #ifndef _ADC_H_ #define _ADC_H_ -#include "util.h" + +#include "libmaple.h" +#include "bitband.h" +#include "rcc.h" #ifdef __cplusplus extern "C"{ #endif -/* Notes: - * The maximum input impedance on each channel MUST be below .4kohms, - * or face the wrath of incorrect readings... - * This can be changed at the expense of sample time... see datasheet - * - * Need to up the sample time if otherwise... see datasheet */ - -/* TODO: We'll only use ADC1 for now. See page 41 of the manual for - ADC2 and ADC3's real addresses. */ -#define ADC1_BASE 0x40012400 -#define ADC2_BASE 0x40012400 -#define ADC3_BASE 0x40012400 - -#define ADC_SR *(volatile uint32*)(ADC1_BASE + 0) -#define ADC_CR1 *(volatile uint32*)(ADC1_BASE + 0x4) -#define ADC_CR2 *(volatile uint32*)(ADC1_BASE + 0x8) -#define ADC_SMPR1 *(volatile uint32*)(ADC1_BASE + 0xC) -#define ADC_SMPR2 *(volatile uint32*)(ADC1_BASE + 0x10) -#define ADC_SQR1 *(volatile uint32*)(ADC1_BASE + 0x2C) -#define ADC_SQR3 *(volatile uint32*)(ADC1_BASE + 0x34) -#define ADC_DR *(volatile uint32*)(ADC1_BASE + 0x4C) - -#define CR2_EXTSEL_SWSTART (0xE << 16) -#define CR2_RSTCAL (BIT(3)) -#define CR2_EXTTRIG (BIT(20)) - -/* Bit banded bits */ -#define CR2_ADON_BIT *(volatile uint32*)(BITBAND_PERI(ADC1_BASE+0x8, 0)) -#define CR2_CAL_BIT *(volatile uint32*)(BITBAND_PERI(ADC1_BASE+0x8, 2)) -#define CR2_RSTCAL_BIT *(volatile uint32*)(BITBAND_PERI(ADC1_BASE+0x8, 3)) -#define CR2_SWSTART_BIT *(volatile uint32*)(BITBAND_PERI(ADC1_BASE+0x8, 22)) -#define SR_EOC_BIT *(volatile uint32*)(BITBAND_PERI(ADC1_BASE+0, 1)) -/* (NR_ANALOG_PINS is board specific) */ +/** ADC register map type. */ +typedef struct adc_reg_map { + __io uint32 SR; ///< Status register + __io uint32 CR1; ///< Control register 1 + __io uint32 CR2; ///< Control register 2 + __io uint32 SMPR1; ///< Sample time register 1 + __io uint32 SMPR2; ///< Sample time register 2 + __io uint32 JOFR1; ///< Injected channel data offset register 1 + __io uint32 JOFR2; ///< Injected channel data offset register 2 + __io uint32 JOFR3; ///< Injected channel data offset register 3 + __io uint32 JOFR4; ///< Injected channel data offset register 4 + __io uint32 HTR; ///< Watchdog high threshold register + __io uint32 LTR; ///< Watchdog low threshold register + __io uint32 SQR1; ///< Regular sequence register 1 + __io uint32 SQR2; ///< Regular sequence register 2 + __io uint32 SQR3; ///< Regular sequence register 3 + __io uint32 JSQR; ///< Injected sequence register + __io uint32 JDR1; ///< Injected data register 1 + __io uint32 JDR2; ///< Injected data register 2 + __io uint32 JDR3; ///< Injected data register 3 + __io uint32 JDR4; ///< Injected data register 4 + __io uint32 DR; ///< Regular data register +} adc_reg_map; + +/** ADC device type. */ +typedef struct adc_dev { + adc_reg_map *regs; /**< Register map */ + rcc_clk_id clk_id; /**< RCC clock information */ +} adc_dev; + +/** ADC1 device. */ +extern const adc_dev *ADC1; +/** ADC2 device. */ +extern const adc_dev *ADC2; +#ifdef STM32_HIGH_DENSITY +/** ADC3 device. */ +extern const adc_dev *ADC3; +#endif + +/* + * Register map base pointers + */ + +/** ADC1 register map base pointer. */ +#define ADC1_BASE ((adc_reg_map*)0x40012400) +/** ADC2 register map base pointer. */ +#define ADC2_BASE ((adc_reg_map*)0x40012800) +/** ADC3 register map base pointer. */ +#define ADC3_BASE ((adc_reg_map*)0x40013C00) + +/* + * Register bit definitions + */ + +/* Status register */ + +#define ADC_SR_AWD_BIT 0 +#define ADC_SR_EOC_BIT 1 +#define ADC_SR_JEOC_BIT 2 +#define ADC_SR_JSTRT_BIT 3 +#define ADC_SR_STRT_BIT 4 + +#define ADC_SR_AWD BIT(ADC_SR_AWD_BIT) +#define ADC_SR_EOC BIT(ADC_SR_EOC_BIT) +#define ADC_SR_JEOC BIT(ADC_SR_JEOC_BIT) +#define ADC_SR_JSTRT BIT(ADC_SR_JSTRT_BIT) +#define ADC_SR_STRT BIT(ADC_SR_STRT_BIT) + +/* Control register 1 */ + +#define ADC_CR1_EOCIE_BIT 5 +#define ADC_CR1_AWDIE_BIT 6 +#define ADC_CR1_JEOCIE_BIT 7 +#define ADC_CR1_SCAN_BIT 8 +#define ADC_CR1_AWDSGL_BIT 9 +#define ADC_CR1_JAUTO_BIT 10 +#define ADC_CR1_DISCEN_BIT 11 +#define ADC_CR1_JDISCEN_BIT 12 +#define ADC_CR1_JAWDEN_BIT 22 +#define ADC_CR1_AWDEN_BIT 23 + +#define ADC_CR1_AWDCH (0x1F) +#define ADC_CR1_EOCIE BIT(ADC_CR1_EOCIE_BIT) +#define ADC_CR1_AWDIE BIT(ADC_CR1_AWDIE_BIT) +#define ADC_CR1_JEOCIE BIT(ADC_CR1_JEOCIE_BIT) +#define ADC_CR1_SCAN BIT(ADC_CR1_SCAN_BIT) +#define ADC_CR1_AWDSGL BIT(ADC_CR1_AWDSGL_BIT) +#define ADC_CR1_JAUTO BIT(ADC_CR1_JAUTO_BIT) +#define ADC_CR1_DISCEN BIT(ADC_CR1_DISCEN_BIT) +#define ADC_CR1_JDISCEN BIT(ADC_CR1_JDISCEN_BIT) +#define ADC_CR1_DISCNUM (0xE000) +#define ADC_CR1_JAWDEN BIT(ADC_CR1_JAWDEN_BIT) +#define ADC_CR1_AWDEN BIT(ADC_CR1_AWDEN_BIT) + +/* Control register 2 */ + +#define ADC_CR2_ADON_BIT 0 +#define ADC_CR2_CONT_BIT 1 +#define ADC_CR2_CAL_BIT 2 +#define ADC_CR2_RSTCAL_BIT 3 +#define ADC_CR2_DMA_BIT 8 +#define ADC_CR2_ALIGN_BIT 11 +#define ADC_CR2_JEXTTRIG_BIT 15 +#define ADC_CR2_EXTTRIG_BIT 20 +#define ADC_CR2_JSWSTART_BIT 21 +#define ADC_CR2_SWSTART_BIT 22 +#define ADC_CR2_TSEREFE_BIT 23 + +#define ADC_CR2_ADON BIT(ADC_CR2_ADON_BIT) +#define ADC_CR2_CONT BIT(ADC_CR2_CONT_BIT) +#define ADC_CR2_CAL BIT(ADC_CR2_CAL_BIT) +#define ADC_CR2_RSTCAL BIT(ADC_CR2_RSTCAL_BIT) +#define ADC_CR2_DMA BIT(ADC_CR2_DMA_BIT) +#define ADC_CR2_ALIGN BIT(ADC_CR2_ALIGN_BIT) +#define ADC_CR2_JEXTSEL (0x7000) +#define ADC_CR2_JEXTTRIG BIT(ADC_CR2_JEXTTRIG_BIT) +#define ADC_CR2_EXTSEL (0xE0000) +#define ADC_CR2_EXTTRIG BIT(ADC_CR2_EXTTRIG_BIT) +#define ADC_CR2_JSWSTART BIT(ADC_CR2_JSWSTART_BIT) +#define ADC_CR2_SWSTART BIT(ADC_CR2_SWSTART_BIT) +#define ADC_CR2_TSEREFE BIT(ADC_CR2_TSEREFE_BIT) + +/* Sample time register 1 */ + +#define ADC_SMPR1_SMP17 (0x7 << 21) +#define ADC_SMPR1_SMP16 (0x7 << 18) +#define ADC_SMPR1_SMP15 (0x7 << 15) +#define ADC_SMPR1_SMP14 (0x7 << 12) +#define ADC_SMPR1_SMP13 (0x7 << 9) +#define ADC_SMPR1_SMP12 (0x7 << 6) +#define ADC_SMPR1_SMP11 (0x7 << 3) +#define ADC_SMPR1_SMP10 0x7 + +/* Sample time register 2 */ + +#define ADC_SMPR2_SMP9 (0x7 << 27) +#define ADC_SMPR2_SMP8 (0x7 << 24) +#define ADC_SMPR2_SMP7 (0x7 << 21) +#define ADC_SMPR2_SMP6 (0x7 << 18) +#define ADC_SMPR2_SMP5 (0x7 << 15) +#define ADC_SMPR2_SMP4 (0x7 << 12) +#define ADC_SMPR2_SMP3 (0x7 << 9) +#define ADC_SMPR2_SMP2 (0x7 << 6) +#define ADC_SMPR2_SMP1 (0x7 << 3) +#define ADC_SMPR2_SMP0 0x7 + +/* Injected channel data offset register */ + +#define ADC_JOFR_JOFFSET 0x3FF + +/* Watchdog high threshold register */ + +#define ADC_HTR_HT 0x3FF + +/* Watchdog low threshold register */ + +#define ADC_LTR_LT 0x3FF + +/* Regular sequence register 1 */ + +#define ADC_SQR1_L (0x1F << 20) +#define ADC_SQR1_SQ16 (0x1F << 15) +#define ADC_SQR1_SQ15 (0x1F << 10) +#define ADC_SQR1_SQ14 (0x1F << 5) +#define ADC_SQR1_SQ13 0x1F + +/* Regular sequence register 2 */ + +#define ADC_SQR2_SQ12 (0x1F << 25) +#define ADC_SQR2_SQ11 (0x1F << 20) +#define ADC_SQR2_SQ10 (0x1F << 16) +#define ADC_SQR2_SQ9 (0x1F << 10) +#define ADC_SQR2_SQ8 (0x1F << 5) +#define ADC_SQR2_SQ7 0x1F + +/* Regular sequence register 3 */ + +#define ADC_SQR3_SQ6 (0x1F << 25) +#define ADC_SQR3_SQ5 (0x1F << 20) +#define ADC_SQR3_SQ4 (0x1F << 16) +#define ADC_SQR3_SQ3 (0x1F << 10) +#define ADC_SQR3_SQ2 (0x1F << 5) +#define ADC_SQR3_SQ1 0x1F + +/* Injected sequence register */ + +#define ADC_JSQR_JL (0x3 << 20) +#define ADC_JSQR_JL_1CONV (0x0 << 20) +#define ADC_JSQR_JL_2CONV (0x1 << 20) +#define ADC_JSQR_JL_3CONV (0x2 << 20) +#define ADC_JSQR_JL_4CONV (0x3 << 20) +#define ADC_JSQR_JSQ4 (0x1F << 15) +#define ADC_JSQR_JSQ3 (0x1F << 10) +#define ADC_JSQR_JSQ2 (0x1F << 5) +#define ADC_JSQR_JSQ1 0x1F + +/* Injected data registers */ + +#define ADC_JDR_JDATA 0xFFFF + +/* Regular data register */ + +#define ADC_DR_ADC2DATA (0xFFFF << 16) +#define ADC_DR_DATA 0xFFFF + +void adc_init(const adc_dev *dev); + +/** + * @brief External event selector for regular group conversion. + * @see adc_set_extsel + */ +typedef enum adc_extsel_event { + ADC_ADC12_TIM1_CC1 = (0 << 17), /**< ADC1 and ADC2: Timer 1 CC1 event */ + ADC_ADC12_TIM1_CC2 = (1 << 17), /**< ADC1 and ADC2: Timer 1 CC2 event */ + ADC_ADC12_TIM1_CC3 = (2 << 17), /**< ADC1 and ADC2: Timer 1 CC3 event */ + ADC_ADC12_TIM2_CC2 = (3 << 17), /**< ADC1 and ADC2: Timer 2 CC2 event */ + ADC_ADC12_TIM3_TRGO = (4 << 17), /**< ADC1 and ADC2: Timer 3 TRGO event */ + ADC_ADC12_TIM4_CC4 = (5 << 17), /**< ADC1 and ADC2: Timer 4 CC4 event */ + ADC_ADC12_EXTI11 = (6 << 17), /**< ADC1 and ADC2: EXTI11 event */ +#ifdef STM32_HIGH_DENSITY + ADC_ADC12_TIM8_TRGO = (6 << 17), /**< ADC1 and ADC2: Timer 8 TRGO + event (high density only) */ +#endif + ADC_ADC12_SWSTART = (7 << 17), /**< ADC1 and ADC2: Software start */ +#ifdef STM32_HIGH_DENSITY + ADC_ADC3_TIM3_CC1 = (0 << 17), /**< ADC3: Timer 3 CC1 event + (high density only) */ + ADC_ADC3_TIM2_CC3 = (1 << 17), /**< ADC3: Timer 2 CC3 event + (high density only) */ + ADC_ADC3_TIM1_CC3 = (2 << 17), /**< ADC3: Timer 1 CC3 event + (high density only) */ + ADC_ADC3_TIM8_CC1 = (3 << 17), /**< ADC3: Timer 8 CC1 event + (high density only) */ + ADC_ADC3_TIM8_TRGO = (4 << 17), /**< ADC3: Timer 8 TRGO event + (high density only) */ + ADC_ADC3_TIM5_CC1 = (5 << 17), /**< ADC3: Timer 5 CC1 event + (high density only) */ + ADC_ADC3_TIM5_CC3 = (6 << 17), /**< ADC3: Timer 5 CC3 event + (high density only) */ + ADC_ADC3_SWSTART = (7 << 17), /**< ADC3: Software start (high + density only) */ +#endif + ADC_SWSTART = (7 << 17) /**< ADC1, ADC2, ADC3: Software start */ +} adc_extsel_event; + +void adc_set_extsel(const adc_dev *dev, adc_extsel_event event); +void adc_foreach(void (*fn)(const adc_dev*)); /** ADC per-sample conversion times, in ADC clock cycles */ typedef enum { - ADC_SMPR_1_5, - ADC_SMPR_7_5, - ADC_SMPR_13_5, - ADC_SMPR_28_5, - ADC_SMPR_41_5, - ADC_SMPR_55_5, - ADC_SMPR_71_5, - ADC_SMPR_239_5 + ADC_SMPR_1_5, /**< 1.5 ADC cycles */ + ADC_SMPR_7_5, /**< 7.5 ADC cycles */ + ADC_SMPR_13_5, /**< 13.5 ADC cycles */ + ADC_SMPR_28_5, /**< 28.5 ADC cycles */ + ADC_SMPR_41_5, /**< 41.5 ADC cycles */ + ADC_SMPR_55_5, /**< 55.5 ADC cycles */ + ADC_SMPR_71_5, /**< 71.5 ADC cycles */ + ADC_SMPR_239_5 /**< 239.5 ADC cycles */ } adc_smp_rate; -/** Initialize ADC1 to do one-shot conversions at the given sample - rate. */ -void adc_init(adc_smp_rate smp_rate); +void adc_set_sample_rate(const adc_dev *dev, adc_smp_rate smp_rate); +void adc_calibrate(const adc_dev *dev); +uint16 adc_read(const adc_dev *dev, uint8 channel); -void adc_set_sample_rate(adc_smp_rate smp_rate); - -void adc_disable(void); +/** + * @brief Set the regular channel sequence length. + * + * Defines the total number of conversions in the regular channel + * conversion sequence. + * + * @param dev ADC device. + * @param length Regular channel sequence length, from 1 to 16. + */ +static inline void adc_set_reg_seqlen(const adc_dev *dev, uint8 length) { + uint32 tmp = dev->regs->SQR1; + tmp &= ~ADC_SQR1_L; + tmp |= (length - 1) << 20; + dev->regs->SQR1 = tmp; +} /** - * Perform a single conversion on ADC[0-15]. - * PRECONDITIONS: - * adc initialized */ -static inline int adc_read(int channel) { - /* Set channel */ - ADC_SQR3 = channel; + * @brief Set external trigger conversion mode event for regular channels + * @param dev ADC device + * @param enable If 1, conversion on external events is enabled; if 0, + * disabled. + */ +static inline void adc_set_exttrig(const adc_dev *dev, uint8 enable) { + *bb_perip(&dev->regs->CR2, ADC_CR2_EXTTRIG_BIT) = !!enable; +} - /* Start the conversion */ - CR2_SWSTART_BIT = 1; +/** + * @brief Enable an adc peripheral + * @param dev ADC device to enable + */ +static inline void adc_enable(const adc_dev *dev) { + *bb_perip(&dev->regs->CR2, ADC_CR2_ADON_BIT) = 1; +} - /* Wait for it to finish */ - while(SR_EOC_BIT == 0) - ; +/** + * @brief Disable an ADC peripheral + * @param dev ADC device to disable + */ +static inline void adc_disable(const adc_dev *dev) { + *bb_perip(&dev->regs->CR2, ADC_CR2_ADON_BIT) = 0; +} - return ADC_DR; +/** + * @brief Disable all ADC peripherals. + */ +static inline void adc_disable_all(void) { + adc_foreach(adc_disable); } #ifdef __cplusplus } // extern "C" #endif -#endif +#endif diff --git a/libmaple/bitband.h b/libmaple/bitband.h new file mode 100644 index 0000000..870abe9 --- /dev/null +++ b/libmaple/bitband.h @@ -0,0 +1,110 @@ +/****************************************************************************** + * The MIT License + * + * Copyright (c) 2011 LeafLabs, LLC. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + *****************************************************************************/ + +/** + * @file bitband.h + * + * @brief Bit-banding utility functions + */ + +#ifndef _BITBAND_H_ +#define _BITBAND_H_ + +#define BB_SRAM_REF 0x20000000 +#define BB_SRAM_BASE 0x22000000 +#define BB_PERI_REF 0x40000000 +#define BB_PERI_BASE 0x42000000 + +static inline __io uint32* __bb_addr(__io void*, uint32, uint32, uint32); + +/** + * @brief Obtain a pointer to the bit-band address corresponding to a + * bit in a volatile SRAM address. + * @param address Address in the bit-banded SRAM region + * @param bit Bit in address to bit-band + */ +static inline __io uint32* bb_sramp(__io void *address, uint32 bit) { + return __bb_addr(address, bit, BB_SRAM_BASE, BB_SRAM_REF); +} + +/** + * @brief Get a bit from an address in the SRAM bit-band region. + * @param address Address in the SRAM bit-band region to read from + * @param bit Bit in address to read + * @return bit's value in address. + */ +static inline uint8 bb_sram_get_bit(__io void *address, uint32 bit) { + return *bb_sramp(address, bit); +} + +/** + * @brief Set a bit in an address in the SRAM bit-band region. + * @param address Address in the SRAM bit-band region to write to + * @param bit Bit in address to write to + * @param val Value to write for bit, either 0 or 1. + */ +static inline void bb_sram_set_bit(__io void *address, uint32 bit, uint8 val) { + *bb_sramp(address, bit) = val; +} + +/** + * @brief Obtain a pointer to the bit-band address corresponding to a + * bit in a peripheral address. + * @param address Address in the bit-banded peripheral region + * @param bit Bit in address to bit-band + */ +static inline __io uint32* bb_perip(__io void *address, uint32 bit) { + return __bb_addr(address, bit, BB_PERI_BASE, BB_PERI_REF); +} + +/** + * @brief Get a bit from an address in the peripheral bit-band region. + * @param address Address in the peripheral bit-band region to read from + * @param bit Bit in address to read + * @return bit's value in address. + */ +static inline uint8 bb_peri_get_bit(__io void *address, uint32 bit) { + return *bb_perip(address, bit); +} + +/** + * @brief Set a bit in an address in the peripheral bit-band region. + * @param address Address in the peripheral bit-band region to write to + * @param bit Bit in address to write to + * @param val Value to write for bit, either 0 or 1. + */ +static inline void bb_peri_set_bit(__io void *address, uint32 bit, uint8 val) { + *bb_perip(address, bit) = val; +} + +static inline __io uint32* __bb_addr(__io void *address, + uint32 bit, + uint32 bb_base, + uint32 bb_ref) { + return (__io uint32*)(bb_base + ((uint32)address - bb_ref) * 32 + bit * 4); +} + +#endif /* _BITBAND_H_ */ diff --git a/libmaple/bkp.c b/libmaple/bkp.c index e89abd0..aaccb1f 100644 --- a/libmaple/bkp.c +++ b/libmaple/bkp.c @@ -24,63 +24,101 @@ * SOFTWARE. *****************************************************************************/ -#include "libmaple.h" #include "bkp.h" #include "pwr.h" #include "rcc.h" -#include "util.h" +#include "bitband.h" -/* Data register memory layout is not contiguous. It's split up from - 1--NR_LOW_DRS, beginning at BKP_LOW_OFFSET, through - (NR_LOW_DRS+1)--NR_DRS, beginning at BKP_HIGH_OFFSET. */ -#define NR_LOW_DRS 10 -#define BKP_LOW_OFFSET 0x4 /* start offset for data registers 1--10 */ -#define BKP_HIGH_OFFSET 0x40 /* start offset for data registers 11--42 */ +static inline __io uint32* data_register(uint8 reg); -inline volatile uint16* reg_addr(uint8 reg) { - if (1 <= reg) { - if (reg <= NR_LOW_DRS) { - return (volatile uint16*)(BKP_BASE + BKP_LOW_OFFSET + - (reg - 1) * 4); - } else if (reg <= NR_BKP_REGS) { - return (volatile uint16*)(BKP_BASE + BKP_HIGH_OFFSET + - (reg - NR_LOW_DRS - 1) * 4); - } - } - return 0; -} +bkp_dev bkp = { + .regs = BKP_BASE, +}; -void bkp_init(void) { - /* Set PWREN (28) and BKPEN (27) bits */ - __set_bits(RCC_APB1ENR, BIT(28) | BIT(27)); -} +const bkp_dev *BKP = &bkp; -void bkp_disable(void) { - __clear_bits(RCC_APB1ENR, BIT(28) | BIT(27)); +/** + * @brief Initialize backup interface. + * + * Enables the power and backup interface clocks, and resets the + * backup device. + */ +void bkp_init(void) { + /* Don't call pwr_init(), or you'll reset the device. We just + * need the clock. */ + rcc_clk_enable(RCC_PWR); + rcc_clk_enable(RCC_BKP); + rcc_reset_dev(RCC_BKP); } +/** + * Enable write access to the backup registers. Backup interface must + * be initialized for subsequent register writes to work. + * @see bkp_init() + */ void bkp_enable_writes(void) { - /* Set the DBP bit in PWR_CR */ - __write(BITBAND_PERI(PWR_CR, PWR_CR_DBP), 1); + *bb_perip(&PWR_BASE->CR, PWR_CR_DBP) = 1; } +/** + * Disable write access to the backup registers. + */ void bkp_disable_writes(void) { - __write(BITBAND_PERI(PWR_CR, PWR_CR_DBP), 0); + *bb_perip(&PWR_BASE->CR, PWR_CR_DBP) = 0; } +/** + * Read a value from given backup data register. + * @param reg Data register to read, from 1 to BKP_NR_DATA_REGS (10 on + * medium-density devices, 42 on high-density devices). + */ uint16 bkp_read(uint8 reg) { - volatile uint16* addr = reg_addr(reg); - if (addr != 0) { - return *addr; + __io uint32* dr = data_register(reg); + if (!dr) { + ASSERT(0); /* nonexistent register */ + return 0; } - ASSERT(0); /* nonexistent register */ - return 0; + return (uint16)*dr; } +/** + * @brief Write a value to given data register. + * + * Write access to backup registers must be enabled. + * + * @param reg Data register to write, from 1 to BKP_NR_DATA_REGS (10 + * on medium-density devices, 42 on high-density devices). + * @param val Value to write into the register. + * @see bkp_enable_writes() + */ void bkp_write(uint8 reg, uint16 val) { - volatile uint16* addr = reg_addr(reg); - if (addr != 0) { - *addr = val; + __io uint32* dr = data_register(reg); + if (!dr) { + ASSERT(0); /* nonexistent register */ + return; + } + *dr = (uint32)val; +} + +/* + * Data register memory layout is not contiguous. It's split up from + * 1--NR_LOW_DRS, beginning at BKP_BASE->DR1, through to + * (NR_LOW_DRS+1)--BKP_NR_DATA_REGS, beginning at BKP_BASE->DR11. + */ +#define NR_LOW_DRS 10 + +static inline __io uint32* data_register(uint8 reg) { + if (reg < 1 || reg > BKP_NR_DATA_REGS) { + return 0; + } + +#if BKP_NR_DATA_REGS == NR_LOW_DRS + return (uint32*)BKP_BASE + reg; +#else + if (reg <= NR_LOW_DRS) { + return (uint32*)BKP_BASE + reg; + } else { + return (uint32*)&(BKP_BASE->DR11) + (reg - NR_LOW_DRS - 1); } - ASSERT(0); /* nonexistent register */ +#endif } diff --git a/libmaple/bkp.h b/libmaple/bkp.h index 9ad4c41..97edd2a 100644 --- a/libmaple/bkp.h +++ b/libmaple/bkp.h @@ -32,51 +32,87 @@ #ifndef _BKP_H_ #define _BKP_H_ +#include "libmaple.h" + #ifdef __cplusplus extern "C" { #endif -#define BKP_BASE 0x40006C00 -#define BKP_RTCCR (BKP_BASE + 0x2C) -#define BKP_CR (BKP_BASE + 0x30) -#define BKP_CSR (BKP_BASE + 0x34) +#if defined(STM32_MEDIUM_DENSITY) +#define BKP_NR_DATA_REGS 10 +#elif defined(STM32_HIGH_DENSITY) +#define BKP_NR_DATA_REGS 42 +#endif -/** - * Initialize backup interface. This function enables the power and - * backup interface clocks. It does not enable write access to the - * backup registers. - */ -void bkp_init(void); +/** Backup peripheral register map type. */ +typedef struct bkp_reg_map { + const uint32 RESERVED1; ///< Reserved + __io uint32 DR1; ///< Data register 1 + __io uint32 DR2; ///< Data register 2 + __io uint32 DR3; ///< Data register 3 + __io uint32 DR4; ///< Data register 4 + __io uint32 DR5; ///< Data register 5 + __io uint32 DR6; ///< Data register 6 + __io uint32 DR7; ///< Data register 7 + __io uint32 DR8; ///< Data register 8 + __io uint32 DR9; ///< Data register 9 + __io uint32 DR10; ///< Data register 10 + __io uint32 RTCCR; ///< RTC control register + __io uint32 CR; ///< Control register + __io uint32 CSR; ///< Control and status register +#ifdef STM32_HIGH_DENSITY + const uint32 RESERVED2; ///< Reserved + const uint32 RESERVED3; ///< Reserved + __io uint32 DR11; ///< Data register 11 + __io uint32 DR12; ///< Data register 12 + __io uint32 DR13; ///< Data register 13 + __io uint32 DR14; ///< Data register 14 + __io uint32 DR15; ///< Data register 15 + __io uint32 DR16; ///< Data register 16 + __io uint32 DR17; ///< Data register 17 + __io uint32 DR18; ///< Data register 18 + __io uint32 DR19; ///< Data register 19 + __io uint32 DR20; ///< Data register 20 + __io uint32 DR21; ///< Data register 21 + __io uint32 DR22; ///< Data register 22 + __io uint32 DR23; ///< Data register 23 + __io uint32 DR24; ///< Data register 24 + __io uint32 DR25; ///< Data register 25 + __io uint32 DR26; ///< Data register 26 + __io uint32 DR27; ///< Data register 27 + __io uint32 DR28; ///< Data register 28 + __io uint32 DR29; ///< Data register 29 + __io uint32 DR30; ///< Data register 30 + __io uint32 DR31; ///< Data register 31 + __io uint32 DR32; ///< Data register 32 + __io uint32 DR33; ///< Data register 33 + __io uint32 DR34; ///< Data register 34 + __io uint32 DR35; ///< Data register 35 + __io uint32 DR36; ///< Data register 36 + __io uint32 DR37; ///< Data register 37 + __io uint32 DR38; ///< Data register 38 + __io uint32 DR39; ///< Data register 39 + __io uint32 DR40; ///< Data register 40 + __io uint32 DR41; ///< Data register 41 + __io uint32 DR42; ///< Data register 42 +#endif +} bkp_reg_map; -/** Disable power and backup interface clocks. */ -void bkp_disable(void); +/** Backup peripheral register map base pointer. */ +#define BKP_BASE ((bkp_reg_map*)0x40006C00) -/** - * Enable write access to the backup registers. Backup interface must - * be initialized for subsequent register writes to work. - * @see bkp_init() - */ -void bkp_enable_writes(void); +/** Backup peripheral device type. */ +typedef struct bkp_dev { + bkp_reg_map *regs; /**< Register map */ +} bkp_dev; -/** - * Disable write access to the backup registers. Does not disable - * backup interface clocks. - */ -void bkp_disable_writes(void); +/** Backup device. */ +extern const bkp_dev *BKP; -/** - * Read a value from given backup data register. - * @param reg Data register to read, from 1 to NR_BKP_REGS (10 on Maple). - */ +void bkp_init(void); +void bkp_enable_writes(void); +void bkp_disable_writes(void); uint16 bkp_read(uint8 reg); - -/** - * Write a value to given data register. Backup interface must have - * been previously initialized, and write access to backup registers - * must be enabled. - * @param reg Data register to write, from 1 to NR_BKP_REGS (10 on Maple). - * @param val Value to write into the register. - */ void bkp_write(uint8 reg, uint16 val); #ifdef __cplusplus diff --git a/libmaple/dac.c b/libmaple/dac.c index 63a96ac..1726d19 100644 --- a/libmaple/dac.c +++ b/libmaple/dac.c @@ -23,43 +23,96 @@ *****************************************************************************/ #include "libmaple.h" -#include "rcc.h" #include "gpio.h" #include "dac.h" +#ifdef STM32_HIGH_DENSITY + /** * @brief DAC peripheral routines. */ -/* This numbering follows the registers (1-indexed) */ -#define DAC_CH1 1 -#define DAC_CH2 2 - -DAC_Map *dac = (DAC_Map*)(DAC_BASE); +dac_dev dac = { + .regs = DAC_BASE, +}; +const dac_dev *DAC = &dac; -/* Sets up the DAC peripheral */ -void dac_init(void) { +/** + * @brief Initialize the digital to analog converter + * @param dev DAC device + * @param flags Flags: + * DAC_CH1: Enable channel 1 + * DAC_CH2: Enable channel 2 + * @sideeffect May set PA4 or PA5 to INPUT_ANALOG + */ +void dac_init(const dac_dev *dev, uint32 flags) { /* First turn on the clock */ rcc_clk_enable(RCC_DAC); + rcc_reset_dev(RCC_DAC); - /* Then setup ANALOG mode on PA4 and PA5 */ - gpio_set_mode(GPIOA_BASE, 4, CNF_INPUT_ANALOG); - gpio_set_mode(GPIOA_BASE, 5, CNF_INPUT_ANALOG); + if (flags & DAC_CH1) { + dac_enable_channel(dev, 1); + } - /* Then do register stuff. Default does no triggering, and - * buffered output, so all good. */ - dac->CR = DAC_CR_EN1 | DAC_CR_EN2; + if (flags & DAC_CH2) { + dac_enable_channel(dev, 2); + } } -void dac_write(uint8 chan, uint16 val) { - switch(chan) { - case DAC_CH1: - dac->DHR12R1 = 0x0FFF & val; +/** + * @brief Write a 12-bit value to the DAC to output + * @param dev DAC device + * @param channel channel to select (1 or 2) + * @param val value to write + */ +void dac_write_channel(const dac_dev *dev, uint8 channel, uint16 val) { + switch(channel) { + case 1: + dev->regs->DHR12R1 = DAC_DHR12R1_DACC1DHR & val; break; - case DAC_CH2: - dac->DHR12R2 = 0x0FFF & val; + case 2: + dev->regs->DHR12R2 = DAC_DHR12R2_DACC2DHR & val; break; - default: - ASSERT(0); // can't happen } } + +/** + * @brief Enable a DAC channel + * @param dev DAC device + * @param channel channel to enable, either 1 or 2 + * @sideeffect May change pin mode of PA4 or PA5 + */ +void dac_enable_channel(const dac_dev *dev, uint8 channel) { + /* + * Setup ANALOG mode on PA4 and PA5. This mapping is consistent across + * all STM32 chips with a DAC. See RM0008 12.2. + */ + switch (channel) { + case 1: + gpio_set_mode(GPIOA, 4, GPIO_INPUT_ANALOG); + dev->regs->CR |= DAC_CR_EN1; + break; + case 2: + gpio_set_mode(GPIOA, 5, GPIO_INPUT_ANALOG); + dev->regs->CR |= DAC_CR_EN2; + break; + } +} + +/** + * @brief Disable a DAC channel + * @param dev DAC device + * @param channel channel to disable, either 1 or 2 + */ +void dac_disable_channel(const dac_dev *dev, uint8 channel) { + switch (channel) { + case 1: + dev->regs->CR &= ~DAC_CR_EN1; + break; + case 2: + dev->regs->CR &= ~DAC_CR_EN2; + break; + } +} + +#endif /* STM32_HIGH_DENSITY */ diff --git a/libmaple/dac.h b/libmaple/dac.h index 340a49a..cf4fe85 100644 --- a/libmaple/dac.h +++ b/libmaple/dac.h @@ -22,92 +22,146 @@ * THE SOFTWARE. *****************************************************************************/ -/* - * See ../notes/dac.txt for more info - */ - /** * @file dac.h + * @brief Digital to analog converter header file */ +/* See notes/dac.txt for more info */ + #ifndef _DAC_H_ #define _DAC_H_ +#include "rcc.h" + #ifdef __cplusplus extern "C"{ #endif -#define DAC_BASE 0x40007400 - -typedef struct { - volatile uint32 CR; - volatile uint32 SWTRIGR; - volatile uint32 DHR12R1; - volatile uint32 DHR12L1; - volatile uint32 DHR8R1; - volatile uint32 DHR12R2; - volatile uint32 DHR12L2; - volatile uint32 DHR8R2; - volatile uint32 DHR12RD; - volatile uint32 DHR12LD; - volatile uint32 DHR8RD; - volatile uint32 DOR1; - volatile uint32 DOR2; -} DAC_Map; - -/* There's only one DAC, so expose it. */ -extern DAC_Map *dac; - -// And here are the register bit ranges -#define DAC_CR_EN1 BIT(0) -#define DAC_CR_BOFF1 BIT(1) -#define DAC_CR_TEN1 BIT(2) -#define DAC_CR_TSEL1 (BIT(3) | BIT(4) | BIT(5)) -#define DAC_CR_WAVE1 (BIT(6) | BIT(7)) -#define DAC_CR_MAMP1 (BIT(8) | BIT(9) | BIT(10) | BIT(11)) -#define DAC_CR_DMAEN1 BIT(12) -#define DAC_CR_EN2 BIT(16) -#define DAC_CR_BOFF2 BIT(17) -#define DAC_CR_TEN2 BIT(18) -#define DAC_CR_TSEL2 (BIT(19) | BIT(20) | BIT(21)) -#define DAC_CR_WAVE2 (BIT(22) | BIT(23)) -#define DAC_CR_MAMP2 (BIT(24) | BIT(25) | BIT(26) | BIT(27)) -#define DAC_CR_DMAEN2 BIT(28) - -#define DAC_SWTRIGR_SWTRIG1 BIT(0) -#define DAC_SWTRIGR_SWTRIG2 BIT(1) +/* + * Register maps + */ + +/** DAC register map. */ +typedef struct dac_reg_map { + __io uint32 CR; /**< Control register */ + __io uint32 SWTRIGR; /**< Software trigger register */ + __io uint32 DHR12R1; /**< Channel 1 12-bit right-aligned data + holding register */ + __io uint32 DHR12L1; /**< Channel 1 12-bit left-aligned data + holding register */ + __io uint32 DHR8R1; /**< Channel 1 8-bit left-aligned data + holding register */ + __io uint32 DHR12R2; /**< Channel 2 12-bit right-aligned data + holding register */ + __io uint32 DHR12L2; /**< Channel 2 12-bit left-aligned data + holding register */ + __io uint32 DHR8R2; /**< Channel 2 8-bit left-aligned data + holding register */ + __io uint32 DHR12RD; /**< Dual DAC 12-bit right-aligned data + holding register */ + __io uint32 DHR12LD; /**< Dual DAC 12-bit left-aligned data + holding register */ + __io uint32 DHR8RD; /**< Dual DAC 8-bit left-aligned data holding + register */ + __io uint32 DOR1; /**< Channel 1 data output register */ + __io uint32 DOR2; /**< Channel 2 data output register */ +} dac_reg_map; + +/** DAC register map base address */ +#define DAC_BASE ((dac_reg_map*)0x40007400) + +/* + * Devices + */ + +/** DAC device type. */ +typedef struct dac_dev { + dac_reg_map *regs; /**< Register map */ +} dac_dev; + +/** DAC device. */ +extern const dac_dev *DAC; + +/* + * Register bit definitions + */ +/* Control register */ +/* Channel 1 control */ +#define DAC_CR_EN1 BIT(0) /* Enable */ +#define DAC_CR_BOFF1 BIT(1) /* Output buffer disable */ +#define DAC_CR_TEN1 BIT(2) /* Trigger enable */ +#define DAC_CR_TSEL1 (0x7 << 3) /* Trigger selection */ +#define DAC_CR_WAVE1 (0x3 << 6) /* Noise/triangle wave enable */ +#define DAC_CR_MAMP1 (0xF << 8) /* Mask/amplitude selector */ +#define DAC_CR_DMAEN1 BIT(12) /* DMA enable */ +/* Channel 2 control */ +#define DAC_CR_EN2 BIT(16) /* Enable */ +#define DAC_CR_BOFF2 BIT(17) /* Output buffer disable */ +#define DAC_CR_TEN2 BIT(18) /* Trigger enable */ +#define DAC_CR_TSEL2 (0x7 << 19) /* Trigger selection */ +#define DAC_CR_WAVE2 (0x3 << 22) /* Noise/triangle wave generation*/ +#define DAC_CR_MAMP2 (0xF << 24) /* Mask/amplitude selector */ +#define DAC_CR_DMAEN2 BIT(28) /* DMA enable */ + +/* Software trigger register */ +#define DAC_SWTRIGR_SWTRIG1 BIT(0) /* Channel 1 software trigger */ +#define DAC_SWTRIGR_SWTRIG2 BIT(1) /* Channel 2 software trigger */ + +/* Channel 1 12-bit right-aligned data holding register */ #define DAC_DHR12R1_DACC1DHR 0x00000FFF +/* Channel 1 12-bit left-aligned data holding register */ #define DAC_DHR12L1_DACC1DHR 0x0000FFF0 +/* Channel 1 8-bit left-aligned data holding register */ #define DAC_DHR8R1_DACC1DHR 0x000000FF +/* Channel 2 12-bit right-aligned data holding register */ #define DAC_DHR12R2_DACC2DHR 0x00000FFF +/* Channel 2 12-bit left-aligned data holding register */ #define DAC_DHR12L2_DACC2DHR 0x0000FFF0 +/* Channel 2 8-bit left-aligned data holding register */ #define DAC_DHR8R2_DACC2DHR 0x000000FF +/* Dual DAC 12-bit right-aligned data holding register */ #define DAC_DHR12RD_DACC1DHR 0x00000FFF #define DAC_DHR12RD_DACC2DHR 0x0FFF0000 +/* Dual DAC 12-bit left-aligned data holding register */ #define DAC_DHR12LD_DACC1DHR 0x0000FFF0 #define DAC_DHR12LD_DACC2DHR 0xFFF00000 +/* Dual DAC 8-bit left-aligned data holding register */ #define DAC_DHR8RD_DACC1DHR 0x000000FF #define DAC_DHR8RD_DACC2DHR 0x0000FF00 -#define DAC_DOR1 0x00000FFF +/* Channel 1 data output register */ +#define DAC_DOR1_DACC1DOR 0x00000FFF -#define DAC_DOR2 0x00000FFF +/* Channel 1 data output register */ +#define DAC_DOR2_DACC2DOR 0x00000FFF -void dac_init(void); -void dac_write(uint8 chan, uint16 val); +/* + * Convenience functions + */ + +/* We take the dev argument in these convenience functions for + * future-proofing */ + +#define DAC_CH1 0x1 +#define DAC_CH2 0x2 +void dac_init(const dac_dev *dev, uint32 flags); + +void dac_write_channel(const dac_dev *dev, uint8 channel, uint16 val); +void dac_enable_channel(const dac_dev *dev, uint8 channel); +void dac_disable_channel(const dac_dev *dev, uint8 channel); #ifdef __cplusplus } // extern "C" #endif - #endif diff --git a/libmaple/delay.h b/libmaple/delay.h new file mode 100644 index 0000000..5372ac1 --- /dev/null +++ b/libmaple/delay.h @@ -0,0 +1,24 @@ +/** + * @brief Delay implementation + */ + +#ifndef _DELAY_H_ +#define _DELAY_H_ + +static inline void delay_us(uint32 us) { + /* TODO this makes unwarranted assumptions about the RCC + * config; add a hook so users can make their own decisions. */ + /* So (2^32)/12 micros max, or less than 6 minutes */ + us *= 12; + + /* fudge for function call overhead */ + us--; + asm volatile(" mov r0, %[us] \n\t" + "1: subs r0, #1 \n\t" + " bhi 1b \n\t" + : + : [us] "r" (us) + : "r0"); +} +#endif + diff --git a/libmaple/dma.c b/libmaple/dma.c index 15c96e1..04cdcea 100644 --- a/libmaple/dma.c +++ b/libmaple/dma.c @@ -23,126 +23,353 @@ *****************************************************************************/ /** - * @file dma.c - * - * @brief Direct Memory Access peripheral support + * @file dma.c + * @author Marti Bolivar <mbolivar@leaflabs.com>; + * Original implementation by Michael Hope + * @brief Direct Memory Access peripheral support */ -#include "libmaple.h" #include "dma.h" -#include "rcc.h" -#include "nvic.h" - -#define DMA_EN BIT(0) - -typedef struct dma_regs { - uint32 CCR; - uint32 CNDTR; - uint32 CPAR; - uint32 CMAR; -} dma_regs; - -typedef struct DMAChannel { - void (*handler)(void); - uint32 irq_line; -} DMAChannel; - -volatile static DMAChannel dma_channels[] = { - { .handler = NULL, .irq_line = NVIC_DMA_CH1 }, - { .handler = NULL, .irq_line = NVIC_DMA_CH2 }, - { .handler = NULL, .irq_line = NVIC_DMA_CH3 }, - { .handler = NULL, .irq_line = NVIC_DMA_CH4 }, - { .handler = NULL, .irq_line = NVIC_DMA_CH5 }, - { .handler = NULL, .irq_line = NVIC_DMA_CH6 }, - { .handler = NULL, .irq_line = NVIC_DMA_CH7 } +#include "bitband.h" +#include "util.h" + +/* + * Devices + */ + +static dma_dev dma1 = { + .regs = DMA1_BASE, + .clk_id = RCC_DMA1, + .handlers = {{ .handler = NULL, .irq_line = NVIC_DMA_CH1 }, + { .handler = NULL, .irq_line = NVIC_DMA_CH2 }, + { .handler = NULL, .irq_line = NVIC_DMA_CH3 }, + { .handler = NULL, .irq_line = NVIC_DMA_CH4 }, + { .handler = NULL, .irq_line = NVIC_DMA_CH5 }, + { .handler = NULL, .irq_line = NVIC_DMA_CH6 }, + { .handler = NULL, .irq_line = NVIC_DMA_CH7 }} +}; +dma_dev *DMA1 = &dma1; + +#ifdef STM32_HIGH_DENSITY +static dma_dev dma2 = { + .regs = DMA2_BASE, + .clk_id = RCC_DMA2, + .handlers = {{ .handler = NULL, .irq_line = NVIC_DMA2_CH1 }, + { .handler = NULL, .irq_line = NVIC_DMA2_CH2 }, + { .handler = NULL, .irq_line = NVIC_DMA2_CH3 }, + { .handler = NULL, .irq_line = NVIC_DMA2_CH_4_5 }, + { .handler = NULL, .irq_line = NVIC_DMA2_CH_4_5 }} /* !@#$ */ }; +dma_dev *DMA2 = &dma2; +#endif -/** Get the base address of the given channel, asserting and returning - * NULL on illegal +/* + * Convenience routines */ -static dma_regs *dma_get_regs(uint8 channel) { - if (channel >= 1 && channel <= 7) { - return (dma_regs *)(DMA1_CCR1 + DMA_CHANNEL_STRIDE * (channel-1)); - } else { - ASSERT(0); - return NULL; - } + +/** + * @brief Initialize a DMA device. + * @param dev Device to initialize. + */ +void dma_init(dma_dev *dev) { + rcc_clk_enable(dev->clk_id); } -/* Zero-based indexing */ -static inline void dispatch_handler(uint8 channel_idx) { - ASSERT(dma_channels[channel_idx].handler); - if (dma_channels[channel_idx].handler) { - (dma_channels[channel_idx].handler)(); - } +/** + * @brief Set up a DMA transfer. + * + * The channel will be disabled before being reconfigured. The + * transfer will have low priority by default. You may choose another + * priority before the transfer begins using dma_set_priority(), as + * well as performing any other configuration you desire. When the + * channel is configured to your liking, enable it using dma_enable(). + * + * @param dev DMA device. + * @param channel DMA channel. + * @param peripheral_address Base address of peripheral data register + * involved in the transfer. + * @param peripheral_size Peripheral data transfer size. + * @param memory_address Base memory address involved in the transfer. + * @param memory_size Memory data transfer size. + * @param mode Logical OR of dma_mode_flags + * @sideeffect Disables the given DMA channel. + * @see dma_xfer_size + * @see dma_mode_flags + * @see dma_set_num_transfers() + * @see dma_set_priority() + * @see dma_attach_interrupt() + * @see dma_enable() + */ +void dma_setup_transfer(dma_dev *dev, + dma_channel channel, + __io void *peripheral_address, + dma_xfer_size peripheral_size, + __io void *memory_address, + dma_xfer_size memory_size, + uint32 mode) { + dma_channel_reg_map *channel_regs = dma_channel_regs(dev, channel); + + dma_disable(dev, channel); /* can't write to CMAR/CPAR otherwise */ + channel_regs->CCR = (memory_size << 10) | (peripheral_size << 8) | mode; + channel_regs->CMAR = (uint32)memory_address; + channel_regs->CPAR = (uint32)peripheral_address; } -void DMAChannel1_IRQHandler(void) { - dispatch_handler(0); +/** + * @brief Set the number of data to be transferred on a DMA channel. + * + * You may not call this function while the channel is enabled. + * + * @param dev DMA device + * @param channel Channel through which the transfer occurs. + * @param num_transfers + */ +void dma_set_num_transfers(dma_dev *dev, + dma_channel channel, + uint16 num_transfers) { + dma_channel_reg_map *channel_regs; + + ASSERT_FAULT(!dma_is_channel_enabled(dev, channel)); + + channel_regs = dma_channel_regs(dev, channel); + channel_regs->CNDTR = num_transfers; +} + +/** + * @brief Set the priority of a DMA transfer. + * + * You may not call this function while the channel is enabled. + * + * @param dev DMA device + * @param channel DMA channel + * @param priority priority to set. + */ +void dma_set_priority(dma_dev *dev, + dma_channel channel, + dma_priority priority) { + dma_channel_reg_map *channel_regs; + uint32 ccr; + + ASSERT_FAULT(!dma_is_channel_enabled(dev, channel)); + + channel_regs = dma_channel_regs(dev, channel); + ccr = channel_regs->CCR; + ccr &= ~DMA_CCR_PL; + ccr |= priority; + channel_regs->CCR = ccr; } -void DMAChannel2_IRQHandler(void) { - dispatch_handler(1); +/** + * @brief Attach an interrupt to a DMA transfer. + * + * Interrupts are enabled using appropriate mode flags in + * dma_setup_transfer(). + * + * @param dev DMA device + * @param channel Channel to attach handler to + * @param handler Interrupt handler to call when channel interrupt fires. + * @see dma_setup_transfer() + * @see dma_get_irq_cause() + * @see dma_detach_interrupt() + */ +void dma_attach_interrupt(dma_dev *dev, + dma_channel channel, + void (*handler)(void)) { + dev->handlers[channel - 1].handler = handler; + nvic_irq_enable(dev->handlers[channel - 1].irq_line); } -void DMAChannel3_IRQHandler(void) { - dispatch_handler(2); +/** + * @brief Detach a DMA transfer interrupt handler. + * + * After calling this function, the given channel's interrupts will be + * disabled. + * + * @param dev DMA device + * @param channel Channel whose handler to detach + * @sideeffect Clears interrupt enable bits in the channel's CCR register. + * @see dma_attach_interrupt() + */ +void dma_detach_interrupt(dma_dev *dev, dma_channel channel) { + /* Don't use nvic_irq_disable()! Think about DMA2 channels 4 and 5. */ + dma_channel_regs(dev, channel)->CCR &= ~0xF; + dev->handlers[channel - 1].handler = NULL; } -void DMAChannel4_IRQHandler(void) { - dispatch_handler(3); +/** + * @brief Discover the reason why a DMA interrupt was called. + * + * You may only call this function within an attached interrupt + * handler for the given channel. + * + * This function resets the internal DMA register state which encodes + * the cause of the interrupt; consequently, it can only be called + * once per interrupt handler invocation. + * + * @brief dev DMA device + * @brief channel Channel whose interrupt is being handled. + * @return Reason why the interrupt fired. + * @sideeffect Clears channel status flags in dev->regs->ISR. + * @see dma_attach_interrupt() + * @see dma_irq_cause + */ +dma_irq_cause dma_get_irq_cause(dma_dev *dev, dma_channel channel) { + uint8 status_bits = dma_get_isr_bits(dev, channel); + + /* If the channel global interrupt flag is cleared, then + * something's very wrong. */ + ASSERT(status_bits & BIT(0)); + + dma_clear_isr_bits(dev, channel); + + /* ISR flags get set even if the corresponding interrupt enable + * bits in the channel's configuration register are cleared, so we + * can't use a switch here. + * + * Don't change the order of these if statements. */ + if (status_bits & BIT(3)) { + return DMA_TRANSFER_ERROR; + } else if (status_bits & BIT(1)) { + return DMA_TRANSFER_COMPLETE; + } else if (status_bits & BIT(2)) { + return DMA_TRANSFER_HALF_COMPLETE; + } else if (status_bits & BIT(0)) { + /* Shouldn't happen (unless someone messed up an IFCR write). */ + throb(); + } +#if DEBUG_LEVEL < DEBUG_ALL + else { + /* We shouldn't have been called, but the debug level is too + * low for the above ASSERT() to have had any effect. In + * order to fail fast, mimic the DMA controller's behavior + * when an error occurs. */ + dma_disable(dev, channel); + return DMA_TRANSFER_ERROR; + } +#endif } -void DMAChannel5_IRQHandler(void) { - dispatch_handler(4); +/** + * @brief Enable a DMA channel. + * @param dev DMA device + * @param channel Channel to enable + */ +void dma_enable(dma_dev *dev, dma_channel channel) { + dma_channel_reg_map *chan_regs = dma_channel_regs(dev, channel); + bb_peri_set_bit(&chan_regs->CCR, DMA_CCR_EN_BIT, 1); } -void DMAChannel6_IRQHandler(void) { - dispatch_handler(5); +/** + * @brief Disable a DMA channel. + * @param dev DMA device + * @param channel Channel to disable + */ +void dma_disable(dma_dev *dev, dma_channel channel) { + dma_channel_reg_map *chan_regs = dma_channel_regs(dev, channel); + bb_peri_set_bit(&chan_regs->CCR, DMA_CCR_EN_BIT, 0); } -void DMAChannel7_IRQHandler(void) { - dispatch_handler(6); +/** + * @brief Set the base memory address where data will be read from or + * written to. + * + * You must not call this function while the channel is enabled. + * + * If the DMA memory size is 16 bits, the address is automatically + * aligned to a half-word. If the DMA memory size is 32 bits, the + * address is aligned to a word. + * + * @param dev DMA Device + * @param channel Channel whose base memory address to set. + * @param addr Memory base address to use. + */ +void dma_set_mem_addr(dma_dev *dev, dma_channel channel, __io void *addr) { + dma_channel_reg_map *chan_regs; + + ASSERT_FAULT(!dma_is_channel_enabled(dev, channel)); + + chan_regs = dma_channel_regs(dev, channel); + chan_regs->CMAR = (uint32)addr; } -void dma_init(uint8 channel, volatile void *paddr, - dma_transfer_size psize, dma_transfer_size msize, - int mode) { - volatile dma_regs *regs = dma_get_regs(channel); +/** + * @brief Set the base peripheral address where data will be read from + * or written to. + * + * You must not call this function while the channel is enabled. + * + * If the DMA peripheral size is 16 bits, the address is automatically + * aligned to a half-word. If the DMA peripheral size is 32 bits, the + * address is aligned to a word. + * + * @param dev DMA Device + * @param channel Channel whose peripheral data register base address to set. + * @param addr Peripheral memory base address to use. + */ +void dma_set_per_addr(dma_dev *dev, dma_channel channel, __io void *addr) { + dma_channel_reg_map *chan_regs; - if (regs != NULL) { - rcc_clk_enable(RCC_DMA1); + ASSERT_FAULT(!dma_is_channel_enabled(dev, channel)); - regs->CCR = ((0 << 12) /* Low priority */ - | (msize << 10) - | (psize << 8) - | (0 << 0) /* Disabled (until started) */ - | mode); + chan_regs = dma_channel_regs(dev, channel); + chan_regs->CPAR = (uint32)addr; +} + +/* + * IRQ handlers + */ - regs->CPAR = (uint32)paddr; +static inline void dispatch_handler(dma_dev *dev, dma_channel channel) { + void (*handler)(void) = dev->handlers[channel - 1].handler; + if (handler) { + handler(); + dma_clear_isr_bits(dev, channel); /* in case handler doesn't */ } } -void dma_start(uint8 channel, volatile void *buffer, uint16 count) { - volatile dma_regs *regs = dma_get_regs(channel); +void __irq_dma_channel1(void) { + dispatch_handler(DMA1, DMA_CH1); +} - if (regs != NULL) { - regs->CCR &= ~DMA_EN; /* CMAR may not be written with EN set */ - regs->CMAR = (uint32)buffer; - regs->CNDTR = count; +void __irq_dma_channel2(void) { + dispatch_handler(DMA1, DMA_CH2); +} - regs->CCR |= DMA_EN; - } +void __irq_dma_channel3(void) { + dispatch_handler(DMA1, DMA_CH3); +} + +void __irq_dma_channel4(void) { + dispatch_handler(DMA1, DMA_CH4); +} + +void __irq_dma_channel5(void) { + dispatch_handler(DMA1, DMA_CH5); +} + +void __irq_dma_channel6(void) { + dispatch_handler(DMA1, DMA_CH6); +} + +void __irq_dma_channel7(void) { + dispatch_handler(DMA1, DMA_CH7); +} + +#ifdef STM32_HIGH_DENSITY +void __irq_dma2_channel1(void) { + dispatch_handler(DMA2, DMA_CH1); +} + +void __irq_dma2_channel2(void) { + dispatch_handler(DMA2, DMA_CH2); } -void dma_attach_interrupt(uint8 channel, voidFuncPtr handler) { - channel--; /* 1-based -> 0-based indexing */ - dma_channels[channel].handler = handler; - nvic_irq_enable(dma_channels[channel].irq_line); +void __irq_dma2_channel3(void) { + dispatch_handler(DMA2, DMA_CH3); } -void dma_detach_interrupt(uint8 channel) { - channel--; - nvic_irq_disable(dma_channels[channel].irq_line); - dma_channels[channel].handler = NULL; +void __irq_dma2_channel4_5(void) { + dispatch_handler(DMA2, DMA_CH4); + dispatch_handler(DMA2, DMA_CH5); } +#endif diff --git a/libmaple/dma.h b/libmaple/dma.h index 3417f02..7c380d0 100644 --- a/libmaple/dma.h +++ b/libmaple/dma.h @@ -23,97 +23,422 @@ *****************************************************************************/ /** - * @file dma.h + * @file dma.h * - * @brief Direct Memory Access peripheral support + * @author Marti Bolivar <mbolivar@leaflabs.com>; + * Original implementation by Michael Hope * - * TODO: add DMA2 support for high-density devices. + * @brief Direct Memory Access peripheral support + */ + +/* + * See /notes/dma.txt for more information. */ #ifndef _DMA_H_ #define _DMA_H_ #include "libmaple_types.h" +#include "rcc.h" +#include "nvic.h" #ifdef __cplusplus extern "C"{ #endif -/** Base address of the DMA1 peripheral */ -#define DMA1_BASE 0x40020000 -/** DMA Interrupt Status Register */ -#define DMA1_ISR (DMA1_BASE + 0x00) -/** DMA Interrupt Flag Clear Register */ -#define DMA1_IFCR (DMA1_BASE + 0x04) -/** DMA Channel Configuration Register */ -#define DMA1_CCR1 (DMA1_BASE + 0x08) -/** DMA Channel Number of Data Register */ -#define DMA1_CNDTR1 (DMA1_BASE + 0x0C) -/** DMA Channel Peripheral Address Register */ -#define DMA1_CPAR1 (DMA1_BASE + 0x10) -/** DMA Channel Memory Address Register */ -#define DMA1_CMAR1 (DMA1_BASE + 0x14) -/** Spacing between channel registers */ -#define DMA_CHANNEL_STRIDE 20 +/* + * Register maps + */ + +/** + * @brief DMA register map type. + * + * Note that DMA controller 2 (register map base pointer DMA2_BASE) + * only supports channels 1--5. + */ +typedef struct dma_reg_map { + __io uint32 ISR; /**< Interrupt status register */ + __io uint32 IFCR; /**< Interrupt flag clear register */ + __io uint32 CCR1; /**< Channel 1 configuration register */ + __io uint32 CNDTR1; /**< Channel 1 number of data register */ + __io uint32 CPAR1; /**< Channel 1 peripheral address register */ + __io uint32 CMAR1; /**< Channel 1 memory address register */ + const uint32 RESERVED1; /**< Reserved. */ + __io uint32 CCR2; /**< Channel 2 configuration register */ + __io uint32 CNDTR2; /**< Channel 2 number of data register */ + __io uint32 CPAR2; /**< Channel 2 peripheral address register */ + __io uint32 CMAR2; /**< Channel 2 memory address register */ + const uint32 RESERVED2; /**< Reserved. */ + __io uint32 CCR3; /**< Channel 3 configuration register */ + __io uint32 CNDTR3; /**< Channel 3 number of data register */ + __io uint32 CPAR3; /**< Channel 3 peripheral address register */ + __io uint32 CMAR3; /**< Channel 3 memory address register */ + const uint32 RESERVED3; /**< Reserved. */ + __io uint32 CCR4; /**< Channel 4 configuration register */ + __io uint32 CNDTR4; /**< Channel 4 number of data register */ + __io uint32 CPAR4; /**< Channel 4 peripheral address register */ + __io uint32 CMAR4; /**< Channel 4 memory address register */ + const uint32 RESERVED4; /**< Reserved. */ + __io uint32 CCR5; /**< Channel 5 configuration register */ + __io uint32 CNDTR5; /**< Channel 5 number of data register */ + __io uint32 CPAR5; /**< Channel 5 peripheral address register */ + __io uint32 CMAR5; /**< Channel 5 memory address register */ + const uint32 RESERVED5; /**< Reserved. */ + __io uint32 CCR6; /**< Channel 6 configuration register */ + __io uint32 CNDTR6; /**< Channel 6 number of data register */ + __io uint32 CPAR6; /**< Channel 6 peripheral address register */ + __io uint32 CMAR6; /**< Channel 6 memory address register */ + const uint32 RESERVED6; /**< Reserved. */ + __io uint32 CCR7; /**< Channel 7 configuration register */ + __io uint32 CNDTR7; /**< Channel 7 number of data register */ + __io uint32 CPAR7; /**< Channel 7 peripheral address register */ + __io uint32 CMAR7; /**< Channel 7 memory address register */ + const uint32 RESERVED7; /**< Reserved. */ +} dma_reg_map; + +/** DMA controller 1 register map base pointer */ +#define DMA1_BASE ((struct dma_reg_map*)0x40020000) + +#ifdef STM32_HIGH_DENSITY +/** DMA controller 2 register map base pointer */ +#define DMA2_BASE ((struct dma_reg_map*)0x40020400) +#endif + +/* + * Register bit definitions + */ + +/* Interrupt status register */ + +#define DMA_ISR_TEIF7_BIT 27 +#define DMA_ISR_HTIF7_BIT 26 +#define DMA_ISR_TCIF7_BIT 25 +#define DMA_ISR_GIF7_BIT 24 +#define DMA_ISR_TEIF6_BIT 23 +#define DMA_ISR_HTIF6_BIT 22 +#define DMA_ISR_TCIF6_BIT 21 +#define DMA_ISR_GIF6_BIT 20 +#define DMA_ISR_TEIF5_BIT 19 +#define DMA_ISR_HTIF5_BIT 18 +#define DMA_ISR_TCIF5_BIT 17 +#define DMA_ISR_GIF5_BIT 16 +#define DMA_ISR_TEIF4_BIT 15 +#define DMA_ISR_HTIF4_BIT 14 +#define DMA_ISR_TCIF4_BIT 13 +#define DMA_ISR_GIF4_BIT 12 +#define DMA_ISR_TEIF3_BIT 11 +#define DMA_ISR_HTIF3_BIT 10 +#define DMA_ISR_TCIF3_BIT 9 +#define DMA_ISR_GIF3_BIT 8 +#define DMA_ISR_TEIF2_BIT 7 +#define DMA_ISR_HTIF2_BIT 6 +#define DMA_ISR_TCIF2_BIT 5 +#define DMA_ISR_GIF2_BIT 4 +#define DMA_ISR_TEIF1_BIT 3 +#define DMA_ISR_HTIF1_BIT 2 +#define DMA_ISR_TCIF1_BIT 1 +#define DMA_ISR_GIF1_BIT 0 + +#define DMA_ISR_TEIF7 BIT(DMA_ISR_TEIF7_BIT) +#define DMA_ISR_HTIF7 BIT(DMA_ISR_HTIF7_BIT) +#define DMA_ISR_TCIF7 BIT(DMA_ISR_TCIF7_BIT) +#define DMA_ISR_GIF7 BIT(DMA_ISR_GIF7_BIT) +#define DMA_ISR_TEIF6 BIT(DMA_ISR_TEIF6_BIT) +#define DMA_ISR_HTIF6 BIT(DMA_ISR_HTIF6_BIT) +#define DMA_ISR_TCIF6 BIT(DMA_ISR_TCIF6_BIT) +#define DMA_ISR_GIF6 BIT(DMA_ISR_GIF6_BIT) +#define DMA_ISR_TEIF5 BIT(DMA_ISR_TEIF5_BIT) +#define DMA_ISR_HTIF5 BIT(DMA_ISR_HTIF5_BIT) +#define DMA_ISR_TCIF5 BIT(DMA_ISR_TCIF5_BIT) +#define DMA_ISR_GIF5 BIT(DMA_ISR_GIF5_BIT) +#define DMA_ISR_TEIF4 BIT(DMA_ISR_TEIF4_BIT) +#define DMA_ISR_HTIF4 BIT(DMA_ISR_HTIF4_BIT) +#define DMA_ISR_TCIF4 BIT(DMA_ISR_TCIF4_BIT) +#define DMA_ISR_GIF4 BIT(DMA_ISR_GIF4_BIT) +#define DMA_ISR_TEIF3 BIT(DMA_ISR_TEIF3_BIT) +#define DMA_ISR_HTIF3 BIT(DMA_ISR_HTIF3_BIT) +#define DMA_ISR_TCIF3 BIT(DMA_ISR_TCIF3_BIT) +#define DMA_ISR_GIF3 BIT(DMA_ISR_GIF3_BIT) +#define DMA_ISR_TEIF2 BIT(DMA_ISR_TEIF2_BIT) +#define DMA_ISR_HTIF2 BIT(DMA_ISR_HTIF2_BIT) +#define DMA_ISR_TCIF2 BIT(DMA_ISR_TCIF2_BIT) +#define DMA_ISR_GIF2 BIT(DMA_ISR_GIF2_BIT) +#define DMA_ISR_TEIF1 BIT(DMA_ISR_TEIF1_BIT) +#define DMA_ISR_HTIF1 BIT(DMA_ISR_HTIF1_BIT) +#define DMA_ISR_TCIF1 BIT(DMA_ISR_TCIF1_BIT) +#define DMA_ISR_GIF1 BIT(DMA_ISR_GIF1_BIT) + +/* Interrupt flag clear register */ + +#define DMA_IFCR_CTEIF7_BIT 27 +#define DMA_IFCR_CHTIF7_BIT 26 +#define DMA_IFCR_CTCIF7_BIT 25 +#define DMA_IFCR_CGIF7_BIT 24 +#define DMA_IFCR_CTEIF6_BIT 23 +#define DMA_IFCR_CHTIF6_BIT 22 +#define DMA_IFCR_CTCIF6_BIT 21 +#define DMA_IFCR_CGIF6_BIT 20 +#define DMA_IFCR_CTEIF5_BIT 19 +#define DMA_IFCR_CHTIF5_BIT 18 +#define DMA_IFCR_CTCIF5_BIT 17 +#define DMA_IFCR_CGIF5_BIT 16 +#define DMA_IFCR_CTEIF4_BIT 15 +#define DMA_IFCR_CHTIF4_BIT 14 +#define DMA_IFCR_CTCIF4_BIT 13 +#define DMA_IFCR_CGIF4_BIT 12 +#define DMA_IFCR_CTEIF3_BIT 11 +#define DMA_IFCR_CHTIF3_BIT 10 +#define DMA_IFCR_CTCIF3_BIT 9 +#define DMA_IFCR_CGIF3_BIT 8 +#define DMA_IFCR_CTEIF2_BIT 7 +#define DMA_IFCR_CHTIF2_BIT 6 +#define DMA_IFCR_CTCIF2_BIT 5 +#define DMA_IFCR_CGIF2_BIT 4 +#define DMA_IFCR_CTEIF1_BIT 3 +#define DMA_IFCR_CHTIF1_BIT 2 +#define DMA_IFCR_CTCIF1_BIT 1 +#define DMA_IFCR_CGIF1_BIT 0 + +#define DMA_IFCR_CTEIF7 BIT(DMA_IFCR_CTEIF7_BIT) +#define DMA_IFCR_CHTIF7 BIT(DMA_IFCR_CHTIF7_BIT) +#define DMA_IFCR_CTCIF7 BIT(DMA_IFCR_CTCIF7_BIT) +#define DMA_IFCR_CGIF7 BIT(DMA_IFCR_CGIF7_BIT) +#define DMA_IFCR_CTEIF6 BIT(DMA_IFCR_CTEIF6_BIT) +#define DMA_IFCR_CHTIF6 BIT(DMA_IFCR_CHTIF6_BIT) +#define DMA_IFCR_CTCIF6 BIT(DMA_IFCR_CTCIF6_BIT) +#define DMA_IFCR_CGIF6 BIT(DMA_IFCR_CGIF6_BIT) +#define DMA_IFCR_CTEIF5 BIT(DMA_IFCR_CTEIF5_BIT) +#define DMA_IFCR_CHTIF5 BIT(DMA_IFCR_CHTIF5_BIT) +#define DMA_IFCR_CTCIF5 BIT(DMA_IFCR_CTCIF5_BIT) +#define DMA_IFCR_CGIF5 BIT(DMA_IFCR_CGIF5_BIT) +#define DMA_IFCR_CTEIF4 BIT(DMA_IFCR_CTEIF4_BIT) +#define DMA_IFCR_CHTIF4 BIT(DMA_IFCR_CHTIF4_BIT) +#define DMA_IFCR_CTCIF4 BIT(DMA_IFCR_CTCIF4_BIT) +#define DMA_IFCR_CGIF4 BIT(DMA_IFCR_CGIF4_BIT) +#define DMA_IFCR_CTEIF3 BIT(DMA_IFCR_CTEIF3_BIT) +#define DMA_IFCR_CHTIF3 BIT(DMA_IFCR_CHTIF3_BIT) +#define DMA_IFCR_CTCIF3 BIT(DMA_IFCR_CTCIF3_BIT) +#define DMA_IFCR_CGIF3 BIT(DMA_IFCR_CGIF3_BIT) +#define DMA_IFCR_CTEIF2 BIT(DMA_IFCR_CTEIF2_BIT) +#define DMA_IFCR_CHTIF2 BIT(DMA_IFCR_CHTIF2_BIT) +#define DMA_IFCR_CTCIF2 BIT(DMA_IFCR_CTCIF2_BIT) +#define DMA_IFCR_CGIF2 BIT(DMA_IFCR_CGIF2_BIT) +#define DMA_IFCR_CTEIF1 BIT(DMA_IFCR_CTEIF1_BIT) +#define DMA_IFCR_CHTIF1 BIT(DMA_IFCR_CHTIF1_BIT) +#define DMA_IFCR_CTCIF1 BIT(DMA_IFCR_CTCIF1_BIT) +#define DMA_IFCR_CGIF1 BIT(DMA_IFCR_CGIF1_BIT) + +/* Channel configuration register */ + +#define DMA_CCR_MEM2MEM_BIT 14 +#define DMA_CCR_MINC_BIT 7 +#define DMA_CCR_PINC_BIT 6 +#define DMA_CCR_CIRC_BIT 5 +#define DMA_CCR_DIR_BIT 4 +#define DMA_CCR_TEIE_BIT 3 +#define DMA_CCR_HTIE_BIT 2 +#define DMA_CCR_TCIE_BIT 1 +#define DMA_CCR_EN_BIT 0 + +#define DMA_CCR_MEM2MEM BIT(DMA_CCR_MEM2MEM_BIT) +#define DMA_CCR_PL (0x3 << 12) +#define DMA_CCR_PL_LOW (0x0 << 12) +#define DMA_CCR_PL_MEDIUM (0x1 << 12) +#define DMA_CCR_PL_HIGH (0x2 << 12) +#define DMA_CCR_PL_VERY_HIGH (0x3 << 12) +#define DMA_CCR_MSIZE (0x3 << 10) +#define DMA_CCR_MSIZE_8BITS (0x0 << 10) +#define DMA_CCR_MSIZE_16BITS (0x1 << 10) +#define DMA_CCR_MSIZE_32BITS (0x2 << 10) +#define DMA_CCR_PSIZE (0x3 << 8) +#define DMA_CCR_PSIZE_8BITS (0x0 << 8) +#define DMA_CCR_PSIZE_16BITS (0x1 << 8) +#define DMA_CCR_PSIZE_32BITS (0x2 << 8) +#define DMA_CCR_MINC BIT(DMA_CCR_MINC_BIT) +#define DMA_CCR_PINC BIT(DMA_CCR_PINC_BIT) +#define DMA_CCR_CIRC BIT(DMA_CCR_CIRC_BIT) +#define DMA_CCR_DIR BIT(DMA_CCR_DIR_BIT) +#define DMA_CCR_TEIE BIT(DMA_CCR_TEIE_BIT) +#define DMA_CCR_HTIE BIT(DMA_CCR_HTIE_BIT) +#define DMA_CCR_TCIE BIT(DMA_CCR_TCIE_BIT) +#define DMA_CCR_EN BIT(DMA_CCR_EN_BIT) + +/* + * Devices + */ + +/** Encapsulates state related to a DMA channel interrupt. */ +typedef struct dma_handler_config { + void (*handler)(void); + nvic_irq_num irq_line; +} dma_handler_config; + +/** DMA device type */ +typedef struct dma_dev { + dma_reg_map *regs; /**< Register map */ + rcc_clk_id clk_id; /**< Clock ID */ + dma_handler_config handlers[]; /**< IRQ handlers and NVIC numbers. */ +} dma_dev; + +/** DMA1 device */ +extern dma_dev *DMA1; +#ifdef STM32_HIGH_DENSITY +/** DMA2 device */ +extern dma_dev *DMA2; +#endif + +/* + * Convenience functions + */ + +void dma_init(dma_dev *dev); /** Flags for DMA transfer configuration. */ typedef enum dma_mode_flags { - DMA_MINC_MODE = 1 << 7, /**< Auto-increment memory address */ - DMA_PINC_MODE = 1 << 6, /**< Auto-increment peripheral address */ - DMA_CIRC_MODE = 1 << 5, /**< Circular mode */ - DMA_FROM_MEM = 1 << 4, /**< Read from memory to peripheral */ - DMA_TRNS_ERR = 1 << 3, /**< Interrupt on transfer error */ - DMA_HALF_TRNS = 1 << 2, /**< Interrupt on half-transfer */ - DMA_TRNS_CMPLT = 1 << 1 /**< Interrupt on transfer completion */ + DMA_MEM_2_MEM = 1 << 14, /**< Memory to memory mode */ + DMA_MINC_MODE = 1 << 7, /**< Auto-increment memory address */ + DMA_PINC_MODE = 1 << 6, /**< Auto-increment peripheral address */ + DMA_CIRC_MODE = 1 << 5, /**< Circular mode */ + DMA_FROM_MEM = 1 << 4, /**< Read from memory to peripheral */ + DMA_TRNS_ERR = 1 << 3, /**< Interrupt on transfer error */ + DMA_HALF_TRNS = 1 << 2, /**< Interrupt on half-transfer */ + DMA_TRNS_CMPLT = 1 << 1 /**< Interrupt on transfer completion */ } dma_mode_flags; /** Source and destination transfer sizes. */ -typedef enum dma_transfer_size { - DMA_SIZE_8BITS = 0, - DMA_SIZE_16BITS = 1, - DMA_SIZE_32BITS = 2 -} dma_transfer_size; +typedef enum dma_xfer_size { + DMA_SIZE_8BITS = 0, /**< 8-bit transfers */ + DMA_SIZE_16BITS = 1, /**< 16-bit transfers */ + DMA_SIZE_32BITS = 2 /**< 32-bit transfers */ +} dma_xfer_size; + +/** DMA channel */ +typedef enum dma_channel { + DMA_CH1 = 1, /**< Channel 1 */ + DMA_CH2 = 2, /**< Channel 2 */ + DMA_CH3 = 3, /**< Channel 3 */ + DMA_CH4 = 4, /**< Channel 4 */ + DMA_CH5 = 5, /**< Channel 5 */ + DMA_CH6 = 6, /**< Channel 6 */ + DMA_CH7 = 7, /**< Channel 7 */ +} dma_channel; + +void dma_setup_transfer(dma_dev *dev, + dma_channel channel, + __io void *peripheral_address, + dma_xfer_size peripheral_size, + __io void *memory_address, + dma_xfer_size memory_size, + uint32 mode); + +void dma_set_num_transfers(dma_dev *dev, + dma_channel channel, + uint16 num_transfers); + +/** DMA transfer priority. */ +typedef enum dma_priority { + DMA_PRIORITY_LOW = DMA_CCR_PL_LOW, /**< Low priority */ + DMA_PRIORITY_MEDIUM = DMA_CCR_PL_MEDIUM, /**< Medium priority */ + DMA_PRIORITY_HIGH = DMA_CCR_PL_HIGH, /**< High priority */ + DMA_PRIORITY_VERY_HIGH = DMA_CCR_PL_VERY_HIGH /**< Very high priority */ +} dma_priority; + +void dma_set_priority(dma_dev *dev, + dma_channel channel, + dma_priority priority); + +void dma_attach_interrupt(dma_dev *dev, + dma_channel channel, + void (*handler)(void)); +void dma_detach_interrupt(dma_dev *dev, dma_channel channel); + +/** + * Encodes the reason why a DMA interrupt was called. + * @see dma_get_irq_cause() + */ +typedef enum dma_irq_cause { + DMA_TRANSFER_COMPLETE, /**< Transfer is complete. */ + DMA_TRANSFER_HALF_COMPLETE, /**< Transfer is half complete. */ + DMA_TRANSFER_ERROR, /**< Error occurred during transfer. */ +} dma_irq_cause; + +dma_irq_cause dma_get_irq_cause(dma_dev *dev, dma_channel channel); + +void dma_enable(dma_dev *dev, dma_channel channel); +void dma_disable(dma_dev *dev, dma_channel channel); + +void dma_set_mem_addr(dma_dev *dev, dma_channel channel, __io void *address); +void dma_set_per_addr(dma_dev *dev, dma_channel channel, __io void *address); /** - * Initialize a DMA channel. If desired, attach an interrupt handler - * using dma_attach_interrupt(). Start the transfer using - * dma_start(). + * @brief DMA channel register map type. * - * @param channel the channel number (1..7) - * @param paddr address of the peripheral - * @param psize peripheral size - * @param msize memory size - * @param mode OR of the dma_mode_flags - * @see dma_mode_flags - * @see dma_attach_interrupt() - * @see dma_start() */ -void dma_init(uint8 channel, volatile void *paddr, - dma_transfer_size psize, dma_transfer_size msize, - int mode); + * Provides access to an individual channel's registers. + */ +typedef struct dma_channel_reg_map { + __io uint32 CCR; /**< Channel configuration register */ + __io uint32 CNDTR; /**< Channel number of data register */ + __io uint32 CPAR; /**< Channel peripheral address register */ + __io uint32 CMAR; /**< Channel memory address register */ +} dma_channel_reg_map; + +#define DMA_CHANNEL_NREGS 5 /** - * Begin a DMA transfer initialized with dma_init(). + * @brief Obtain a pointer to an individual DMA channel's registers. * - * @param channel Channel transfer to start. - * @param buffer Buffer to write to (unless DMA_FROM_MEM was set in - * mode argument to dma_init(), in which case, buffer - * to read from). This must be aligned with msize - * argument to dma_init(). - * @param count Number of elements to transfer. - * @see dma_init() */ -void dma_start(uint8 channel, volatile void *buffer, uint16 count); + * For example, dma_channel_regs(DMA1, DMA_CH1)->CCR is DMA1_BASE->CCR1. + * + * @param dev DMA device + * @param channel DMA channel whose channel register map to obtain. + */ +static inline dma_channel_reg_map* dma_channel_regs(dma_dev *dev, + dma_channel channel) { + __io uint32 *ccr1 = &dev->regs->CCR1; + return (dma_channel_reg_map*)(ccr1 + DMA_CHANNEL_NREGS * (channel - 1)); +} + +/** + * @brief Check if a DMA channel is enabled + * @param dev DMA device + * @param channel Channel whose enabled bit to check. + */ +static inline uint8 dma_is_channel_enabled(dma_dev *dev, dma_channel channel) { + return (uint8)(dma_channel_regs(dev, channel)->CCR & DMA_CCR_EN); +} /** - * Attach an interrupt handler for the given DMA channel. - * @param channel DMA channel (1..7) - * @param handler Interrupt handler to attach - * @see voidFuncPtr */ -void dma_attach_interrupt(uint8 channel, voidFuncPtr handler); + * @brief Get the ISR status bits for a DMA channel. + * + * The bits are returned right-aligned, in the following order: + * transfer error flag, half-transfer flag, transfer complete flag, + * global interrupt flag. + * + * If you're attempting to figure out why a DMA interrupt fired; you + * may find dma_get_irq_cause() more convenient. + * + * @param dev DMA device + * @param channel Channel whose ISR bits to return. + * @see dma_get_irq_cause(). + */ +static inline uint8 dma_get_isr_bits(dma_dev *dev, dma_channel channel) { + uint8 shift = (channel - 1) * 4; + return (dev->regs->ISR >> shift) & 0xF; +} /** - * Detach any handler associated with the given DMA channel. - * @param channel Channel whose interrupt handler to detach. */ -void dma_detach_interrupt(uint8 channel); + * @brief Clear the ISR status bits for a given DMA channel. + * + * If you're attempting to clean up after yourself in a DMA interrupt, + * you may find dma_get_irq_cause() more convenient. + * + * @param dev DMA device + * @param channel Channel whose ISR bits to clear. + * @see dma_get_irq_cause() + */ +static inline void dma_clear_isr_bits(dma_dev *dev, dma_channel channel) { + dev->regs->IFCR = BIT(4 * (channel - 1)); +} #ifdef __cplusplus } // extern "C" diff --git a/libmaple/exc.S b/libmaple/exc.S index a9f6561..7631e48 100644 --- a/libmaple/exc.S +++ b/libmaple/exc.S @@ -41,21 +41,61 @@ # SP--> r0 .text -.globl HardFaultException +.globl __exc_hardfault +.globl __exc_nmi +.globl __exc_hardfault +.globl __exc_memmanage +.globl __exc_busfault +.globl __exc_usagefault + +.code 16 +.thumb_func +__exc_nmi: + mov r0, #1 + b __default_exc + +.thumb_func +__exc_hardfault: + mov r0, #2 + b __default_exc + +.thumb_func +__exc_memmanage: + mov r0, #3 + b __default_exc + +.thumb_func +__exc_busfault: + mov r0, #4 + b __default_exc + +.thumb_func +__exc_usagefault: + mov r0, #5 + b __default_exc + .thumb_func -HardFaultException: - ldr r0, CPSR_MASK @ Set default CPSR - push {r0} - ldr r0, TARGET_PC @ Set target pc - push {r0} - sub sp, sp, #24 @ its not like i even care - ldr r0, EXC_RETURN @ Return to thread mode - mov lr, r0 - bx lr @ Exception exit - - .align 4 - CPSR_MASK: .word 0x61000000 - EXC_RETURN: .word 0xFFFFFFF9 - TARGET_PC: .word throb +__default_exc: + ldr r2, NVIC_CCR @ Enable returning to thread mode even if there are + mov r1 ,#1 @ pending exceptions. See flag NONEBASETHRDENA. + str r1, [r2] + cpsid i @ Disable global interrupts + ldr r2, SYSTICK_CSR @ Disable systick handler + mov r1, #0 + str r1, [r2] + ldr r1, CPSR_MASK @ Set default CPSR + push {r1} + ldr r1, TARGET_PC @ Set target pc + push {r1} + sub sp, sp, #24 @ Don't care + ldr r1, EXC_RETURN @ Return to thread mode + mov lr, r1 + bx lr @ Exception exit +.align 4 +CPSR_MASK: .word 0x61000000 +EXC_RETURN: .word 0xFFFFFFF9 +TARGET_PC: .word __error +NVIC_CCR: .word 0xE000ED14 @ NVIC configuration control register +SYSTICK_CSR: .word 0xE000E010 @ Systick control register diff --git a/libmaple/exti.c b/libmaple/exti.c index 150dd05..38d3bba 100644 --- a/libmaple/exti.c +++ b/libmaple/exti.c @@ -23,184 +23,228 @@ *****************************************************************************/ /** + * @file exti.c * @brief External interrupt control routines */ #include "libmaple.h" #include "exti.h" #include "nvic.h" +#include "bitband.h" -typedef struct ExtIChannel { +/* + * Internal state + */ + +/* Status bitmaps, for external interrupts with multiplexed IRQs */ +static uint16 exti_9_5_en = 0; +static uint16 exti_15_10_en = 0; + +typedef struct exti_channel { void (*handler)(void); uint32 irq_line; -} ExtIChannel; - -static ExtIChannel exti_channels[] = { - { .handler = NULL, .irq_line = NVIC_EXTI0 }, // EXTI0 - { .handler = NULL, .irq_line = NVIC_EXTI1 }, // EXTI1 - { .handler = NULL, .irq_line = NVIC_EXTI2 }, // EXTI2 - { .handler = NULL, .irq_line = NVIC_EXTI3 }, // EXTI3 - { .handler = NULL, .irq_line = NVIC_EXTI4 }, // EXTI4 - { .handler = NULL, .irq_line = NVIC_EXTI9_5 }, // EXTI5 - { .handler = NULL, .irq_line = NVIC_EXTI9_5 }, // EXTI6 - { .handler = NULL, .irq_line = NVIC_EXTI9_5 }, // EXTI7 - { .handler = NULL, .irq_line = NVIC_EXTI9_5 }, // EXTI8 - { .handler = NULL, .irq_line = NVIC_EXTI9_5 }, // EXTI9 - { .handler = NULL, .irq_line = NVIC_EXTI15_10 }, // EXTI10 - { .handler = NULL, .irq_line = NVIC_EXTI15_10 }, // EXTI11 - { .handler = NULL, .irq_line = NVIC_EXTI15_10 }, // EXTI12 - { .handler = NULL, .irq_line = NVIC_EXTI15_10 }, // EXTI13 - { .handler = NULL, .irq_line = NVIC_EXTI15_10 }, // EXTI14 - { .handler = NULL, .irq_line = NVIC_EXTI15_10 }, // EXTI15 +} exti_channel; + +static exti_channel exti_channels[] = { + { .handler = NULL, .irq_line = NVIC_EXTI0 }, // EXTI0 + { .handler = NULL, .irq_line = NVIC_EXTI1 }, // EXTI1 + { .handler = NULL, .irq_line = NVIC_EXTI2 }, // EXTI2 + { .handler = NULL, .irq_line = NVIC_EXTI3 }, // EXTI3 + { .handler = NULL, .irq_line = NVIC_EXTI4 }, // EXTI4 + { .handler = NULL, .irq_line = NVIC_EXTI_9_5 }, // EXTI5 + { .handler = NULL, .irq_line = NVIC_EXTI_9_5 }, // EXTI6 + { .handler = NULL, .irq_line = NVIC_EXTI_9_5 }, // EXTI7 + { .handler = NULL, .irq_line = NVIC_EXTI_9_5 }, // EXTI8 + { .handler = NULL, .irq_line = NVIC_EXTI_9_5 }, // EXTI9 + { .handler = NULL, .irq_line = NVIC_EXTI_15_10 }, // EXTI10 + { .handler = NULL, .irq_line = NVIC_EXTI_15_10 }, // EXTI11 + { .handler = NULL, .irq_line = NVIC_EXTI_15_10 }, // EXTI12 + { .handler = NULL, .irq_line = NVIC_EXTI_15_10 }, // EXTI13 + { .handler = NULL, .irq_line = NVIC_EXTI_15_10 }, // EXTI14 + { .handler = NULL, .irq_line = NVIC_EXTI_15_10 }, // EXTI15 }; -static inline void clear_pending(int bit) { - __set_bits(EXTI_PR, BIT(bit)); - /* If the pending bit is cleared as the last instruction in an ISR, - * it won't actually be cleared in time and the ISR will fire again. - * Insert a 2-cycle buffer to allow it to take effect. */ - asm volatile("nop"); - asm volatile("nop"); -} +/* + * Convenience routines + */ + +static inline void enable_irq(afio_exti_num exti_num); +static inline void maybe_disable_irq(afio_exti_num exti_num); + +/** + * @brief Register a handler to run upon external interrupt. + * + * This function assumes that the interrupt request corresponding to + * the given external interrupt is masked. + * + * @param num External interrupt line number. + * @param port Port to use as source input for external interrupt. + * @param handler Function handler to execute when interrupt is triggered. + * @param mode Type of transition to trigger on, one of: + * EXTI_RISING, EXTI_FALLING, EXTI_RISING_FALLING. + * @see exti_num + * @see exti_port + * @see exti_trigger_mode + */ +void exti_attach_interrupt(afio_exti_num num, + afio_exti_port port, + voidFuncPtr handler, + exti_trigger_mode mode) { + ASSERT(handler); -static inline void dispatch_handler(uint32 channel) { - ASSERT(exti_channels[channel].handler); - if (exti_channels[channel].handler) { - (exti_channels[channel].handler)(); + /* Register the handler */ + exti_channels[num].handler = handler; + + /* Set trigger mode */ + switch (mode) { + case EXTI_RISING: + *bb_perip(&EXTI_BASE->RTSR, num) = 1; + break; + case EXTI_FALLING: + *bb_perip(&EXTI_BASE->FTSR, num) = 1; + break; + case EXTI_RISING_FALLING: + *bb_perip(&EXTI_BASE->RTSR, num) = 1; + *bb_perip(&EXTI_BASE->FTSR, num) = 1; + break; } + + /* Map num to port */ + afio_exti_select(num, port); + + /* Unmask external interrupt request */ + *bb_perip(&EXTI_BASE->IMR, num) = 1; + + /* Enable the interrupt line */ + enable_irq(num); +} + +/** + * @brief Unregister an external interrupt handler + * @param num Number of the external interrupt line to disable. + * @see exti_num + */ +void exti_detach_interrupt(afio_exti_num num) { + /* First, mask the interrupt request */ + *bb_perip(&EXTI_BASE->IMR, num) = 0; + + /* Then, clear the trigger selection registers */ + *bb_perip(&EXTI_BASE->FTSR, num) = 0; + *bb_perip(&EXTI_BASE->RTSR, num) = 0; + + /* Next, disable the IRQ, unless it's multiplexed and there are + * other active external interrupts on the same IRQ line */ + maybe_disable_irq(num); + + /* Finally, unregister the user's handler */ + exti_channels[num].handler = NULL; } -/* For EXTI0 through EXTI4, only one handler - * is associated with each channel, so we - * don't have to keep track of which channel +/* + * Interrupt handlers + */ + +static inline void clear_pending(uint32 exti_num); +static inline void dispatch_handler(uint32 exti_num); + +/* For AFIO_EXTI_0 through AFIO_EXTI_4, only one handler is associated + * with each channel, so we don't have to keep track of which channel * we came from */ -void EXTI0_IRQHandler(void) { - dispatch_handler(EXTI0); - clear_pending(EXTI0); +void __irq_exti0(void) { + dispatch_handler(AFIO_EXTI_0); + clear_pending(AFIO_EXTI_0); } -void EXTI1_IRQHandler(void) { - dispatch_handler(EXTI1); - clear_pending(EXTI1); +void __irq_exti1(void) { + dispatch_handler(AFIO_EXTI_1); + clear_pending(AFIO_EXTI_1); } -void EXTI2_IRQHandler(void) { - dispatch_handler(EXTI2); - clear_pending(EXTI2); +void __irq_exti2(void) { + dispatch_handler(AFIO_EXTI_2); + clear_pending(AFIO_EXTI_2); } -void EXTI3_IRQHandler(void) { - dispatch_handler(EXTI3); - clear_pending(EXTI3); +void __irq_exti3(void) { + dispatch_handler(AFIO_EXTI_3); + clear_pending(AFIO_EXTI_3); } -void EXTI4_IRQHandler(void) { - dispatch_handler(EXTI4); - clear_pending(EXTI4); +void __irq_exti4(void) { + dispatch_handler(AFIO_EXTI_4); + clear_pending(AFIO_EXTI_4); } -void EXTI9_5_IRQHandler(void) { +void __irq_exti9_5(void) { /* Figure out which channel it came from */ - uint32 pending; + uint32 pending = GET_BITS(EXTI_BASE->PR, 5, 9); uint32 i; - pending = REG_GET(EXTI_PR); - pending = GET_BITS(pending, 5, 9); /* Dispatch every handler if the pending bit is set */ for (i = 0; i < 5; i++) { if (pending & 0x1) { - dispatch_handler(EXTI5 + i); - clear_pending(EXTI5 + i); + dispatch_handler(AFIO_EXTI_5 + i); + clear_pending(AFIO_EXTI_5 + i); } pending >>= 1; } } -void EXTI15_10_IRQHandler(void) { +void __irq_exti15_10(void) { /* Figure out which channel it came from */ - uint32 pending; + uint32 pending = GET_BITS(EXTI_BASE->PR, 10, 15); uint32 i; - pending = REG_GET(EXTI_PR); - pending = GET_BITS(pending, 10, 15); /* Dispatch every handler if the pending bit is set */ for (i = 0; i < 6; i++) { if (pending & 0x1) { - dispatch_handler(EXTI10 + i); - clear_pending(EXTI10 + i); + dispatch_handler(AFIO_EXTI_10 + i); + clear_pending(AFIO_EXTI_10 + i); } pending >>= 1; } } - -/** - * @brief Register a handler to run upon external interrupt - * @param port source port of pin (eg EXTI_CONFIG_PORTA) - * @param pin pin number on the source port - * @param handler function handler to execute - * @param mode type of transition to trigger on +/* + * Auxiliary functions */ -void exti_attach_interrupt(uint32 port, - uint32 pin, - voidFuncPtr handler, - uint32 mode) { - static uint32 afio_regs[] = { - AFIO_EXTICR1, // EXT0-3 - AFIO_EXTICR2, // EXT4-7 - AFIO_EXTICR3, // EXT8-11 - AFIO_EXTICR4, // EXT12-15 - }; - - /* Note: All of the following code assumes that EXTI0 = 0 */ - ASSERT(EXTI0 == 0); - ASSERT(handler); - - uint32 channel = pin; - /* map port to channel */ - __write(afio_regs[pin/4], (port << ((pin % 4) * 4))); - - /* Unmask appropriate interrupt line */ - __set_bits(EXTI_IMR, BIT(channel)); - - /* Set trigger mode */ - switch (mode) { - case EXTI_RISING: - __set_bits(EXTI_RTSR, BIT(channel)); - break; - - case EXTI_FALLING: - __set_bits(EXTI_FTSR, BIT(channel)); - break; +static inline void clear_pending(uint32 exti_num) { + *bb_perip(&EXTI_BASE->PR, exti_num) = 1; + /* If the pending bit is cleared as the last instruction in an ISR, + * it won't actually be cleared in time and the ISR will fire again. + * Insert a 2-cycle buffer to allow it to take effect. */ + asm volatile("nop"); + asm volatile("nop"); +} - case EXTI_RISING_FALLING: - __set_bits(EXTI_RTSR, BIT(channel)); - __set_bits(EXTI_FTSR, BIT(channel)); - break; +static inline void dispatch_handler(uint32 exti_num) { + ASSERT(exti_channels[exti_num].handler); + if (exti_channels[exti_num].handler) { + (exti_channels[exti_num].handler)(); } - - /* Register the handler */ - exti_channels[channel].handler = handler; - - /* Configure the enable interrupt bits for the NVIC */ - nvic_irq_enable(exti_channels[channel].irq_line); } +static inline void enable_irq(afio_exti_num exti) { + /* Maybe twiddle the IRQ bitmap for extis with multiplexed IRQs */ + if (exti > 4) { + uint16 *bitmap = exti < 10 ? &exti_9_5_en : &exti_15_10_en; + *bb_sramp(bitmap, exti) = 1; + } -/** - * @brief Unregister an external interrupt handler - * @param channel channel to disable (eg EXTI0) - */ -void exti_detach_interrupt(uint32 channel) { - ASSERT(channel < NR_EXTI_CHANNELS); - ASSERT(EXTI0 == 0); - - __clear_bits(EXTI_IMR, BIT(channel)); - __clear_bits(EXTI_FTSR, BIT(channel)); - __clear_bits(EXTI_RTSR, BIT(channel)); - - nvic_irq_disable(exti_channels[channel].irq_line); + nvic_irq_enable(exti_channels[exti].irq_line); +} - exti_channels[channel].handler = NULL; +static inline void maybe_disable_irq(afio_exti_num exti) { + if (exti > 4) { + uint16 *bitmap = exti < 10 ? &exti_9_5_en : &exti_15_10_en; + *bb_sramp(bitmap, exti) = 0; + if (*bitmap == 0) { + /* All of the external interrupts which share this IRQ + * line are disabled. */ + nvic_irq_disable(exti_channels[exti].irq_line); + } + } else { + nvic_irq_disable(exti_channels[exti].irq_line); + } } diff --git a/libmaple/exti.h b/libmaple/exti.h index 806578f..e145033 100644 --- a/libmaple/exti.h +++ b/libmaple/exti.h @@ -22,145 +22,51 @@ * THE SOFTWARE. *****************************************************************************/ - /** - * @file exti.h - * - * @brief External interrupt control prototypes and defines + * @file exti.h + * @brief External interrupt control prototypes and defines */ -#ifndef _EXTI_H_ -#define _EXTI_H_ - -/* 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_EXTIICR[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." - * - * ---------------------------------------------------------------------------- - * Pin to EXTI Line Mappings: - * EXTI0 EXTI1 EXTI2 EXTI3 EXTI4 - * -------------------------------------------------------------------------- - * D2/PA0 D3/PA1 D1/PA2 D0/A6/PA3 D10/A10/PA4 - * D26/EXT7/PB0 D27/EXT8/PB1 D16/A2/PC2 D17/A3/PC3 D18/A4/PC4 - * D14/A0/PC0 D15/PC1 D25/EXT5/PD2 - * - * EXTI5 EXTI6 EXTI7 EXTI8 EXTI9 - * ---------------------------------------------------------------------------- - * D13/A13/PA5 D12/A12/PA6 D11/A11/PA7 D6/PA8 D7/PA9 - * D4/PB5 D5/PB6 D9/PB7 D38/PB8 D23/EXT4/PB9 - * D19/A5/PC5 D34/EXTI15/PC6 D35/EXT16/PC7 D36/PC8 D37/EXT18/PC9 - * - * EXTI10 EXTI11 EXTI12 EXTI13 EXTI14 - * ---------------------------------------------------------------------------- - * D8/PA10 D29/EXT10/PB11 D30/EXTI1/PB12 D31/EXTI12/PB13 D32/EXT13/PB14 - * D28/PB10 D20/EXTI1/PC13 D21/EXT2/PC14 - * D25/PC10 - * - * EXTI15 - * ---------------------------------------------------------------------------- - * D33/EXTI14/PB15 - * D22/EXT3/PC15 - * - * - * 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 - * - * */ - -#define NR_EXTI_MODES 3 -#define NR_EXTI_CHANNELS 16 -#define NR_EXTI_PORTS NR_GPIO_PORTS // board specific +/* See notes/exti.txt for more info */ -#define EXTI_RISING 0 -#define EXTI_FALLING 1 -#define EXTI_RISING_FALLING 2 +#include "libmaple.h" +#include "gpio.h" -#define EXTI_IMR 0x40010400 // Interrupt mask register -#define EXTI_EMR (EXTI_IMR + 0x04) // Event mask register -#define EXTI_RTSR (EXTI_IMR + 0x08) // Rising trigger selection register -#define EXTI_FTSR (EXTI_IMR + 0x0C) // Falling trigger selection register -#define EXTI_SWIER (EXTI_IMR + 0x10) // Software interrupt event register -#define EXTI_PR (EXTI_IMR + 0x14) // Pending register - -#define AFIO_EVCR 0x40010000 -#define AFIO_EXTICR1 (AFIO_EVCR + 0x08) -#define AFIO_EXTICR2 (AFIO_EVCR + 0x0C) -#define AFIO_EXTICR3 (AFIO_EVCR + 0x10) -#define AFIO_EXTICR4 (AFIO_EVCR + 0x14) - -#define EXTI0 0 -#define EXTI1 1 -#define EXTI2 2 -#define EXTI3 3 -#define EXTI4 4 -#define EXTI5 5 -#define EXTI6 6 -#define EXTI7 7 -#define EXTI8 8 -#define EXTI9 9 -#define EXTI10 10 -#define EXTI11 11 -#define EXTI12 12 -#define EXTI13 13 -#define EXTI14 14 -#define EXTI15 15 - -#define EXTI_CONFIG_PORTA 0 // Maple, Maple Native, Maple Mini -#define EXTI_CONFIG_PORTB 1 // Maple, Maple Native, Maple Mini -#define EXTI_CONFIG_PORTC 2 // Maple, Maple Native, Maple Mini -#define EXTI_CONFIG_PORTD 3 // Maple and Maple Native only -#define EXTI_CONFIG_PORTE 4 // Native only -#define EXTI_CONFIG_PORTF 5 // Native only -#define EXTI_CONFIG_PORTG 6 // Native only +#ifndef _EXTI_H_ +#define _EXTI_H_ #ifdef __cplusplus extern "C"{ #endif -void exti_attach_interrupt(uint32 port, uint32 pin, voidFuncPtr handler, - uint32 mode); -void exti_detach_interrupt(uint32 channel); +/** EXTI register map type */ +typedef struct exti_reg_map { + __io uint32 IMR; /**< Interrupt mask register */ + __io uint32 EMR; /**< Event mask register */ + __io uint32 RTSR; /**< Rising trigger selection register */ + __io uint32 FTSR; /**< Falling trigger selection register */ + __io uint32 SWIER; /**< Software interrupt event register */ + __io uint32 PR; /**< Pending register */ +} exti_reg_map; + +/** EXTI register map base pointer */ +#define EXTI_BASE ((exti_reg_map*)0x40010400) + +/** External interrupt trigger mode */ +typedef enum exti_trigger_mode { + EXTI_RISING, /**< Trigger on the rising edge */ + EXTI_FALLING, /**< Trigger on the falling edge */ + EXTI_RISING_FALLING /**< Trigger on both the rising and falling edges */ +} exti_trigger_mode; + +void exti_attach_interrupt(afio_exti_num num, + afio_exti_port port, + voidFuncPtr handler, + exti_trigger_mode mode); +void exti_detach_interrupt(afio_exti_num num); #ifdef __cplusplus } // extern "C" #endif - #endif - diff --git a/libmaple/flash.c b/libmaple/flash.c index 1d7bfa6..c921256 100644 --- a/libmaple/flash.c +++ b/libmaple/flash.c @@ -28,41 +28,30 @@ #include "libmaple.h" #include "flash.h" - -/* flash registers */ -#define FLASH_BASE 0x40022000 -#define FLASH_ACR FLASH_BASE - -/* flash prefetcher */ -#define ACR_PRFTBE BIT(4) -#define ACR_PRFTBE_ENABLE BIT(4) - -/* flash wait states */ -#define ACR_LATENCY (0x7) - -#define FLASH_WRITE_ACR(val) __write(FLASH_ACR, val) -#define FLASH_READ_ACR() __read(FLASH_ACR) +#include "bitband.h" /** - * @brief turn on the hardware prefetcher + * @brief Turn on the hardware prefetcher. */ void flash_enable_prefetch(void) { - uint32 val = FLASH_READ_ACR(); - - val |= ACR_PRFTBE_ENABLE; - - FLASH_WRITE_ACR(val); + *bb_perip(&FLASH_BASE->ACR, FLASH_ACR_PRFTBE_BIT) = 1; } /** - * @brief set flash wait states - * @param number of wait states + * @brief Set flash wait states + * + * See ST PM0042, section 3.1 for restrictions on the acceptable value + * of wait_states for a given SYSCLK configuration. + * + * @param wait_states number of wait states (one of + * FLASH_WAIT_STATE_0, FLASH_WAIT_STATE_1, + * FLASH_WAIT_STATE_2). */ void flash_set_latency(uint32 wait_states) { - uint32 val = FLASH_READ_ACR(); + uint32 val = FLASH_BASE->ACR; - val &= ~ACR_LATENCY; + val &= ~FLASH_ACR_LATENCY; val |= wait_states; - FLASH_WRITE_ACR(val); + FLASH_BASE->ACR = val; } diff --git a/libmaple/flash.h b/libmaple/flash.h index 7b74c83..9db5015 100644 --- a/libmaple/flash.h +++ b/libmaple/flash.h @@ -25,20 +25,108 @@ /** * @file flash.h - * @brief basic stm32 flash setup routines + * @brief STM32 Medium and high density Flash register map and setup + * routines */ #ifndef _FLASH_H_ #define _FLASH_H_ -#define FLASH_WAIT_STATE_0 0x0 -#define FLASH_WAIT_STATE_1 0x1 -#define FLASH_WAIT_STATE_2 0x2 - #ifdef __cplusplus extern "C"{ #endif +/** Flash register map type */ +typedef struct flash_reg_map { + __io uint32 ACR; /**< Access control register */ + __io uint32 KEYR; /**< Key register */ + __io uint32 OPTKEYR; /**< OPTKEY register */ + __io uint32 SR; /**< Status register */ + __io uint32 CR; /**< Control register */ + __io uint32 AR; /**< Address register */ + __io uint32 OBR; /**< Option byte register */ + __io uint32 WRPR; /**< Write protection register */ +} flash_reg_map; + +/** Flash register map base pointer */ +#define FLASH_BASE ((flash_reg_map*)0x40022000) + +/* + * Register bit definitions + */ + +/* Access control register */ + +#define FLASH_ACR_PRFTBS_BIT 5 +#define FLASH_ACR_PRFTBE_BIT 4 +#define FLASH_ACR_HLFCYA_BIT 3 + +#define FLASH_ACR_PRFTBS BIT(FLASH_ACR_PRFTBS_BIT) +#define FLASH_ACR_PRFTBE BIT(FLASH_ACR_PRFTBE_BIT) +#define FLASH_ACR_HLFCYA BIT(FLASH_ACR_HLFCYA_BIT) +#define FLASH_ACR_LATENCY 0x7 + +/* Status register */ + +#define FLASH_SR_EOP_BIT 5 +#define FLASH_SR_WRPRTERR_BIT 4 +#define FLASH_SR_PGERR_BIT 2 +#define FLASH_SR_BSY_BIT 0 + +#define FLASH_SR_EOP BIT(FLASH_SR_EOP_BIT) +#define FLASH_SR_WRPRTERR BIT(FLASH_SR_WRPRTERR_BIT) +#define FLASH_SR_PGERR BIT(FLASH_SR_PGERR_BIT) +#define FLASH_SR_BSY BIT(FLASH_SR_BSY_BIT) + +/* Control register */ + +#define FLASH_CR_EOPIE_BIT 12 +#define FLASH_CR_ERRIE_BIT 10 +#define FLASH_CR_OPTWRE_BIT 9 +#define FLASH_CR_LOCK_BIT 7 +#define FLASH_CR_STRT_BIT 6 +#define FLASH_CR_OPTER_BIT 5 +#define FLASH_CR_OPTPG_BIT 4 +#define FLASH_CR_MER_BIT 2 +#define FLASH_CR_PER_BIT 1 +#define FLASH_CR_PG_BIT 0 + +#define FLASH_CR_EOPIE BIT(FLASH_CR_EOPIE_BIT) +#define FLASH_CR_ERRIE BIT(FLASH_CR_ERRIE_BIT) +#define FLASH_CR_OPTWRE BIT(FLASH_CR_OPTWRE_BIT) +#define FLASH_CR_LOCK BIT(FLASH_CR_LOCK_BIT) +#define FLASH_CR_STRT BIT(FLASH_CR_STRT_BIT) +#define FLASH_CR_OPTER BIT(FLASH_CR_OPTER_BIT) +#define FLASH_CR_OPTPG BIT(FLASH_CR_OPTPG_BIT) +#define FLASH_CR_MER BIT(FLASH_CR_MER_BIT) +#define FLASH_CR_PER BIT(FLASH_CR_PER_BIT) +#define FLASH_CR_PG BIT(FLASH_CR_PG_BIT) + +/* Option byte register */ + +#define FLASH_OBR_nRST_STDBY_BIT 4 +#define FLASH_OBR_nRST_STOP_BIT 3 +#define FLASH_OBR_WDG_SW_BIT 2 +#define FLASH_OBR_RDPRT_BIT 1 +#define FLASH_OBR_OPTERR_BIT 0 + +#define FLASH_OBR_DATA1 (0xFF << 18) +#define FLASH_OBR_DATA0 (0xFF << 10) +#define FLASH_OBR_USER 0x3FF +#define FLASH_OBR_nRST_STDBY BIT(FLASH_OBR_nRST_STDBY_BIT) +#define FLASH_OBR_nRST_STOP BIT(FLASH_OBR_nRST_STOP_BIT) +#define FLASH_OBR_WDG_SW BIT(FLASH_OBR_WDG_SW_BIT) +#define FLASH_OBR_RDPRT BIT(FLASH_OBR_RDPRT_BIT) +#define FLASH_OBR_OPTERR BIT(FLASH_OBR_OPTERR_BIT) + +/* + * Setup routines + */ + +#define FLASH_WAIT_STATE_0 0x0 +#define FLASH_WAIT_STATE_1 0x1 +#define FLASH_WAIT_STATE_2 0x2 + void flash_enable_prefetch(void); void flash_set_latency(uint32 wait_states); diff --git a/libmaple/fsmc.c b/libmaple/fsmc.c index 49526f4..356e1e5 100644 --- a/libmaple/fsmc.c +++ b/libmaple/fsmc.c @@ -26,109 +26,65 @@ * @brief */ -#include "libmaple.h" -#include "rcc.h" -#include "gpio.h" #include "fsmc.h" +#include "gpio.h" -/* These values determined for a particular SRAM chip by following the - * calculations in the ST FSMC application note. */ -#define FSMC_ADDSET 0x0 -#define FSMC_DATAST 0x3 - -/* Sets up the FSMC peripheral to use the SRAM chip on the maple - * native as an external segment of system memory space. This - * implementation is for the IS62WV51216BLL 8mbit chip (55ns - * timing) */ -void fsmc_native_sram_init(void) { - FSMC_Bank *bank; +#ifdef STM32_HIGH_DENSITY - /* First we setup all the GPIO pins. */ +/** + * Configure FSMC GPIOs for use with SRAM. + */ +void fsmc_sram_init_gpios(void) { /* Data lines... */ - gpio_set_mode(GPIOD_BASE, 0, MODE_AF_OUTPUT_PP); - gpio_set_mode(GPIOD_BASE, 1, MODE_AF_OUTPUT_PP); - gpio_set_mode(GPIOD_BASE, 8, MODE_AF_OUTPUT_PP); - gpio_set_mode(GPIOD_BASE, 9, MODE_AF_OUTPUT_PP); - gpio_set_mode(GPIOD_BASE, 10, MODE_AF_OUTPUT_PP); - gpio_set_mode(GPIOD_BASE, 14, MODE_AF_OUTPUT_PP); - gpio_set_mode(GPIOD_BASE, 15, MODE_AF_OUTPUT_PP); - gpio_set_mode(GPIOE_BASE, 7, MODE_AF_OUTPUT_PP); - gpio_set_mode(GPIOE_BASE, 8, MODE_AF_OUTPUT_PP); - gpio_set_mode(GPIOE_BASE, 9, MODE_AF_OUTPUT_PP); - gpio_set_mode(GPIOE_BASE, 10, MODE_AF_OUTPUT_PP); - gpio_set_mode(GPIOE_BASE, 11, MODE_AF_OUTPUT_PP); - gpio_set_mode(GPIOE_BASE, 12, MODE_AF_OUTPUT_PP); - gpio_set_mode(GPIOE_BASE, 13, MODE_AF_OUTPUT_PP); - gpio_set_mode(GPIOE_BASE, 14, MODE_AF_OUTPUT_PP); - gpio_set_mode(GPIOE_BASE, 15, MODE_AF_OUTPUT_PP); + gpio_set_mode(GPIOD, 0, GPIO_AF_OUTPUT_PP); + gpio_set_mode(GPIOD, 1, GPIO_AF_OUTPUT_PP); + gpio_set_mode(GPIOD, 8, GPIO_AF_OUTPUT_PP); + gpio_set_mode(GPIOD, 9, GPIO_AF_OUTPUT_PP); + gpio_set_mode(GPIOD, 10, GPIO_AF_OUTPUT_PP); + gpio_set_mode(GPIOD, 14, GPIO_AF_OUTPUT_PP); + gpio_set_mode(GPIOD, 15, GPIO_AF_OUTPUT_PP); + gpio_set_mode(GPIOE, 7, GPIO_AF_OUTPUT_PP); + gpio_set_mode(GPIOE, 8, GPIO_AF_OUTPUT_PP); + gpio_set_mode(GPIOE, 9, GPIO_AF_OUTPUT_PP); + gpio_set_mode(GPIOE, 10, GPIO_AF_OUTPUT_PP); + gpio_set_mode(GPIOE, 11, GPIO_AF_OUTPUT_PP); + gpio_set_mode(GPIOE, 12, GPIO_AF_OUTPUT_PP); + gpio_set_mode(GPIOE, 13, GPIO_AF_OUTPUT_PP); + gpio_set_mode(GPIOE, 14, GPIO_AF_OUTPUT_PP); + gpio_set_mode(GPIOE, 15, GPIO_AF_OUTPUT_PP); /* Address lines... */ - gpio_set_mode(GPIOD_BASE, 11, MODE_AF_OUTPUT_PP); - gpio_set_mode(GPIOD_BASE, 12, MODE_AF_OUTPUT_PP); - gpio_set_mode(GPIOD_BASE, 13, MODE_AF_OUTPUT_PP); - gpio_set_mode(GPIOF_BASE, 0, MODE_AF_OUTPUT_PP); - gpio_set_mode(GPIOF_BASE, 1, MODE_AF_OUTPUT_PP); - gpio_set_mode(GPIOF_BASE, 2, MODE_AF_OUTPUT_PP); - gpio_set_mode(GPIOF_BASE, 3, MODE_AF_OUTPUT_PP); - gpio_set_mode(GPIOF_BASE, 4, MODE_AF_OUTPUT_PP); - gpio_set_mode(GPIOF_BASE, 5, MODE_AF_OUTPUT_PP); - gpio_set_mode(GPIOF_BASE, 12, MODE_AF_OUTPUT_PP); - gpio_set_mode(GPIOF_BASE, 13, MODE_AF_OUTPUT_PP); - gpio_set_mode(GPIOF_BASE, 14, MODE_AF_OUTPUT_PP); - gpio_set_mode(GPIOF_BASE, 15, MODE_AF_OUTPUT_PP); - gpio_set_mode(GPIOG_BASE, 0, MODE_AF_OUTPUT_PP); - gpio_set_mode(GPIOG_BASE, 1, MODE_AF_OUTPUT_PP); - gpio_set_mode(GPIOG_BASE, 2, MODE_AF_OUTPUT_PP); - gpio_set_mode(GPIOG_BASE, 3, MODE_AF_OUTPUT_PP); - gpio_set_mode(GPIOG_BASE, 4, MODE_AF_OUTPUT_PP); - gpio_set_mode(GPIOG_BASE, 5, MODE_AF_OUTPUT_PP); + gpio_set_mode(GPIOD, 11, GPIO_AF_OUTPUT_PP); + gpio_set_mode(GPIOD, 12, GPIO_AF_OUTPUT_PP); + gpio_set_mode(GPIOD, 13, GPIO_AF_OUTPUT_PP); + gpio_set_mode(GPIOF, 0, GPIO_AF_OUTPUT_PP); + gpio_set_mode(GPIOF, 1, GPIO_AF_OUTPUT_PP); + gpio_set_mode(GPIOF, 2, GPIO_AF_OUTPUT_PP); + gpio_set_mode(GPIOF, 3, GPIO_AF_OUTPUT_PP); + gpio_set_mode(GPIOF, 4, GPIO_AF_OUTPUT_PP); + gpio_set_mode(GPIOF, 5, GPIO_AF_OUTPUT_PP); + gpio_set_mode(GPIOF, 12, GPIO_AF_OUTPUT_PP); + gpio_set_mode(GPIOF, 13, GPIO_AF_OUTPUT_PP); + gpio_set_mode(GPIOF, 14, GPIO_AF_OUTPUT_PP); + gpio_set_mode(GPIOF, 15, GPIO_AF_OUTPUT_PP); + gpio_set_mode(GPIOG, 0, GPIO_AF_OUTPUT_PP); + gpio_set_mode(GPIOG, 1, GPIO_AF_OUTPUT_PP); + gpio_set_mode(GPIOG, 2, GPIO_AF_OUTPUT_PP); + gpio_set_mode(GPIOG, 3, GPIO_AF_OUTPUT_PP); + gpio_set_mode(GPIOG, 4, GPIO_AF_OUTPUT_PP); + gpio_set_mode(GPIOG, 5, GPIO_AF_OUTPUT_PP); /* And control lines... */ - gpio_set_mode(GPIOD_BASE, 4, MODE_AF_OUTPUT_PP); // NOE - gpio_set_mode(GPIOD_BASE, 5, MODE_AF_OUTPUT_PP); // NWE - - gpio_set_mode(GPIOD_BASE, 7, MODE_AF_OUTPUT_PP); // NE1 - gpio_set_mode(GPIOG_BASE, 9, MODE_AF_OUTPUT_PP); // NE2 - gpio_set_mode(GPIOG_BASE, 10, MODE_AF_OUTPUT_PP); // NE3 - gpio_set_mode(GPIOG_BASE, 12, MODE_AF_OUTPUT_PP); // NE4 - - gpio_set_mode(GPIOE_BASE, 0, MODE_AF_OUTPUT_PP); // NBL0 - gpio_set_mode(GPIOE_BASE, 1, MODE_AF_OUTPUT_PP); // NBL1 - - /* Next enable the clock */ - rcc_clk_enable(RCC_FSMC); - - /* Then we configure channel 1 the FSMC SRAM peripheral (all SRAM - * channels are in "Bank 1" of the FSMC) */ - bank = (FSMC_Bank*)(FSMC1_BASE); - - /* Everything else is cleared (BCR1) */ - bank->BCR = 0x0000; - - /* Memory type is SRAM */ - bank->BCR &= ~(FSMC_BCR_MTYP); // '00' - - /* Databus width is 16bits */ - bank->BCR &= ~(FSMC_BCR_MWID); - bank->BCR |= 0x1 << 4; // '01' - - /* Memory is nonmultiplexed */ - bank->BCR &= ~(FSMC_BCR_MUXEN); // '0' - - /* Need write enable to write to the chip */ - bank->BCR |= FSMC_BCR_WREN; - - /* Set ADDSET */ - bank->BTR &= ~(FSMC_BTR_ADDSET); - bank->BTR |= (FSMC_BTR_ADDSET | FSMC_ADDSET); - - /* Set DATAST */ - bank->BTR &= ~(FSMC_BTR_DATAST); - bank->BTR |= (FSMC_BTR_DATAST | (FSMC_DATAST << 8)); + gpio_set_mode(GPIOD, 4, GPIO_AF_OUTPUT_PP); // NOE + gpio_set_mode(GPIOD, 5, GPIO_AF_OUTPUT_PP); // NWE - /* Enable channel 1 */ - bank->BCR |= FSMC_BCR_MBKEN; // '1' + gpio_set_mode(GPIOD, 7, GPIO_AF_OUTPUT_PP); // NE1 + gpio_set_mode(GPIOG, 9, GPIO_AF_OUTPUT_PP); // NE2 + gpio_set_mode(GPIOG, 10, GPIO_AF_OUTPUT_PP); // NE3 + gpio_set_mode(GPIOG, 12, GPIO_AF_OUTPUT_PP); // NE4 - /* (FSMC_BWTR3 not used for this simple configuration.) */ + gpio_set_mode(GPIOE, 0, GPIO_AF_OUTPUT_PP); // NBL0 + gpio_set_mode(GPIOE, 1, GPIO_AF_OUTPUT_PP); // NBL1 } +#endif /* STM32_HIGH_DENSITY */ diff --git a/libmaple/fsmc.h b/libmaple/fsmc.h index e83b529..8b6cac5 100644 --- a/libmaple/fsmc.h +++ b/libmaple/fsmc.h @@ -26,6 +26,8 @@ * See ../notes/fsmc.txt for more info */ +#include "libmaple_types.h" + /** * @file fsmc.h */ @@ -37,54 +39,275 @@ extern "C"{ #endif -// There are 4 FSMC chip-select devices; here are the SRAM-specific registers -// for each - -#define FSMC1_BASE 0xA0000000 -#define FSMC2_BASE 0xA0000008 -#define FSMC3_BASE 0xA0000010 -#define FSMC4_BASE 0xA0000018 - -typedef struct { - volatile uint32 BCR; - volatile uint32 BTR; - //uint32 pad[62]; // double check this? - //__io uint32 BWTR; -} FSMC_Bank; - -// And here are the register bit ranges -#define FSMC_BCR_MBKEN 0b00000000000000000000000000000001 -#define FSMC_BCR_MUXEN 0b00000000000000000000000000000010 -#define FSMC_BCR_MTYP 0b00000000000000000000000000001100 -#define FSMC_BCR_MWID 0b00000000000000000000000000110000 -#define FSMC_BCR_FACCEN 0b00000000000000000000000001000000 -#define FSMC_BCR_BURSTEN 0b00000000000000000000000100000000 -#define FSMC_BCR_WAITPOL 0b00000000000000000000001000000000 -#define FSMC_BCR_WRAPMOD 0b00000000000000000000010000000000 -#define FSMC_BCR_WAITCFG 0b00000000000000000000100000000000 -#define FSMC_BCR_WREN 0b00000000000000000001000000000000 -#define FSMC_BCR_WAITEN 0b00000000000000000010000000000000 -#define FSMC_BCR_EXTMOD 0b00000000000000000100000000000000 -#define FSMC_BCR_CBURSTRW 0b00000000000010000000000000000000 -#define FSMC_BTR_ADDSET 0b00000000000000000000000000001111 -#define FSMC_BTR_ADDHOLD 0b00000000000000000000000011110000 -#define FSMC_BTR_DATAST 0b00000000000000001111111100000000 -#define FSMC_BTR_BUSTURN 0b00000000000011110000000000000000 -#define FSMC_BTR_CLKDIV 0b00000000111100000000000000000000 -#define FSMC_BTR_DATALAT 0b00001111000000000000000000000000 -#define FSMC_BTR_ACCMOD 0b00110000000000000000000000000000 -#define FSMC_BWTR_ADDSET 0b00000000000000000000000000001111 -#define FSMC_BWTR_ADDHLD 0b00000000000000000000000011110000 -#define FSMC_BWTR_DATAST 0b00000000000000001111111100000000 -#define FSMC_BWTR_CLKDIV 0b00000000111100000000000000000000 -#define FSMC_BWTR_DATLAT 0b00001111000000000000000000000000 -#define FSMC_BWTR_ACCMOD 0b00110000000000000000000000000000 - -void fsmc_native_sram_init(void); +#ifdef STM32_HIGH_DENSITY + +/* + * Register maps and devices + */ + +/** FSMC register map type */ +typedef struct fsmc_reg_map { + __io uint32 BCR1; /**< SRAM/NOR-Flash chip-select control register 1 */ + __io uint32 BTR1; /**< SRAM/NOR-Flash chip-select timing register 1 */ + __io uint32 BCR2; /**< SRAM/NOR-Flash chip-select control register 2 */ + __io uint32 BTR2; /**< SRAM/NOR-Flash chip-select timing register 2 */ + __io uint32 BCR3; /**< SRAM/NOR-Flash chip-select control register 3 */ + __io uint32 BTR3; /**< SRAM/NOR-Flash chip-select timing register 3 */ + __io uint32 BCR4; /**< SRAM/NOR-Flash chip-select control register 4 */ + __io uint32 BTR4; /**< SRAM/NOR-Flash chip-select timing register 4 */ + const uint8 RESERVED1[64]; /**< Reserved */ + __io uint32 PCR2; /**< PC Card/NAND Flash control register 2 */ + __io uint32 SR2; /**< FIFO status and interrupt register 2 */ + __io uint32 PMEM2; /**< Common memory space timing register 2 */ + __io uint32 PATT2; /**< Attribute memory space timing register 2 */ + const uint8 RESERVED2[4]; /**< Reserved */ + __io uint32 ECCR2; /**< ECC result register 2 */ + const uint8 RESERVED3[2]; + __io uint32 PCR3; /**< PC Card/NAND Flash control register 3 */ + __io uint32 SR3; /**< FIFO status and interrupt register 3 */ + __io uint32 PMEM3; /**< Common memory space timing register 3 */ + __io uint32 PATT3; /**< Attribute memory space timing register 3 */ + const uint32 RESERVED4; /**< Reserved */ + __io uint32 ECCR3; /**< ECC result register 3 */ + const uint8 RESERVED5[8]; /**< Reserved */ + __io uint32 PCR4; /**< PC Card/NAND Flash control register 4 */ + __io uint32 SR4; /**< FIFO status and interrupt register 4 */ + __io uint32 PMEM4; /**< Common memory space timing register 4 */ + __io uint32 PATT4; /**< Attribute memory space timing register 4 */ + __io uint32 PIO4; /**< I/O space timing register 4 */ + const uint8 RESERVED6[80]; /**< Reserved */ + __io uint32 BWTR1; /**< SRAM/NOR-Flash write timing register 1 */ + const uint32 RESERVED7; /**< Reserved */ + __io uint32 BWTR2; /**< SRAM/NOR-Flash write timing register 2 */ + const uint32 RESERVED8; /**< Reserved */ + __io uint32 BWTR3; /**< SRAM/NOR-Flash write timing register 3 */ + const uint32 RESERVED9; /**< Reserved */ + __io uint32 BWTR4; /**< SRAM/NOR-Flash write timing register 4 */ +} __attribute__((packed)) fsmc_reg_map; + +#define __FSMCB 0xA0000000 + +/** FSMC register map base pointer */ +#define FSMC_BASE ((struct fsmc_reg_map*)__FSMCB) + +/** FSMC NOR/PSRAM register map type */ +typedef struct fsmc_nor_psram_reg_map { + __io uint32 BCR; /**< Chip-select control register */ + __io uint32 BTR; /**< Chip-select timing register */ + const uint8 RESERVED[252]; /**< Reserved */ + __io uint32 BWTR; /**< Write timing register */ +} fsmc_nor_psram_reg_map; + +/** FSMC NOR/PSRAM base pointer 1 */ +#define FSMC_NOR_PSRAM1_BASE ((struct fsmc_nor_psram_reg_map*)__FSMCB) + +/** FSMC NOR/PSRAM base pointer 2 */ +#define FSMC_NOR_PSRAM2_BASE ((struct fsmc_nor_psram_reg_map*)(__FSMCB + 0x8)) + +/** FSMC NOR/PSRAM base pointer 3 */ +#define FSMC_NOR_PSRAM3_BASE ((struct fsmc_nor_psram_reg_map*)(__FSMCB + 0x10)) + +/** FSMC NOR/PSRAM base pointer 4 */ +#define FSMC_NOR_PSRAM4_BASE ((struct fsmc_nor_psram_reg_map*)(__FSMCB + 0x18)) + +/* + * Register bit definitions + */ + +/* NOR/PSRAM chip-select control registers */ + +#define FSMC_BCR_CBURSTRW_BIT 19 +#define FSMC_BCR_ASYNCWAIT_BIT 15 +#define FSMC_BCR_EXTMOD_BIT 14 +#define FSMC_BCR_WAITEN_BIT 13 +#define FSMC_BCR_WREN_BIT 12 +#define FSMC_BCR_WAITCFG_BIT 11 +#define FSMC_BCR_WRAPMOD_BIT 10 +#define FSMC_BCR_WAITPOL_BIT 9 +#define FSMC_BCR_BURSTEN_BIT 8 +#define FSMC_BCR_FACCEN_BIT 6 +#define FSMC_BCR_MUXEN_BIT 1 +#define FSMC_BCR_MBKEN_BIT 0 + +#define FSMC_BCR_CBURSTRW BIT(FSMC_BCR_CBURSTRW_BIT) +#define FSMC_BCR_ASYNCWAIT BIT(FSMC_BCR_ASYNCWAIT_BIT) +#define FSMC_BCR_EXTMOD BIT(FSMC_BCR_EXTMOD_BIT) +#define FSMC_BCR_WAITEN BIT(FSMC_BCR_WAITEN_BIT) +#define FSMC_BCR_WREN BIT(FSMC_BCR_WREN_BIT) +#define FSMC_BCR_WAITCFG BIT(FSMC_BCR_WAITCFG_BIT) +#define FSMC_BCR_WRAPMOD BIT(FSMC_BCR_WRAPMOD_BIT) +#define FSMC_BCR_WAITPOL BIT(FSMC_BCR_WAITPOL_BIT) +#define FSMC_BCR_BURSTEN BIT(FSMC_BCR_BURSTEN_BIT) +#define FSMC_BCR_FACCEN BIT(FSMC_BCR_FACCEN_BIT) +#define FSMC_BCR_MWID (0x3 << 4) +#define FSMC_BCR_MWID_8BITS (0x0 << 4) +#define FSMC_BCR_MWID_16BITS (0x1 << 4) +#define FSMC_BCR_MTYP (0x3 << 2) +#define FSMC_BCR_MTYP_SRAM (0x0 << 2) +#define FSMC_BCR_MTYP_PSRAM (0x1 << 2) +#define FSMC_BCR_MTYP_NOR_FLASH (0x2 << 2) +#define FSMC_BCR_MUXEN BIT(FSMC_BCR_MUXEN_BIT) +#define FSMC_BCR_MBKEN BIT(FSMC_BCR_MBKEN_BIT) + +/* SRAM/NOR-Flash chip-select timing registers */ + +#define FSMC_BTR_ACCMOD (0x3 << 28) +#define FSMC_BTR_ACCMOD_A (0x0 << 28) +#define FSMC_BTR_ACCMOD_B (0x1 << 28) +#define FSMC_BTR_ACCMOD_C (0x2 << 28) +#define FSMC_BTR_ACCMOD_D (0x3 << 28) +#define FSMC_BTR_DATLAT (0xF << 24) +#define FSMC_BTR_CLKDIV (0xF << 20) +#define FSMC_BTR_BUSTURN (0xF << 16) +#define FSMC_BTR_DATAST (0xFF << 8) +#define FSMC_BTR_ADDHLD (0xF << 4) +#define FSMC_BTR_ADDSET 0xF + +/* SRAM/NOR-Flash write timing registers */ + +#define FSMC_BWTR_ACCMOD (0x3 << 28) +#define FSMC_BWTR_ACCMOD_A (0x0 << 28) +#define FSMC_BWTR_ACCMOD_B (0x1 << 28) +#define FSMC_BWTR_ACCMOD_C (0x2 << 28) +#define FSMC_BWTR_ACCMOD_D (0x3 << 28) +#define FSMC_BWTR_DATLAT (0xF << 24) +#define FSMC_BWTR_CLKDIV (0xF << 20) +#define FSMC_BWTR_DATAST (0xFF << 8) +#define FSMC_BWTR_ADDHLD (0xF << 4) +#define FSMC_BWTR_ADDSET 0xF + +/* NAND Flash/PC Card controller registers */ + +#define FSMC_PCR_ECCEN_BIT 6 +#define FSMC_PCR_PTYP_BIT 3 +#define FSMC_PCR_PBKEN_BIT 2 +#define FSMC_PCR_PWAITEN_BIT 1 + +#define FSMC_PCR_ECCPS (0x7 << 17) +#define FSMC_PCR_ECCPS_256B (0x0 << 17) +#define FSMC_PCR_ECCPS_512B (0x1 << 17) +#define FSMC_PCR_ECCPS_1024B (0x2 << 17) +#define FSMC_PCR_ECCPS_2048B (0x3 << 17) +#define FSMC_PCR_ECCPS_4096B (0x4 << 17) +#define FSMC_PCR_ECCPS_8192B (0x5 << 17) +#define FSMC_PCR_TAR (0xF << 13) +#define FSMC_PCR_TCLR (0xF << 9) +#define FSMC_PCR_ECCEN BIT(FSMC_PCR_ECCEN_BIT) +#define FSMC_PCR_PWID (0x3 << 4) +#define FSMC_PCR_PWID_8BITS (0x0 << 4) +#define FSMC_PCR_PWID_16BITS (0x1 << 4) +#define FSMC_PCR_PTYP BIT(FSMC_PCR_PTYP_BIT) +#define FSMC_PCR_PTYP_PC_CF_PCMCIA (0x0 << FSMC_PCR_PTYP_BIT) +#define FSMC_PCR_PTYP_NAND (0x1 << FSMC_PCR_PTYP_BIT) +#define FSMC_PCR_PBKEN BIT(FSMC_PCR_PBKEN_BIT) +#define FSMC_PCR_PWAITEN BIT(FSMC_PCR_PWAITEN_BIT) + +/* FIFO status and interrupt registers */ + +#define FSMC_SR_FEMPT_BIT 6 +#define FSMC_SR_IFEN_BIT 5 +#define FSMC_SR_ILEN_BIT 4 +#define FSMC_SR_IREN_BIT 3 +#define FSMC_SR_IFS_BIT 2 +#define FSMC_SR_ILS_BIT 1 +#define FSMC_SR_IRS_BIT 0 + +#define FSMC_SR_FEMPT BIT(FSMC_SR_FEMPT_BIT) +#define FSMC_SR_IFEN BIT(FSMC_SR_IFEN_BIT) +#define FSMC_SR_ILEN BIT(FSMC_SR_ILEN_BIT) +#define FSMC_SR_IREN BIT(FSMC_SR_IREN_BIT) +#define FSMC_SR_IFS BIT(FSMC_SR_IFS_BIT) +#define FSMC_SR_ILS BIT(FSMC_SR_ILS_BIT) +#define FSMC_SR_IRS BIT(FSMC_SR_IRS_BIT) + +/* Common memory space timing registers */ + +#define FSMC_PMEM_MEMHIZ (0xFF << 24) +#define FSMC_PMEM_MEMHOLD (0xFF << 16) +#define FSMC_PMEM_MEMWAIT (0xFF << 8) +#define FSMC_PMEM_MEMSET 0xFF + +/* Attribute memory space timing registers */ + +#define FSMC_PATT_ATTHIZ (0xFF << 24) +#define FSMC_PATT_ATTHOLD (0xFF << 16) +#define FSMC_PATT_ATTWAIT (0xFF << 8) +#define FSMC_PATT_ATTSET 0xFF + +/* I/O space timing register 4 */ + +#define FSMC_PIO_IOHIZ (0xFF << 24) +#define FSMC_PIO_IOHOLD (0xFF << 16) +#define FSMC_PIO_IOWAIT (0xFF << 8) +#define FSMC_PIO_IOSET 0xFF + +/* + * Memory bank boundary addresses + */ + +/** Pointer to base address of FSMC memory bank 1 (split into 4 + * regions, each supporting 1 NOR Flash, SRAM, or PSRAM chip) */ +#define FSMC_BANK1 ((void*)0x60000000) + +/** Pointer to base address of FSMC memory bank 1, region 1 (for NOR/PSRAM) */ +#define FSMC_NOR_PSRAM_REGION1 FSMC_BANK1 + +/** Pointer to base address of FSMC memory bank 1, region 2 (for NOR/PSRAM) */ +#define FSMC_NOR_PSRAM_REGION2 ((void*)0x64000000) + +/** Pointer to base address of FSMC memory bank 1, region 3 (for NOR/PSRAM) */ +#define FSMC_NOR_PSRAM_REGION3 ((void*)0x68000000) + +/** Pointer to base address of FSMC memory bank 1, region 4 (for NOR/PSRAM) */ +#define FSMC_NOR_PSRAM_REGION4 ((void*)0x6C000000) + +/** Pointer to base address of FSMC memory bank 2 (for NAND Flash) */ +#define FSMC_BANK2 ((void*)0x70000000) + +/** Pointer to base address of FSMC memory bank 3 (for NAND Flash) */ +#define FSMC_BANK3 ((void*)0x80000000) + +/** Pointer to base address of FSMC memory bank 4 (for PC card devices */ +#define FSMC_BANK4 ((void*)0x90000000) + +/* + * SRAM/NOR Flash routines + */ + +void fsmc_sram_init_gpios(void); + +/** + * Set the DATAST bits in the given NOR/PSRAM register map's + * chip-select timing register (FSMC_BTR). + * + * @param regs NOR Flash/PSRAM register map whose chip-select timing + * register to set. + * @param datast Value to use for DATAST bits. + */ +static inline void fsmc_nor_psram_set_datast(fsmc_nor_psram_reg_map *regs, + uint8 datast) { + regs->BTR &= ~FSMC_BTR_DATAST; + regs->BTR |= datast << 8; +} + +/** + * Set the ADDHLD bits in the given NOR/PSRAM register map's chip + * select timing register (FSMC_BTRx). + * + * @param regs NOR Flash/PSRAM register map whose chip-select timing + * register to set. + * @param addset Value to use for ADDSET bits. + */ +static inline void fsmc_nor_psram_set_addset(fsmc_nor_psram_reg_map *regs, + uint8 addset) { + regs->BTR &= ~FSMC_BTR_ADDSET; + regs->BTR |= addset & 0xF; +} + +#endif /* STM32_HIGH_DENSITY */ #ifdef __cplusplus -} // extern "C" +} /* extern "C" */ #endif - #endif diff --git a/libmaple/gpio.c b/libmaple/gpio.c index 71e5230..5484e21 100644 --- a/libmaple/gpio.c +++ b/libmaple/gpio.c @@ -26,44 +26,151 @@ * @brief GPIO initialization routine */ -#include "libmaple.h" -#include "rcc.h" #include "gpio.h" +#include "rcc.h" + +/* + * GPIO devices + */ + +gpio_dev gpioa = { + .regs = GPIOA_BASE, + .clk_id = RCC_GPIOA, + .exti_port = AFIO_EXTI_PA, +}; +gpio_dev* const GPIOA = &gpioa; + +gpio_dev gpiob = { + .regs = GPIOB_BASE, + .clk_id = RCC_GPIOB, + .exti_port = AFIO_EXTI_PB, +}; +gpio_dev* const GPIOB = &gpiob; + +gpio_dev gpioc = { + .regs = GPIOC_BASE, + .clk_id = RCC_GPIOC, + .exti_port = AFIO_EXTI_PC, +}; +gpio_dev* const GPIOC = &gpioc; + +gpio_dev gpiod = { + .regs = GPIOD_BASE, + .clk_id = RCC_GPIOD, + .exti_port = AFIO_EXTI_PD, +}; +gpio_dev* const GPIOD = &gpiod; + +#ifdef STM32_HIGH_DENSITY +gpio_dev gpioe = { + .regs = GPIOE_BASE, + .clk_id = RCC_GPIOE, + .exti_port = AFIO_EXTI_PE, +}; +gpio_dev* const GPIOE = &gpioe; + +gpio_dev gpiof = { + .regs = GPIOF_BASE, + .clk_id = RCC_GPIOF, + .exti_port = AFIO_EXTI_PF, +}; +gpio_dev* const GPIOF = &gpiof; -void gpio_init(void) { - rcc_clk_enable(RCC_GPIOA); - rcc_clk_enable(RCC_GPIOB); - rcc_clk_enable(RCC_GPIOC); -#if NR_GPIO_PORTS >= 4 /* Maple, but not Maple Mini */ - rcc_clk_enable(RCC_GPIOD); -#elif NR_GPIO_PORTS >= 7 /* Maple Native (high density only) */ - rcc_clk_enable(RCC_GPIOE); - rcc_clk_enable(RCC_GPIOF); - rcc_clk_enable(RCC_GPIOG); +gpio_dev gpiog = { + .regs = GPIOG_BASE, + .clk_id = RCC_GPIOG, + .exti_port = AFIO_EXTI_PG, +}; +gpio_dev* const GPIOG = &gpiog; #endif - rcc_clk_enable(RCC_AFIO); + +/* + * GPIO convenience routines + */ + +/** + * Initialize a GPIO device. + * + * Enables the clock for and resets the given device. + * + * @param dev GPIO device to initialize. + */ +void gpio_init(gpio_dev *dev) { + rcc_clk_enable(dev->clk_id); + rcc_reset_dev(dev->clk_id); } -void gpio_set_mode(GPIO_Port* port, uint8 gpio_pin, GPIOPinMode mode) { - uint32 tmp; - uint32 shift = POS(gpio_pin % 8); - GPIOReg CR; +/** + * Initialize and reset all available GPIO devices. + */ +void gpio_init_all(void) { + gpio_init(GPIOA); + gpio_init(GPIOB); + gpio_init(GPIOC); + gpio_init(GPIOD); +#ifdef STM32_HIGH_DENSITY + gpio_init(GPIOE); + gpio_init(GPIOF); + gpio_init(GPIOG); +#endif +} - ASSERT(port); - ASSERT(gpio_pin < 16); +/** + * Set the mode of a GPIO pin. + * + * @param dev GPIO device. + * @param pin Pin on the device whose mode to set, 0--15. + * @param mode General purpose or alternate function mode to set the pin to. + * @see gpio_pin_mode + */ +void gpio_set_mode(gpio_dev *dev, uint8 pin, gpio_pin_mode mode) { + gpio_reg_map *regs = dev->regs; + __io uint32 *cr = ®s->CRL + (pin >> 3); + uint32 shift = (pin & 0x7) * 4; + uint32 tmp = *cr; + + tmp &= ~(0xF << shift); + tmp |= (mode == GPIO_INPUT_PU ? GPIO_INPUT_PD : mode) << shift; + *cr = tmp; - if (mode == GPIO_MODE_INPUT_PU) { - port->ODR |= BIT(gpio_pin); - mode = CNF_INPUT_PD; - } else if (mode == GPIO_MODE_INPUT_PD) { - port->ODR &= ~BIT(gpio_pin); + if (mode == GPIO_INPUT_PD) { + regs->ODR &= ~BIT(pin); + } else if (mode == GPIO_INPUT_PU) { + regs->ODR |= BIT(pin); } +} + +/* + * AFIO + */ - CR = (gpio_pin < 8) ? &(port->CRL) : &(port->CRH); +/** + * Initialize the AFIO clock, and reset the AFIO registers. + */ +void afio_init(void) { + rcc_clk_enable(RCC_AFIO); + rcc_reset_dev(RCC_AFIO); +} - tmp = *CR; - tmp &= POS_MASK(shift); - tmp |= mode << shift; +#define AFIO_EXTI_SEL_MASK 0xF + +/** + * Select a source input for an external interrupt. + * + * @param exti External interrupt. One of: AFIO_EXTI_0, + * AFIO_EXTI_1, ..., AFIO_EXTI_15. + * @param gpio_port Port which contains pin to use as source input. + * One of: AFIO_EXTI_PA, AFIO_EXTI_PB, AFIO_EXTI_PC, + * AFIO_EXTI_PD, and, on high density devices, + * AFIO_EXTI_PE, AFIO_EXTI_PF, AFIO_EXTI_PG. + * @see exti_port + */ +void afio_exti_select(afio_exti_num exti, afio_exti_port gpio_port) { + __io uint32 *exti_cr = &AFIO_BASE->EXTICR1 + exti / 4; + uint32 shift = 4 * (exti % 4); + uint32 cr = *exti_cr; - *CR = tmp; + cr &= ~(AFIO_EXTI_SEL_MASK << shift); + cr |= gpio_port << shift; + *exti_cr = cr; } diff --git a/libmaple/gpio.h b/libmaple/gpio.h index 53f77c4..353f965 100644 --- a/libmaple/gpio.h +++ b/libmaple/gpio.h @@ -3,126 +3,357 @@ * * Copyright (c) 2010 Perry Hung. * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - *****************************************************************************/ + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. +*****************************************************************************/ /** * @file gpio.h * - * @brief GPIO prototypes, defines, and inlined access functions + * @brief General purpose I/0 (GPIO) and Alternate Function I/O + * (AFIO) prototypes, defines, and inlined access functions. */ -#ifndef _GPIO_H -#define _GPIO_H - -/* Each of the GPIO port bits can be in the following modes (STM32 138/995): - * Input floating - * Input pull-up - * Input pull-down - * Analog Input - * Output open-drain - * Output push-pull - * Alternate function push-pull - * Alternate function open-drain - * - * - After reset, the alternate functions are not active and IO prts - * are set to Input Floating mode, EXCEPT for the Serial Wire and JTAG - * ports, which are in alternate function mode by default. */ - -#define AFIO_MAPR ((volatile uint32*)0x40010004) - -#define GPIOA_BASE ((GPIO_Port*)0x40010800) -#define GPIOB_BASE ((GPIO_Port*)0x40010C00) -#define GPIOC_BASE ((GPIO_Port*)0x40011000) -#define GPIOD_BASE ((GPIO_Port*)0x40011400) -#define GPIOE_BASE ((GPIO_Port*)0x40011800) // High-density devices only -#define GPIOF_BASE ((GPIO_Port*)0x40011C00) // High-density devices only -#define GPIOG_BASE ((GPIO_Port*)0x40012000) // High-density devices only - -#define GPIO_SPEED_50MHZ (0x3) - -#define MODE_OUTPUT_PP ((0x00 << 2) | GPIO_SPEED_50MHZ) -#define MODE_OUTPUT_OD ((0x01 << 2) | GPIO_SPEED_50MHZ) -#define MODE_AF_OUTPUT_PP ((0x02 << 2) | GPIO_SPEED_50MHZ) -#define MODE_AF_OUTPUT_OD ((0x03 << 2) | GPIO_SPEED_50MHZ) - -#define CNF_INPUT_ANALOG (0x00 << 2) -#define CNF_INPUT_FLOATING (0x01 << 2) -#define CNF_INPUT_PD (0x02 << 2) -#define CNF_INPUT_PU (0x02 << 2) - -typedef enum GPIOPinMode { - GPIO_MODE_OUTPUT_PP = MODE_OUTPUT_PP, - GPIO_MODE_OUTPUT_OD = MODE_OUTPUT_OD, - GPIO_MODE_AF_OUTPUT_PP = MODE_AF_OUTPUT_PP, - GPIO_MODE_AF_OUTPUT_OD = MODE_AF_OUTPUT_OD, - GPIO_MODE_INPUT_ANALOG = CNF_INPUT_ANALOG, - GPIO_MODE_INPUT_FLOATING = CNF_INPUT_FLOATING, - GPIO_MODE_INPUT_PD = CNF_INPUT_PD, - GPIO_MODE_INPUT_PU, -} GPIOPinMode; - -typedef struct { - volatile uint32 CRL; // Port configuration register low - volatile uint32 CRH; // Port configuration register high - volatile uint32 IDR; // Port input data register - volatile uint32 ODR; // Port output data register - volatile uint32 BSRR; // Port bit set/reset register - volatile uint32 BRR; // Port bit reset register - volatile uint32 LCKR; // Port configuration lock register -} GPIO_Port; - -typedef volatile uint32* GPIOReg; - -#define POS_MASK(shift) (~(0xF << shift)) -#define POS(val) (val << 2) +#ifndef _GPIO_H_ +#define _GPIO_H_ + +#include "libmaple.h" +#include "rcc.h" #ifdef __cplusplus extern "C"{ #endif -void gpio_init(void); -void gpio_set_mode(GPIO_Port* port, uint8 gpio_pin, GPIOPinMode mode); +/* + * GPIO register maps and devices + */ + +/** GPIO register map type */ +typedef struct gpio_reg_map { + __io uint32 CRL; /**< Port configuration register low */ + __io uint32 CRH; /**< Port configuration register high */ + __io uint32 IDR; /**< Port input data register */ + __io uint32 ODR; /**< Port output data register */ + __io uint32 BSRR; /**< Port bit set/reset register */ + __io uint32 BRR; /**< Port bit reset register */ + __io uint32 LCKR; /**< Port configuration lock register */ +} gpio_reg_map; + +/** + * External interrupt line port selector. Used to determine which + * GPIO port to map an external interrupt line onto. */ +/* (See AFIO sections, below) */ +typedef enum { + AFIO_EXTI_PA, /**< Use PAx pin. */ + AFIO_EXTI_PB, /**< Use PBx pin. */ + AFIO_EXTI_PC, /**< Use PCx pin. */ + AFIO_EXTI_PD, /**< Use PDx pin. */ +#ifdef STM32_HIGH_DENSITY + AFIO_EXTI_PE, /**< Use PEx pin. */ + AFIO_EXTI_PF, /**< Use PFx pin. */ + AFIO_EXTI_PG, /**< Use PGx pin. */ +#endif +} afio_exti_port; + +/** GPIO device type */ +typedef struct gpio_dev { + gpio_reg_map *regs; /**< Register map */ + rcc_clk_id clk_id; /**< RCC clock information */ + afio_exti_port exti_port; /**< AFIO external interrupt port value */ +} gpio_dev; + +extern gpio_dev gpioa; +extern gpio_dev* const GPIOA; +extern gpio_dev gpiob; +extern gpio_dev* const GPIOB; +extern gpio_dev gpioc; +extern gpio_dev* const GPIOC; +extern gpio_dev gpiod; +extern gpio_dev* const GPIOD; +#ifdef STM32_HIGH_DENSITY +extern gpio_dev gpioe; +extern gpio_dev* const GPIOE; +extern gpio_dev gpiof; +extern gpio_dev* const GPIOF; +extern gpio_dev gpiog; +extern gpio_dev* const GPIOG; +#endif + +/** GPIO port A register map base pointer */ +#define GPIOA_BASE ((gpio_reg_map*)0x40010800) +/** GPIO port B register map base pointer */ +#define GPIOB_BASE ((gpio_reg_map*)0x40010C00) +/** GPIO port C register map base pointer */ +#define GPIOC_BASE ((gpio_reg_map*)0x40011000) +/** GPIO port D register map base pointer */ +#define GPIOD_BASE ((gpio_reg_map*)0x40011400) +#ifdef STM32_HIGH_DENSITY +/** GPIO port E register map base pointer */ +#define GPIOE_BASE ((gpio_reg_map*)0x40011800) +/** GPIO port F register map base pointer */ +#define GPIOF_BASE ((gpio_reg_map*)0x40011C00) +/** GPIO port G register map base pointer */ +#define GPIOG_BASE ((gpio_reg_map*)0x40012000) +#endif + +/* + * GPIO register bit definitions + */ + +/* Control registers, low and high */ +#define GPIO_CR_CNF_INPUT_ANALOG (0x0 << 2) +#define GPIO_CR_CNF_INPUT_FLOATING (0x1 << 2) +#define GPIO_CR_CNF_INPUT_PU_PD (0x2 << 2) +#define GPIO_CR_CNF_OUTPUT_PP (0x0 << 2) +#define GPIO_CR_CNF_OUTPUT_OD (0x1 << 2) +#define GPIO_CR_CNF_AF_OUTPUT_PP (0x2 << 2) +#define GPIO_CR_CNF_AF_OUTPUT_OD (0x3 << 2) +#define GPIO_CR_MODE_INPUT 0x0 +#define GPIO_CR_MODE_OUTPUT_10MHZ 0x1 +#define GPIO_CR_MODE_OUTPUT_2MHZ 0x2 +#define GPIO_CR_MODE_OUTPUT_50MHZ 0x3 -static inline void gpio_write_bit(GPIO_Port *port, uint8 gpio_pin, uint8 val) { - if (val){ - port->BSRR = BIT(gpio_pin); +/** GPIO Pin modes. These only allow for 50MHZ max output speeds; if + * you want slower, use direct register access. */ +typedef enum gpio_pin_mode { + GPIO_OUTPUT_PP = (GPIO_CR_CNF_OUTPUT_PP | + GPIO_CR_MODE_OUTPUT_50MHZ), /**< Output push-pull. */ + GPIO_OUTPUT_OD = (GPIO_CR_CNF_OUTPUT_OD | + GPIO_CR_MODE_OUTPUT_50MHZ), /**< Output open-drain. */ + GPIO_AF_OUTPUT_PP = (GPIO_CR_CNF_AF_OUTPUT_PP | + GPIO_CR_MODE_OUTPUT_50MHZ), /**< Alternate function + output push-pull. */ + GPIO_AF_OUTPUT_OD = (GPIO_CR_CNF_AF_OUTPUT_OD | + GPIO_CR_MODE_OUTPUT_50MHZ), /**< Alternate function + output open drain. */ + GPIO_INPUT_ANALOG = (GPIO_CR_CNF_INPUT_ANALOG | + GPIO_CR_MODE_INPUT), /**< Analog input. */ + GPIO_INPUT_FLOATING = (GPIO_CR_CNF_INPUT_FLOATING | + GPIO_CR_MODE_INPUT), /**< Input floating. */ + GPIO_INPUT_PD = (GPIO_CR_CNF_INPUT_PU_PD | + GPIO_CR_MODE_INPUT), /**< Input pull-down. */ + GPIO_INPUT_PU /**< Input pull-up. */ + /* GPIO_INPUT_PU treated as a special case, for ODR twiddling */ +} gpio_pin_mode; + +/* + * GPIO Convenience routines + */ + +void gpio_init(gpio_dev *dev); +void gpio_init_all(void); +void gpio_set_mode(gpio_dev *dev, uint8 pin, gpio_pin_mode mode); + +/** + * @brief Get a GPIO port's corresponding afio_exti_port. + * @param dev GPIO device whose afio_exti_port to return. + */ +static inline afio_exti_port gpio_exti_port(gpio_dev *dev) { + return dev->exti_port; +} + +/** + * Set or reset a GPIO pin. + * + * Pin must have previously been configured to output mode. + * + * @param dev GPIO device whose pin to set. + * @param pin Pin on to set or reset + * @param val If true, set the pin. If false, reset the pin. + */ +static inline void gpio_write_bit(gpio_dev *dev, uint8 pin, uint8 val) { + if (val) { + dev->regs->BSRR = BIT(pin); } else { - port->BRR = BIT(gpio_pin); + dev->regs->BRR = BIT(pin); } } -static inline uint32 gpio_read_bit(GPIO_Port *port, uint8 gpio_pin) { - return (port->IDR & BIT(gpio_pin) ? 1 : 0); +/** + * Determine whether or not a GPIO pin is set. + * + * Pin must have previously been configured to input mode. + * + * @param dev GPIO device whose pin to test. + * @param pin Pin on dev to test. + * @return True if the pin is set, false otherwise. + */ +static inline uint32 gpio_read_bit(gpio_dev *dev, uint8 pin) { + return dev->regs->IDR & BIT(pin); +} + +/** + * Toggle a pin configured as output push-pull. + * @param dev GPIO device. + * @param pin Pin on dev to toggle. + */ +static inline void gpio_toggle_bit(gpio_dev *dev, uint8 pin) { + dev->regs->ODR = dev->regs->ODR ^ BIT(pin); } -/* For pins configured as output push-pull, reading the ODR returns - * the last value written in push-pull mode. +/* + * AFIO register map */ -static inline void gpio_toggle_pin(GPIO_Port *port, uint8 gpio_pin) { - port->ODR = port->ODR ^ BIT(gpio_pin); + +/** AFIO register map */ +typedef struct afio_reg_map { + __io uint32 EVCR; /**< Event control register. */ + __io uint32 MAPR; /**< AF remap and debug I/O configuration + register. */ + __io uint32 EXTICR1; /**< External interrupt configuration + register 1. */ + __io uint32 EXTICR2; /**< External interrupt configuration + register 2. */ + __io uint32 EXTICR3; /**< External interrupt configuration + register 3. */ + __io uint32 EXTICR4; /**< External interrupt configuration + register 4. */ + __io uint32 MAPR2; /**< AF remap and debug I/O configuration + register 2. */ +} afio_reg_map; + +/** AFIO register map base pointer. */ +#define AFIO_BASE ((afio_reg_map *)0x40010000) + +/* + * AFIO register bit definitions + */ + +/* Event control register */ +#define AFIO_EVCR_EVOE (0x1 << 7) +#define AFIO_EVCR_PORT_PA (0x0 << 4) +#define AFIO_EVCR_PORT_PB (0x1 << 4) +#define AFIO_EVCR_PORT_PC (0x2 << 4) +#define AFIO_EVCR_PORT_PD (0x3 << 4) +#define AFIO_EVCR_PORT_PE (0x4 << 4) +#define AFIO_EVCR_PIN_0 0x0 +#define AFIO_EVCR_PIN_1 0x1 +#define AFIO_EVCR_PIN_2 0x2 +#define AFIO_EVCR_PIN_3 0x3 +#define AFIO_EVCR_PIN_4 0x4 +#define AFIO_EVCR_PIN_5 0x5 +#define AFIO_EVCR_PIN_6 0x6 +#define AFIO_EVCR_PIN_7 0x7 +#define AFIO_EVCR_PIN_8 0x8 +#define AFIO_EVCR_PIN_9 0x9 +#define AFIO_EVCR_PIN_10 0xA +#define AFIO_EVCR_PIN_11 0xB +#define AFIO_EVCR_PIN_12 0xC +#define AFIO_EVCR_PIN_13 0xD +#define AFIO_EVCR_PIN_14 0xE +#define AFIO_EVCR_PIN_15 0xF + +/* AF remap and debug I/O configuration register */ +#define AFIO_MAPR_SWJ_CFG (0x7 << 24) +#define AFIO_MAPR_SWJ_CFG_FULL_SWJ (0x0 << 24) +#define AFIO_MAPR_SWJ_CFG_FULL_SWJ_NO_NJRST (0x1 << 24) +#define AFIO_MAPR_SWJ_CFG_NO_JTAG_SW (0x2 << 24) +#define AFIO_MAPR_SWJ_CFG_NO_JTAG_NO_SW (0x4 << 24) +#define AFIO_MAPR_ADC2_ETRGREG_REMAP BIT(20) +#define AFIO_MAPR_ADC2_ETRGINJ_REMAP BIT(19) +#define AFIO_MAPR_ADC1_ETRGREG_REMAP BIT(18) +#define AFIO_MAPR_ADC1_ETRGINJ_REMAP BIT(17) +#define AFIO_MAPR_TIM5CH4_IREMAP BIT(16) +#define AFIO_MAPR_PD01_REMAP BIT(15) +#define AFIO_MAPR_CAN_REMAP (0x3 << 13) +#define AFIO_MAPR_TIM4_REMAP BIT(12) +#define AFIO_MAPR_TIM3_REMAP (0x3 << 10) +#define AFIO_MAPR_TIM2_REMAP (0x3 << 8) +#define AFIO_MAPR_TIM1_REMAP (0x3 << 6) +#define AFIO_MAPR_USART3_REMAP (0x3 << 4) +#define AFIO_MAPR_USART2_REMAP BIT(3) +#define AFIO_MAPR_USART1_REMAP BIT(2) +#define AFIO_MAPR_I2C1_REMAP BIT(1) +#define AFIO_MAPR_SPI1_REMAP BIT(0) + +/* External interrupt configuration registers */ + +/** + * External interrupt line numbers. + */ +typedef enum { + AFIO_EXTI_0, /**< External interrupt line 0. */ + AFIO_EXTI_1, /**< External interrupt line 1. */ + AFIO_EXTI_2, /**< External interrupt line 2. */ + AFIO_EXTI_3, /**< External interrupt line 3. */ + AFIO_EXTI_4, /**< External interrupt line 4. */ + AFIO_EXTI_5, /**< External interrupt line 5. */ + AFIO_EXTI_6, /**< External interrupt line 6. */ + AFIO_EXTI_7, /**< External interrupt line 7. */ + AFIO_EXTI_8, /**< External interrupt line 8. */ + AFIO_EXTI_9, /**< External interrupt line 9. */ + AFIO_EXTI_10, /**< External interrupt line 10. */ + AFIO_EXTI_11, /**< External interrupt line 11. */ + AFIO_EXTI_12, /**< External interrupt line 12. */ + AFIO_EXTI_13, /**< External interrupt line 13. */ + AFIO_EXTI_14, /**< External interrupt line 14. */ + AFIO_EXTI_15, /**< External interrupt line 15. */ +} afio_exti_num; + +/* AF remap and debug I/O configuration register 2 */ +#define AFIO_MAPR2_FSMC_NADV BIT(10) +#define AFIO_MAPR2_TIM14_REMAP BIT(9) +#define AFIO_MAPR2_TIM13_REMAP BIT(8) +#define AFIO_MAPR2_TIM11_REMAP BIT(7) +#define AFIO_MAPR2_TIM10_REMAP BIT(6) +#define AFIO_MAPR2_TIM9_REMAP BIT(5) + +/* + * AFIO convenience routines + */ + +void afio_init(void); +void afio_exti_select(afio_exti_num exti, afio_exti_port gpio_port); + +/** + * @brief Debug port configuration + * + * Used to configure the behavior of JTAG and Serial Wire (SW) debug + * ports and their associated GPIO pins. + */ +typedef enum afio_debug_cfg { + AFIO_DEBUG_FULL_SWJ = AFIO_MAPR_SWJ_CFG_FULL_SWJ, /**< + Full Serial Wire and JTAG debug */ + AFIO_DEBUG_FULL_SWJ_NO_NJRST = AFIO_MAPR_SWJ_CFG_FULL_SWJ_NO_NJRST, /**< + Full Serial Wire and JTAG, but no NJTRST. */ + AFIO_DEBUG_SW_ONLY = AFIO_MAPR_SWJ_CFG_NO_JTAG_SW, /**< + Serial Wire debug only (JTAG-DP disabled, + SW-DP enabled) */ + AFIO_DEBUG_NONE = AFIO_MAPR_SWJ_CFG_NO_JTAG_NO_SW /**< + No debug; all JTAG and SW pins are free + for use as GPIOs. */ +} afio_debug_cfg; + +/** + * @brief Enable or disable the JTAG and SW debug ports. + * @param config Desired debug port configuration + * @see afio_debug_cfg + */ +static inline void afio_cfg_debug_ports(afio_debug_cfg config) { + __io uint32 *mapr = &AFIO_BASE->MAPR; + *mapr = (*mapr & ~AFIO_MAPR_SWJ_CFG) | config; } #ifdef __cplusplus -} // extern "C" +} #endif - #endif diff --git a/libmaple/i2c.c b/libmaple/i2c.c new file mode 100644 index 0000000..f4cb522 --- /dev/null +++ b/libmaple/i2c.c @@ -0,0 +1,446 @@ +/****************************************************************************** + * The MIT License + * + * Copyright (c) 2010 Perry Hung. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + *****************************************************************************/ + +/** + * @file i2c.c + * @brief Inter-Integrated Circuit (I2C) support. + */ + +#include "libmaple.h" +#include "rcc.h" +#include "nvic.h" +#include "gpio.h" +#include "nvic.h" +#include "i2c.h" +#include "string.h" + +static inline int32 wait_for_state_change(i2c_dev *dev, i2c_state state); + +static i2c_dev i2c_dev1 = { + .regs = I2C1_BASE, + .gpio_port = &gpiob, + .sda_pin = 7, + .scl_pin = 6, + .clk_line = RCC_I2C1, + .ev_nvic_line = NVIC_I2C1_EV, + .er_nvic_line = NVIC_I2C1_ER, + .state = I2C_STATE_IDLE +}; + +i2c_dev* const I2C1 = &i2c_dev1; + +static i2c_dev i2c_dev2 = { + .regs = I2C2_BASE, + .gpio_port = &gpiob, + .sda_pin = 11, + .scl_pin = 10, + .clk_line = RCC_I2C2, + .ev_nvic_line = NVIC_I2C2_EV, + .er_nvic_line = NVIC_I2C2_ER, + .state = I2C_STATE_IDLE +}; + +i2c_dev* const I2C2 = &i2c_dev2; + +struct crumb { + uint32 event; + uint32 sr1; + uint32 sr2; +}; + +#define NR_CRUMBS 128 +static struct crumb crumbs[NR_CRUMBS]; +static uint32 cur_crumb = 0; + +static inline void leave_big_crumb(uint32 event, uint32 sr1, uint32 sr2) { + if (cur_crumb < NR_CRUMBS) { + struct crumb *crumb = &crumbs[cur_crumb++]; + crumb->event = event; + crumb->sr1 = sr1; + crumb->sr2 = sr2; + } +} + +enum { + IRQ_ENTRY = 1, + TXE_ONLY = 2, + TXE_BTF = 3, + STOP_SENT = 4, + TEST = 5, + RX_ADDR_START = 6, + RX_ADDR_STOP = 7, + RXNE_ONLY = 8, + RXNE_SENDING = 9, + RXNE_START_SENT = 10, + RXNE_STOP_SENT = 11, + RXNE_DONE = 12, + ERROR_ENTRY = 13, +}; + +/** + * @brief IRQ handler for i2c master. Handles transmission/reception. + * @param dev i2c device + */ +static void i2c_irq_handler(i2c_dev *dev) { + i2c_msg *msg = dev->msg; + + uint8 read = msg->flags & I2C_MSG_READ; + + uint32 sr1 = dev->regs->SR1; + uint32 sr2 = dev->regs->SR2; + leave_big_crumb(IRQ_ENTRY, sr1, sr2); + + /* + * EV5: Start condition sent + */ + if (sr1 & I2C_SR1_SB) { + msg->xferred = 0; + i2c_enable_irq(dev, I2C_IRQ_BUFFER); + + /* + * Master receiver + */ + if (read) { + i2c_enable_ack(dev); + } + + i2c_send_slave_addr(dev, msg->addr, read); + sr1 = sr2 = 0; + } + + /* + * EV6: Slave address sent + */ + if (sr1 & I2C_SR1_ADDR) { + /* + * Special case event EV6_1 for master receiver. + * Generate NACK and restart/stop condition after ADDR + * is cleared. + */ + if (read) { + if (msg->length == 1) { + i2c_disable_ack(dev); + if (dev->msgs_left > 1) { + i2c_start_condition(dev); + leave_big_crumb(RX_ADDR_START, 0, 0); + } else { + i2c_stop_condition(dev); + leave_big_crumb(RX_ADDR_STOP, 0, 0); + } + } + } else { + /* + * Master transmitter: write first byte to fill shift + * register. We should get another TXE interrupt + * immediately to fill DR again. + */ + if (msg->length != 1) + i2c_write(dev, msg->data[msg->xferred++]); + } + sr1 = sr2 = 0; + } + + /* + * EV8: Master transmitter + * Transmit buffer empty, but we haven't finished transmitting the last + * byte written. + */ + if ((sr1 & I2C_SR1_TXE) && !(sr1 & I2C_SR1_BTF)) { + leave_big_crumb(TXE_ONLY, 0, 0); + if (dev->msgs_left) { + i2c_write(dev, msg->data[msg->xferred++]); + if (msg->xferred == msg->length) { + /* + * End of this message. Turn off TXE/RXNE and wait for + * BTF to send repeated start or stop condition. + */ + i2c_disable_irq(dev, I2C_IRQ_BUFFER); + dev->msgs_left--; + } + } else { + /* + * This should be impossible... + */ + throb(); + } + sr1 = sr2 = 0; + } + + /* + * EV8_2: Master transmitter + * Last byte sent, program repeated start/stop + */ + if ((sr1 & I2C_SR1_TXE) && (sr1 & I2C_SR1_BTF)) { + leave_big_crumb(TXE_BTF, 0, 0); + if (dev->msgs_left) { + leave_big_crumb(TEST, 0, 0); + /* + * Repeated start insanity: We can't disable ITEVTEN or else SB + * won't interrupt, but if we don't disable ITEVTEN, BTF will + * continually interrupt us. What the fuck ST? + */ + i2c_start_condition(dev); + while (!(dev->regs->SR1 & I2C_SR1_SB)) + ; + dev->msg++; + } else { + i2c_stop_condition(dev); + + /* + * Turn off event interrupts to keep BTF from firing until + * the end of the stop condition. Why on earth they didn't + * have a start/stop condition request clear BTF is beyond + * me. + */ + i2c_disable_irq(dev, I2C_IRQ_EVENT); + leave_big_crumb(STOP_SENT, 0, 0); + dev->state = I2C_STATE_XFER_DONE; + } + sr1 = sr2 = 0; + } + + /* + * EV7: Master Receiver + */ + if (sr1 & I2C_SR1_RXNE) { + leave_big_crumb(RXNE_ONLY, 0, 0); + msg->data[msg->xferred++] = dev->regs->DR; + + /* + * EV7_1: Second to last byte in the reception? Set NACK and generate + * stop/restart condition in time for the last byte. We'll get one more + * RXNE interrupt before shutting things down. + */ + if (msg->xferred == (msg->length - 1)) { + i2c_disable_ack(dev); + if (dev->msgs_left > 2) { + i2c_start_condition(dev); + leave_big_crumb(RXNE_START_SENT, 0, 0); + } else { + i2c_stop_condition(dev); + leave_big_crumb(RXNE_STOP_SENT, 0, 0); + } + } else if (msg->xferred == msg->length) { + dev->msgs_left--; + if (dev->msgs_left == 0) { + /* + * We're done. + */ + leave_big_crumb(RXNE_DONE, 0, 0); + dev->state = I2C_STATE_XFER_DONE; + } else { + dev->msg++; + } + } + } +} + +void __irq_i2c1_ev(void) { + i2c_irq_handler(&i2c_dev1); +} + +void __irq_i2c2_ev(void) { + i2c_irq_handler(&i2c_dev2); +} + +static void i2c_irq_error_handler(i2c_dev *dev) { + uint32 sr1 = dev->regs->SR1; + uint32 sr2 = dev->regs->SR2; + leave_big_crumb(ERROR_ENTRY, sr1, sr2); + + i2c_stop_condition(dev); + i2c_disable_irq(dev, I2C_IRQ_BUFFER | I2C_IRQ_EVENT | I2C_IRQ_ERROR); + dev->state = I2C_STATE_ERROR; +} + +void __irq_i2c1_er(void) { + i2c_irq_error_handler(&i2c_dev1); +} + +void __irq_i2c2_er(void) { + i2c_irq_error_handler(&i2c_dev2); +} + +static void i2c_bus_reset(const i2c_dev *dev) { + /* Release both lines */ + gpio_write_bit(dev->gpio_port, dev->scl_pin, 1); + gpio_write_bit(dev->gpio_port, dev->sda_pin, 1); + gpio_set_mode(dev->gpio_port, dev->scl_pin, GPIO_OUTPUT_OD); + gpio_set_mode(dev->gpio_port, dev->sda_pin, GPIO_OUTPUT_OD); + + /* + * Make sure the bus is free by clocking it until any slaves release the + * bus. + */ + while (!gpio_read_bit(dev->gpio_port, dev->sda_pin)) { + /* Wait for any clock stretching to finish */ + while (!gpio_read_bit(dev->gpio_port, dev->scl_pin)) + ; + delay_us(10); + + /* Pull low */ + gpio_write_bit(dev->gpio_port, dev->scl_pin, 0); + delay_us(10); + + /* Release high again */ + gpio_write_bit(dev->gpio_port, dev->scl_pin, 1); + delay_us(10); + } + + /* Generate start then stop condition */ + gpio_write_bit(dev->gpio_port, dev->sda_pin, 0); + delay_us(10); + gpio_write_bit(dev->gpio_port, dev->scl_pin, 0); + delay_us(10); + gpio_write_bit(dev->gpio_port, dev->scl_pin, 1); + delay_us(10); + gpio_write_bit(dev->gpio_port, dev->sda_pin, 1); +} + +/** + * @brief Initialize an I2C device and reset its registers to their + * default values. + * @param dev Device to enable. + */ +void i2c_init(i2c_dev *dev) { + rcc_reset_dev(dev->clk_line); + rcc_clk_enable(dev->clk_line); +} + +/** + * @brief Initialize an i2c device as bus master + * @param dev Device to enable + * @param flags Bitwise or of the following I2C options: + * I2C_FAST_MODE: 400 khz operation + * I2C_10BIT_ADDRESSING: Enable 10-bit addressing + */ +void i2c_master_enable(i2c_dev *dev, uint32 flags) { +#define I2C_CLK (PCLK1/1000000) +#define STANDARD_CCR (PCLK1/(100000*2)) +#define STANDARD_TRISE (I2C_CLK+1) +#define FAST_CCR (I2C_CLK/10) +#define FAST_TRISE ((I2C_CLK*3)/10+1) + /* Reset the bus. Clock out any hung slaves. */ + i2c_bus_reset(dev); + + /* Turn on clock and set GPIO modes */ + i2c_init(dev); + gpio_set_mode(dev->gpio_port, dev->sda_pin, GPIO_AF_OUTPUT_OD); + gpio_set_mode(dev->gpio_port, dev->scl_pin, GPIO_AF_OUTPUT_OD); + + /* I2C1 and I2C2 are fed from APB1, clocked at 36MHz */ + i2c_set_input_clk(dev, I2C_CLK); + + if(flags & I2C_FAST_MODE) { + /* 400 kHz for fast mode, set DUTY and F/S bits */ + i2c_set_clk_control(dev, FAST_CCR|I2C_CCR_DUTY|I2C_CCR_FS); + + /* Set scl rise time, max rise time in fast mode: 300ns */ + i2c_set_trise(dev, FAST_TRISE); + + } else { + + /* 100 kHz for standard mode */ + i2c_set_clk_control(dev, STANDARD_CCR); + + /* Max rise time in standard mode: 1000 ns */ + i2c_set_trise(dev, STANDARD_TRISE); + } + + /* Enable event and buffer interrupts */ + nvic_irq_enable(dev->ev_nvic_line); + nvic_irq_enable(dev->er_nvic_line); + i2c_enable_irq(dev, I2C_IRQ_EVENT | I2C_IRQ_BUFFER | I2C_IRQ_ERROR); + + /* + * Important STM32 Errata: + * + * See STM32F10xx8 and STM32F10xxB Errata sheet (Doc ID 14574 Rev 8), + * Section 2.11.1, 2.11.2. + * + * 2.11.1: + * When the EV7, EV7_1, EV6_1, EV6_3, EV2, EV8, and EV3 events are not + * managed before the current byte is being transferred, problems may be + * encountered such as receiving an extra byte, reading the same data twice + * or missing data. + * + * 2.11.2: + * In Master Receiver mode, when closing the communication using + * method 2, the content of the last read data can be corrupted. + * + * If the user software is not able to read the data N-1 before the STOP + * condition is generated on the bus, the content of the shift register + * (data N) will be corrupted. (data N is shifted 1-bit to the left). + * + * ---------------------------------------------------------------------- + * + * In order to ensure that events are not missed, the i2c interrupt must + * not be preempted. We set the i2c interrupt priority to be the highest + * interrupt in the system (priority level 0). All other interrupts have + * been initialized to priority level 16. See nvic_init(). + */ + nvic_irq_set_priority(dev->ev_nvic_line, 0); + nvic_irq_set_priority(dev->er_nvic_line, 0); + + /* Make it go! */ + i2c_peripheral_enable(dev); +} + +int32 i2c_master_xfer(i2c_dev *dev, i2c_msg *msgs, uint16 num) { + int32 rc; + + dev->msg = msgs; + dev->msgs_left = num; + + while (dev->regs->SR2 & I2C_SR2_BUSY) + ; + + dev->state = I2C_STATE_BUSY; + i2c_enable_irq(dev, I2C_IRQ_EVENT); + + i2c_start_condition(dev); + rc = wait_for_state_change(dev, I2C_STATE_XFER_DONE); + if (rc < 0) { + goto out; + } + + dev->state = I2C_STATE_IDLE; + rc = num; +out: + return rc; +} + +static inline int32 wait_for_state_change(i2c_dev *dev, i2c_state state) { + int32 rc; + i2c_state tmp; + + while (1) { + tmp = dev->state; + if ((tmp == state) || (tmp == I2C_STATE_ERROR)) { + return (tmp == I2C_STATE_ERROR) ? -1 : 0; + } + } +} diff --git a/libmaple/i2c.h b/libmaple/i2c.h new file mode 100644 index 0000000..3f351b2 --- /dev/null +++ b/libmaple/i2c.h @@ -0,0 +1,261 @@ +/****************************************************************************** + * The MIT License + * + * Copyright (c) 2010 Perry Hung. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + *****************************************************************************/ + +/** + * @file i2c.h + * @brief Inter-Integrated Circuit (I2C) peripheral support + */ + +#ifndef _I2C_H_ +#define _I2C_H_ + +typedef struct i2c_reg_map { + __io uint32 CR1; + __io uint32 CR2; + __io uint32 OAR1; + __io uint32 OAR2; + __io uint32 DR; + __io uint32 SR1; + __io uint32 SR2; + __io uint32 CCR; + __io uint32 TRISE; +} i2c_reg_map; + +typedef enum i2c_state { + I2C_STATE_IDLE, + I2C_STATE_XFER_DONE, + I2C_STATE_BUSY, + I2C_STATE_ERROR = -1 +} i2c_state; + +typedef struct i2c_msg { + uint16 addr; +#define I2C_MSG_READ 0x1 +#define I2C_MSG_10BIT_ADDR 0x2 + uint16 flags; + uint16 length; + uint16 xferred; + uint8 *data; +} i2c_msg; + +typedef struct i2c_dev { + i2c_reg_map *regs; + gpio_dev *gpio_port; + uint8 sda_pin; + uint8 scl_pin; + uint8 clk_line; + uint8 ev_nvic_line; + uint8 er_nvic_line; + volatile uint8 state; + uint16 msgs_left; + i2c_msg *msg; +} i2c_dev; + +extern i2c_dev* const I2C1; +extern i2c_dev* const I2C2; + +#define I2C1_BASE (i2c_reg_map*)0x40005400 +#define I2C2_BASE (i2c_reg_map*)0x40005800 + +/* i2c enable options */ +#define I2C_FAST_MODE 0x1 // 400 khz +#define I2C_DUTY_16_9 0x2 // 16/9 duty ratio + +/* Control register 1 bits */ +#define I2C_CR1_SWRST BIT(15) // Software reset +#define I2C_CR1_ALERT BIT(13) // SMBus alert +#define I2C_CR1_PEC BIT(12) // Packet error checking +#define I2C_CR1_POS BIT(11) // Acknowledge/PEC position +#define I2C_CR1_ACK BIT(10) // Acknowledge enable +#define I2C_CR1_START BIT(8) // Start generation +#define I2C_CR1_STOP BIT(9) // Stop generation +#define I2C_CR1_PE BIT(0) // Peripheral Enable + +/* Control register 2 bits */ +#define I2C_CR2_LAST BIT(12) // DMA last transfer +#define I2C_CR2_DMAEN BIT(11) // DMA requests enable +#define I2C_CR2_ITBUFEN BIT(10) // Buffer interrupt enable +#define I2C_CR2_ITEVTEN BIT(9) // Event interupt enable +#define I2C_CR2_ITERREN BIT(8) // Error interupt enable +#define I2C_CR2_FREQ 0xFFF // Peripheral input frequency + +/* Clock control register bits */ +#define I2C_CCR_FS BIT(15) // Fast mode selection +#define I2C_CCR_DUTY BIT(14) // 16/9 duty ratio +#define I2C_CCR_CCR 0xFFF // Clock control bits + +/* Status register 1 bits */ +#define I2C_SR1_SB BIT(0) // Start bit +#define I2C_SR1_ADDR BIT(1) // Address sent/matched +#define I2C_SR1_BTF BIT(2) // Byte transfer finished +#define I2C_SR1_ADD10 BIT(3) // 10-bit header sent +#define I2C_SR1_STOPF BIT(4) // Stop detection +#define I2C_SR1_RXNE BIT(6) // Data register not empty +#define I2C_SR1_TXE BIT(7) // Data register empty +#define I2C_SR1_BERR BIT(8) // Bus error +#define I2C_SR1_ARLO BIT(9) // Arbitration lost +#define I2C_SR1_AF BIT(10) // Acknowledge failure +#define I2C_SR1_OVR BIT(11) // Overrun/underrun +#define I2C_SR1_PECERR BIT(12) // PEC Error in reception +#define I2C_SR1_TIMEOUT BIT(14) // Timeout or Tlow error +#define I2C_SR1_SMBALERT BIT(15) // SMBus alert + +/* Status register 2 bits */ +#define I2C_SR2_MSL BIT(0) // Master/slave +#define I2C_SR2_BUSY BIT(1) // Bus busy +#define I2C_SR2_TRA BIT(2) // Transmitter/receiver +#define I2C_SR2_GENCALL BIT(4) // General call address +#define I2C_SR2_SMBDEFAULT BIT(5) // SMBus device default address +#define I2C_SR2_SMBHOST BIT(6) // SMBus host header +#define I2C_SR2_DUALF BIT(7) // Dual flag +#define I2C_SR2_PEC 0xFF00 // Packet error checking register + +#ifdef __cplusplus +extern "C" { +#endif + +void i2c_init(i2c_dev *dev); +void i2c_master_enable(i2c_dev *dev, uint32 flags); +int32 i2c_master_xfer(i2c_dev *dev, i2c_msg *msgs, uint16 num); + +/* + * Low level register twiddling functions + */ + +/** + * @brief turn on an i2c peripheral + * @param dev i2c device + */ +static inline void i2c_peripheral_enable(i2c_dev *dev) { + dev->regs->CR1 |= I2C_CR1_PE; +} + +/** + * @brief turn off an i2c peripheral + * @param dev i2c device + */ +static inline void i2c_peripheral_disable(i2c_dev *dev) { + dev->regs->CR1 &= ~I2C_CR1_PE; +} + +/** + * @brief Fill transmit register + * @param dev i2c device + * @param byte byte to write + */ +static inline void i2c_write(i2c_dev *dev, uint8 byte) { + dev->regs->DR = byte; +} + +/** + * @brief Set input clock frequency, in mhz + * @param dev i2c + * @param freq frequency in megahertz (2-36) + */ +static inline void i2c_set_input_clk(i2c_dev *dev, uint32 freq) { + uint32 cr2 = dev->regs->CR2; + cr2 &= ~I2C_CR2_FREQ; + cr2 |= freq; + dev->regs->CR2 = freq; +} + +/** + * @brief Set i2c clock control register. See RM008 + * @param dev i2c device + * @return + * @sideeffect + */ +static inline void i2c_set_clk_control(i2c_dev *dev, uint32 val) { + uint32 ccr = dev->regs->CCR; + ccr &= ~I2C_CCR_CCR; + ccr |= val; + dev->regs->CCR = ccr; +} + +static inline void i2c_set_fast_mode(i2c_dev *dev) { + dev->regs->CCR |= I2C_CCR_FS; +} + +static inline void i2c_set_standard_mode(i2c_dev *dev) { + dev->regs->CCR &= ~I2C_CCR_FS; +} + +/** + * @brief Set SCL rise time + * @param + */ +static inline void i2c_set_trise(i2c_dev *dev, uint32 trise) { + dev->regs->TRISE = trise; +} + +static inline void i2c_start_condition(i2c_dev *dev) { + uint32 cr1; + while ((cr1 = dev->regs->CR1) & (I2C_CR1_START | + I2C_CR1_STOP | + I2C_CR1_PEC)) { + ; + } + dev->regs->CR1 |= I2C_CR1_START; +} + +static inline void i2c_stop_condition(i2c_dev *dev) { + uint32 cr1; + while ((cr1 = dev->regs->CR1) & (I2C_CR1_START | + I2C_CR1_STOP | + I2C_CR1_PEC)) { + ; + } + dev->regs->CR1 |= I2C_CR1_STOP; +} + +static inline void i2c_send_slave_addr(i2c_dev *dev, uint32 addr, uint32 rw) { + dev->regs->DR = (addr << 1) | rw; +} + +#define I2C_IRQ_ERROR I2C_CR2_ITERREN +#define I2C_IRQ_EVENT I2C_CR2_ITEVTEN +#define I2C_IRQ_BUFFER I2C_CR2_ITBUFEN +static inline void i2c_enable_irq(i2c_dev *dev, uint32 irqs) { + dev->regs->CR2 |= irqs; +} + +static inline void i2c_disable_irq(i2c_dev *dev, uint32 irqs) { + dev->regs->CR2 &= ~irqs; +} + +static inline void i2c_enable_ack(i2c_dev *dev) { + dev->regs->CR1 |= I2C_CR1_ACK; +} + +static inline void i2c_disable_ack(i2c_dev *dev) { + dev->regs->CR1 &= ~I2C_CR1_ACK; +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libmaple/iwdg.c b/libmaple/iwdg.c index 82a0151..3d67ee8 100644 --- a/libmaple/iwdg.c +++ b/libmaple/iwdg.c @@ -23,34 +23,37 @@ *****************************************************************************/ /** - * @file iwdg.c - * - * @brief Independent watchdog support + * @file iwdg.c + * @brief Independent watchdog (IWDG) support */ -#include "libmaple.h" #include "iwdg.h" -#define IWDG_UNLOCK 0x5555 -#define IWDG_START 0xCCCC -#define IWDG_FEED 0xAAAA - /** - * @brief Initialise and start the watchdog + * @brief Initialise and start the watchdog + * + * The prescaler and reload set the timeout. A prescaler of 3 divides + * the 40 kHz clock by 32 and gives roughly 1 ms per reload. * - * The prescaler and reload set the timeout. A prescaler of 3 divides - * the 40 kHz clock by 32 and gives roughly 1 ms per reload. + * @param prescaler Prescaler for the 40 KHz IWDG clock. + * @param reload Independent watchdog counter reload value. */ -void iwdg_init(uint8 prescaler, uint16 reload) { - __write(IWDG_KR, IWDG_UNLOCK); - __write(IWDG_PR, prescaler); - __write(IWDG_RLR, reload); +void iwdg_init(iwdg_prescaler prescaler, uint16 reload) { + IWDG_BASE->KR = IWDG_KR_UNLOCK; + IWDG_BASE->PR = prescaler; + IWDG_BASE->RLR = reload; - /* Start things off */ - __write(IWDG_KR, IWDG_START); - __write(IWDG_KR, IWDG_FEED); + /* Start things off */ + IWDG_BASE->KR = IWDG_KR_START; + iwdg_feed(); } +/** + * @brief Reset the IWDG counter. + * + * Calling this function will cause the IWDG counter to be reset to + * its reload value. + */ void iwdg_feed(void) { - __write(IWDG_KR, IWDG_FEED); + IWDG_BASE->KR = IWDG_KR_FEED; } diff --git a/libmaple/iwdg.h b/libmaple/iwdg.h index 4ab0ddf..e0a2d62 100644 --- a/libmaple/iwdg.h +++ b/libmaple/iwdg.h @@ -23,35 +23,83 @@ *****************************************************************************/ /** - * @file iwdg.h + * @file iwdg.h + * @author Michael Hope, Marti Bolivar <mbolivar@leaflabs.com> + * @brief Independent watchdog support. * - * @brief Independent watchdog support + * To use the independent watchdog, first call iwdg_init() with the + * appropriate prescaler and IWDG counter reload values for your + * application. Afterwards, you must periodically call iwdg_feed() + * before the IWDG counter reaches 0 to reset the counter to its + * reload value. If you do not, the chip will reset. + * + * Once started, the independent watchdog cannot be turned off. */ #ifndef _IWDG_H_ #define _IWDG_H_ +#include "libmaple_types.h" +#include "util.h" + #ifdef __cplusplus extern "C"{ #endif -#define IWDG_BASE 0x40003000 -#define IWDG_KR (IWDG_BASE + 0x0) -#define IWDG_PR (IWDG_BASE + 0x4) -#define IWDG_RLR (IWDG_BASE + 0x8) -#define IWDG_SR (IWDG_BASE + 0xC) - -enum { - IWDG_PRE_4, - IWDG_PRE_8, - IWDG_PRE_16, - IWDG_PRE_32, - IWDG_PRE_64, - IWDG_PRE_128, - IWDG_PRE_256 -}; - -void iwdg_init(uint8 prescaler, uint16 reload); +/* + * Register map + */ + +/** Independent watchdog register map type. */ +typedef struct iwdg_reg_map { + __io uint32 KR; /**< Key register. */ + __io uint32 PR; /**< Prescaler register. */ + __io uint32 RLR; /**< Reload register. */ + __io uint32 SR; /**< Status register */ +} iwdg_reg_map; + +#define IWDG_BASE ((struct iwdg_reg_map*)0x40003000) + +/* + * Register bit definitions + */ + +/* Key register */ + +#define IWDG_KR_UNLOCK 0x5555 +#define IWDG_KR_FEED 0xAAAA +#define IWDG_KR_START 0xCCCC + +/* Prescaler register */ + +#define IWDG_PR_DIV_4 0x0 +#define IWDG_PR_DIV_8 0x1 +#define IWDG_PR_DIV_16 0x2 +#define IWDG_PR_DIV_32 0x3 +#define IWDG_PR_DIV_64 0x4 +#define IWDG_PR_DIV_128 0x5 +#define IWDG_PR_DIV_256 0x6 + +/* Status register */ + +#define IWDG_SR_RVU_BIT 1 +#define IWDG_SR_PVU_BIT 0 + +#define IWDG_SR_RVU BIT(IWDG_SR_RVU_BIT) +#define IWDG_SR_PVU BIT(IWDG_SR_PVU_BIT) + +/** Independent watchdog prescalers */ +typedef enum { + IWDG_PRE_4 = IWDG_PR_DIV_4, + IWDG_PRE_8 = IWDG_PR_DIV_8, + IWDG_PRE_16 = IWDG_PR_DIV_16, + IWDG_PRE_32 = IWDG_PR_DIV_32, + IWDG_PRE_64 = IWDG_PR_DIV_64, + IWDG_PRE_128 = IWDG_PR_DIV_128, + IWDG_PRE_256 = IWDG_PR_DIV_256 +} iwdg_prescaler; + +void iwdg_init(iwdg_prescaler prescaler, uint16 reload); void iwdg_feed(void); #ifdef __cplusplus diff --git a/libmaple/libmaple.h b/libmaple/libmaple.h index 02e27d3..8e11660 100644 --- a/libmaple/libmaple.h +++ b/libmaple/libmaple.h @@ -24,159 +24,57 @@ /** * @file libmaple.h - * - * @brief general include file for libmaple + * @brief General include file for libmaple */ #ifndef _LIBMAPLE_H_ #define _LIBMAPLE_H_ #include "libmaple_types.h" +#include "stm32.h" +#include "util.h" +#include "delay.h" -/* General configuration */ -#define DEBUG_NONE 0 -#define DEBUG_FAULT 1 -#define DEBUG_ALL 2 - -#ifndef DEBUG_LEVEL -#define DEBUG_LEVEL DEBUG_ALL -#endif +/* + * Where to put usercode, based on space reserved for bootloader. + * + * FIXME this has no business being here + */ +#define USER_ADDR_ROM 0x08005000 +#define USER_ADDR_RAM 0x20000C00 +#define STACK_TOP 0x20000800 /* MCU-specific configuration */ #if defined(MCU_STM32F103RB) /* e.g., LeafLabs Maple */ /* Number of GPIO ports (GPIOA, GPIOB, etc.) */ - #define NR_GPIO_PORTS 4 - - /* Total number of GPIO pins */ - #define NR_GPIO_PINS 39 - - /* Number of 16-bit backup registers */ - #define NR_BKP_REGS 10 - - /* Number of timer devices ports, definitely used */ - #define NR_TIMERS 4 - - /* Number of USART ports */ - #define NR_USART 3 - - /* Has an FSMC bus? */ - #define NR_FSMC 0 - - /* Has a DAC? */ - #define NR_DAC_PINS 0 - - /* USB Identifier numbers */ - /* Descriptor strings must be modified by hand in - usb/descriptors.c for now */ - #define VCOM_ID_VENDOR 0x1EAF - #define VCOM_ID_PRODUCT 0x0004 - #define USB_DISC_BANK GPIOC_BASE - #define USB_DISC_PIN 12 - #define USB_CONFIG_MAX_POWER (100 >> 1) - #define RESET_DELAY (100) - - /* Where to put usercode (based on space reserved for bootloader) */ - #define USER_ADDR_ROM 0x08005000 - #define USER_ADDR_RAM 0x20000C00 - #define STACK_TOP 0x20000800 - - /* Debug port settings (from ASSERT) */ - #define ERROR_LED_PORT GPIOB_BASE - #define ERROR_LED_PIN 12 - #define ERROR_USART_NUM USART2 - #define ERROR_USART_BAUD 9600 - #define ERROR_TX_PORT GPIOA_BASE - #define ERROR_TX_PIN 2 - - /* Just in case, most boards have at least some memory */ - #ifndef RAMSIZE - # define RAMSIZE (caddr_t)0x50000 - #endif - - /* Bitbanded Memory sections */ - #define BITBAND_SRAM_REF 0x20000000 - #define BITBAND_SRAM_BASE 0x22000000 - #define BITBAND_PERI_REF 0x40000000 - #define BITBAND_PERI_BASE 0x42000000 + #define NR_GPIO_PORTS 4 + + /* SRAM size, in bytes */ + #define SRAM_SIZE 0x5000 #elif defined(MCU_STM32F103ZE) /* e.g., LeafLabs Maple Native */ - #define NR_GPIO_PORTS 7 - #define NR_GPIO_PINS 100 - #define NR_BKP_REGS 42 /* TODO test on Native */ - #define NR_TIMERS 8 - #define NR_USART 5 /* NB: 4 and 5 are UART only */ - #define NR_FSMC 1 - #define NR_DAC_PINS 2 - - #define VCOM_ID_VENDOR 0x1EAF - #define VCOM_ID_PRODUCT 0x0004 - #define USB_DISC_BANK GPIOB_BASE - #define USB_DISC_PIN 8 - #define USB_CONFIG_MAX_POWER (100 >> 1) - #define RESET_DELAY (100) - - #define USER_ADDR_ROM 0x08005000 - #define USER_ADDR_RAM 0x20000C00 - #define STACK_TOP 0x20000800 - - #define ERROR_LED_PORT GPIOC_BASE - #define ERROR_LED_PIN 15 - #define ERROR_USART_NUM USART1 - #define ERROR_USART_BAUD 9600 - #define ERROR_TX_PORT GPIOA_BASE - #define ERROR_TX_PIN 10 - - #ifndef RAMSIZE - # define RAMSIZE (caddr_t)0x50000 - #endif - - #define BITBAND_SRAM_REF 0x20000000 - #define BITBAND_SRAM_BASE 0x22000000 - #define BITBAND_PERI_REF 0x40000000 - #define BITBAND_PERI_BASE 0x42000000 + #define NR_GPIO_PORTS 7 + #define SRAM_SIZE 0x10000 #elif defined(MCU_STM32F103CB) /* e.g., LeafLabs Maple Mini */ - #define NR_GPIO_PORTS 3 - #define NR_GPIO_PINS 34 - #define NR_BKP_REGS 10 /* TODO test on Mini */ - #define NR_TIMERS 4 - #define NR_USART 3 - #define NR_FSMC 0 - #define NR_DAC_PINS 0 - - #define VCOM_ID_VENDOR 0x1EAF - #define VCOM_ID_PRODUCT 0x0005 - #define USB_DISC_BANK GPIOB_BASE - #define USB_DISC_PIN 9 - #define USB_CONFIG_MAX_POWER (100 >> 1) - #define RESET_DELAY 100 - - #define USER_ADDR_ROM 0x08005000 - #define USER_ADDR_RAM 0x20000C00 - #define STACK_TOP 0x20000800 - - #define ERROR_LED_PORT GPIOB_BASE - #define ERROR_LED_PIN 12 - #define ERROR_USART_NUM USART2 - #define ERROR_USART_BAUD 9600 - #define ERROR_TX_PORT GPIOA_BASE - #define ERROR_TX_PIN 2 - - #ifndef RAMSIZE - # define RAMSIZE (caddr_t)0x50000 - #endif - - /* Bitbanded Memory sections */ - #define BITBAND_SRAM_REF 0x20000000 - #define BITBAND_SRAM_BASE 0x22000000 - #define BITBAND_PERI_REF 0x40000000 - #define BITBAND_PERI_BASE 0x42000000 + /* Note that this is not, strictly speaking, true. But only pins + 0 and 1 exist, and they're used for OSC on the Mini, so we'll + live with this for now. */ + #define NR_GPIO_PORTS 3 + + #define SRAM_SIZE 0x5000 + +#elif defined(MCU_STM32F103RE) + /* e.g., LeafLabs Maple RET6 edition */ + + #define NR_GPIO_PORTS 4 + #define SRAM_SIZE 0x10000 #else @@ -185,8 +83,5 @@ #endif -/* Requires board configuration info */ -#include "util.h" - #endif diff --git a/libmaple/libmaple_types.h b/libmaple/libmaple_types.h index 8d216a8..54bef65 100644 --- a/libmaple/libmaple_types.h +++ b/libmaple/libmaple_types.h @@ -44,8 +44,7 @@ typedef long long int64; typedef void (*voidFuncPtr)(void); #define __io volatile - -#define ALWAYS_INLINE inline __attribute__((always_inline)) +#define __attr_flash __attribute__((section (".USER_FLASH"))) #ifndef NULL #define NULL 0 diff --git a/libmaple/nvic.c b/libmaple/nvic.c index b1da605..a7a6d8e 100644 --- a/libmaple/nvic.c +++ b/libmaple/nvic.c @@ -26,56 +26,57 @@ * @brief Nested interrupt controller routines */ -#include "libmaple.h" #include "nvic.h" -#include "systick.h" - -void nvic_set_vector_table(uint32 addr, uint32 offset) { - __write(SCB_VTOR, (uint32)addr | (offset & 0x1FFFFF80)); -} +#include "scb.h" +#include "stm32.h" /** - * @brief turn on interrupt number n - * @param n interrupt number + * @brief Set interrupt priority for an interrupt line + * + * Note: The STM32 only implements 4 bits of priority, ignoring the + * lower 4 bits. This means there are only 16 levels of priority. + * Bits[3:0] read as zero and ignore writes. + * + * @param irqn device to set + * @param priority Priority to set, 0 being highest priority and 15 + * being lowest. */ -void nvic_irq_enable(uint32 n) { - uint32 *iser = &((uint32*)NVIC_ISER0)[(n/32)]; - __write(iser, BIT(n % 32)); +void nvic_irq_set_priority(nvic_irq_num irqn, uint8 priority) { + if (irqn < 0) { + /* This interrupt is in the system handler block */ + SCB_BASE->SHP[((uint32)irqn & 0xF) - 4] = (priority & 0xF) << 4; + } else { + NVIC_BASE->IP[irqn] = (priority & 0xF) << 4; + } } /** - * @brief turn off interrupt number n - * @param n interrupt number + * @brief Initialize the NVIC + * @param vector_table_address Vector table base address. + * @param offset Offset from vector_table_address. Some restrictions + * apply to the use of nonzero offsets; see ST RM0008 + * and the ARM Cortex M3 Technical Reference Manual. */ -void nvic_irq_disable(uint32 n) { - uint32 *icer = &((uint32*)NVIC_ICER0)[(n/32)]; - __write(icer, BIT(n % 32)); -} +void nvic_init(uint32 vector_table_address, uint32 offset) { + uint32 i; + + nvic_set_vector_table(vector_table_address, offset); -void nvic_irq_disable_all(void) { - /* Each ICER register contains 1 bit per interrupt. Writing a 1 - to that bit disables the corresponding interrupt. So each of - the following lines disables up to 32 interrupts at a time. - Since low, medium, and high-density devices all have less than - 64 interrupts, this suffices. */ - /* TODO: fix for connectivity line: __write(NVIC_ICER2,1), - requires connectivity line support in libmaple.h */ - __write(NVIC_ICER0, 0xFFFFFFFF); - __write(NVIC_ICER1, 0xFFFFFFFF); + /* + * Lower priority level for all peripheral interrupts to lowest + * possible. + */ + for (i = 0; i < NR_INTERRUPTS; i++) { + nvic_irq_set_priority((nvic_irq_num)i, 0xF); + } + + /* Lower systick interrupt priority to lowest level */ + nvic_irq_set_priority(NVIC_SYSTICK, 0xF); } /** - * @brief Initialize the NVIC according to VECT_TAB_FLASH, - * VECT_TAB_RAM, or VECT_TAB_BASE. + * Reset the vector table address. */ -void nvic_init(void) { -#ifdef VECT_TAB_FLASH - nvic_set_vector_table(USER_ADDR_ROM, 0x0); -#elif defined VECT_TAB_RAM - nvic_set_vector_table(USER_ADDR_RAM, 0x0); -#elif defined VECT_TAB_BASE - nvic_set_vector_table(((uint32)0x08000000), 0x0); -#else -#error "You must set a base address for the vector table!" -#endif +void nvic_set_vector_table(uint32 addr, uint32 offset) { + SCB_BASE->VTOR = addr | (offset & 0x1FFFFF80); } diff --git a/libmaple/nvic.h b/libmaple/nvic.h index 6004c36..496c41b 100644 --- a/libmaple/nvic.h +++ b/libmaple/nvic.h @@ -24,73 +24,177 @@ /** * @file nvic.h - * @brief Nested interrupt controller defines and prototypes + * @brief Nested vector interrupt controller support. */ #ifndef _NVIC_H_ #define _NVIC_H_ +#include "libmaple_types.h" +#include "util.h" + #ifdef __cplusplus extern "C"{ #endif -#define NVIC_INT_USBHP 19 -#define NVIC_INT_USBLP 20 - -/* NVIC Interrupt Enable registers */ -#define NVIC_ISER0 0xE000E100 -#define NVIC_ISER1 0xE000E104 -/* NVIC_ISER2 only on connectivity line */ - -/* NVIC Interrupt Clear registers */ -#define NVIC_ICER0 0xE000E180 -#define NVIC_ICER1 0xE000E184 -/* NVIC_ICER2 only on connectivity line */ - -/* System control registers */ -#define SCB_VTOR 0xE000ED08 // Vector table offset register - -enum { - NVIC_TIMER1 = 27, - NVIC_TIMER2 = 28, - NVIC_TIMER3 = 29, - NVIC_TIMER4 = 30, - NVIC_TIMER5 = 50, // high density only (Maple Native, Maple Audio) - NVIC_TIMER6 = 54, // high density only - NVIC_TIMER7 = 55, // high density only - NVIC_TIMER8 = 46, // high density only - - NVIC_USART1 = 37, - NVIC_USART2 = 38, - NVIC_USART3 = 39, - NVIC_UART4 = 52, // high density only - NVIC_UART5 = 53, // high density only - - NVIC_EXTI0 = 6, - NVIC_EXTI1 = 7, - NVIC_EXTI2 = 8, - NVIC_EXTI3 = 9, - NVIC_EXTI4 = 10, - NVIC_EXTI9_5 = 23, - NVIC_EXTI15_10 = 40, - - NVIC_DMA_CH1 = 11, - NVIC_DMA_CH2 = 12, - NVIC_DMA_CH3 = 13, - NVIC_DMA_CH4 = 14, - NVIC_DMA_CH5 = 15, - NVIC_DMA_CH6 = 16, - NVIC_DMA_CH7 = 17 -}; - - -#define nvic_globalirq_enable() asm volatile("cpsie i") -#define nvic_globalirq_disable() asm volatile("cpsid i") - -void nvic_init(void); -void nvic_irq_enable(uint32 device); -void nvic_irq_disable(uint32 device); -void nvic_irq_disable_all(void); +/** NVIC register map type. */ +typedef struct nvic_reg_map { + __io uint32 ISER[8]; /**< Interrupt Set Enable Registers */ + uint32 RESERVED0[24]; /**< Reserved */ + __io uint32 ICER[8]; /**< Interrupt Clear Enable Registers */ + uint32 RSERVED1[24]; /**< Reserved */ + __io uint32 ISPR[8]; /**< Interrupt Set Pending Registers */ + uint32 RESERVED2[24]; /**< Reserved */ + __io uint32 ICPR[8]; /**< Interrupt Clear Pending Registers */ + uint32 RESERVED3[24]; /**< Reserved */ + __io uint32 IABR[8]; /**< Interrupt Active bit Registers */ + uint32 RESERVED4[56]; /**< Reserved */ + __io uint8 IP[240]; /**< Interrupt Priority Registers */ + uint32 RESERVED5[644]; /**< Reserved */ + __io uint32 STIR; /**< Software Trigger Interrupt Registers */ +} nvic_reg_map; + +/** NVIC register map base pointer. */ +#define NVIC_BASE ((nvic_reg_map*)0xE000E100) + +/** + * Interrupt vector table interrupt numbers. Each enumerator is the + * position of the corresponding interrupt in the vector table. */ +typedef enum nvic_irq_num { + NVIC_NMI = -14, /**< Non-maskable interrupt */ + NVIC_HARDFAULT = -13, /**< Hard fault (all class of fault) */ + NVIC_MEM_MANAGE = -12, /**< Memory management */ + NVIC_BUS_FAULT = -11, /**< Bus fault: prefetch fault, memory + access fault. */ + NVIC_USAGE_FAULT = -10, /**< Usage fault: Undefined instruction or + illegal state. */ + NVIC_SVC = -5, /**< System service call via SWI insruction */ + NVIC_DEBUG_MON = -4, /**< Debug monitor */ + NVIC_PEND_SVC = -2, /**< Pendable request for system service */ + NVIC_SYSTICK = -1, /**< System tick timer */ + NVIC_WWDG = 0, /**< Window watchdog interrupt */ + NVIC_PVD = 1, /**< PVD through EXTI line detection */ + NVIC_TAMPER = 2, /**< Tamper */ + NVIC_RTC = 3, /**< Real-time clock */ + NVIC_FLASH = 4, /**< Flash */ + NVIC_RCC = 5, /**< Reset and clock control */ + NVIC_EXTI0 = 6, /**< EXTI line 0 */ + NVIC_EXTI1 = 7, /**< EXTI line 1 */ + NVIC_EXTI2 = 8, /**< EXTI line 2 */ + NVIC_EXTI3 = 9, /**< EXTI line 3 */ + NVIC_EXTI4 = 10, /**< EXTI line 4 */ + NVIC_DMA_CH1 = 11, /**< DMA1 channel 1 */ + NVIC_DMA_CH2 = 12, /**< DMA1 channel 2 */ + NVIC_DMA_CH3 = 13, /**< DMA1 channel 3 */ + NVIC_DMA_CH4 = 14, /**< DMA1 channel 4 */ + NVIC_DMA_CH5 = 15, /**< DMA1 channel 5 */ + NVIC_DMA_CH6 = 16, /**< DMA1 channel 6 */ + NVIC_DMA_CH7 = 17, /**< DMA1 channel 7 */ + NVIC_ADC_1_2 = 18, /**< ADC1 and ADC2 */ + NVIC_USB_HP_CAN_TX = 19, /**< USB high priority or CAN TX */ + NVIC_USB_LP_CAN_RX0 = 20, /**< USB low priority or CAN RX0 */ + NVIC_CAN_RX1 = 21, /**< CAN RX1 */ + NVIC_CAN_SCE = 22, /**< CAN SCE */ + NVIC_EXTI_9_5 = 23, /**< EXTI line [9:5] */ + NVIC_TIMER1_BRK = 24, /**< Timer 1 break */ + NVIC_TIMER1_UP = 25, /**< Timer 1 update */ + NVIC_TIMER1_TRG_COM = 26, /**< Timer 1 trigger and commutation */ + NVIC_TIMER1_CC = 27, /**< Timer 1 capture/compare */ + NVIC_TIMER2 = 28, /**< Timer 2 */ + NVIC_TIMER3 = 29, /**< Timer 3 */ + NVIC_TIMER4 = 30, /**< Timer 4 */ + NVIC_I2C1_EV = 31, /**< I2C1 event */ + NVIC_I2C1_ER = 32, /**< I2C1 error */ + NVIC_I2C2_EV = 33, /**< I2C2 event */ + NVIC_I2C2_ER = 34, /**< I2C2 error */ + NVIC_SPI1 = 35, /**< SPI1 */ + NVIC_SPI2 = 36, /**< SPI2 */ + NVIC_USART1 = 37, /**< USART1 */ + NVIC_USART2 = 38, /**< USART2 */ + NVIC_USART3 = 39, /**< USART3 */ + NVIC_EXTI_15_10 = 40, /**< EXTI line [15:10] */ + NVIC_RTCALARM = 41, /**< RTC alarm through EXTI line */ + NVIC_USBWAKEUP = 42, /**< USB wakeup from suspend through + EXTI line */ + NVIC_TIMER8_BRK = 43, /**< Timer 8 break */ + NVIC_TIMER8_UP = 44, /**< Timer 8 update */ + NVIC_TIMER8_TRG_COM = 45, /**< Timer 8 trigger and commutation */ + NVIC_TIMER8_CC = 46, /**< Timer 8 capture/compare */ +#ifdef STM32_HIGH_DENSITY + NVIC_ADC3 = 47, /**< ADC3 */ + NVIC_FSMC = 48, /**< FSMC */ + NVIC_SDIO = 49, /**< SDIO */ + NVIC_TIMER5 = 50, /**< Timer 5 */ + NVIC_SPI3 = 51, /**< SPI3 */ + NVIC_UART4 = 52, /**< UART4 */ + NVIC_UART5 = 53, /**< UART5 */ + NVIC_TIMER6 = 54, /**< Timer 6 */ + NVIC_TIMER7 = 55, /**< Timer 7 */ + NVIC_DMA2_CH1 = 56, /**< DMA2 channel 1 */ + NVIC_DMA2_CH2 = 57, /**< DMA2 channel 2 */ + NVIC_DMA2_CH3 = 58, /**< DMA2 channel 3 */ + NVIC_DMA2_CH_4_5 = 59, /**< DMA2 channels 4 and 5 */ +#endif +} nvic_irq_num; + +void nvic_init(uint32 vector_table_address, uint32 offset); +void nvic_set_vector_table(uint32 address, uint32 offset); +void nvic_set_priority(nvic_irq_num irqn, uint8 priority); + +/** + * Enables interrupts and configurable fault handlers (clear PRIMASK). + */ +static inline void nvic_globalirq_enable() { + asm volatile("cpsie i"); +} + +/** + * Disable interrupts and configurable fault handlers (set PRIMASK). + */ +static inline void nvic_globalirq_disable() { + asm volatile("cpsid i"); +} + +/** + * @brief Enable interrupt irq_num + * @param irq_num Interrupt to enable + */ +static inline void nvic_irq_enable(nvic_irq_num irq_num) { + if (irq_num < 0) { + return; + } + NVIC_BASE->ISER[irq_num / 32] = BIT(irq_num % 32); +} + +/** + * @brief Disable interrupt irq_num + * @param irq_num Interrupt to disable + */ +static inline void nvic_irq_disable(nvic_irq_num irq_num) { + if (irq_num < 0) { + return; + } + NVIC_BASE->ICER[irq_num / 32] = BIT(irq_num % 32); +} + +/** + * @brief Quickly disable all interrupts. + * + * Calling this function is significantly faster than calling + * nvic_irq_disable() in a loop. + */ +static inline void nvic_irq_disable_all(void) { + /* Note: This only works up to XL density. The fix for + * connectivity line is: + * + * NVIC_BASE->ICER[2] = 0xF; + * + * We don't support connectivity line devices (yet), so leave it + * alone for now. + */ + NVIC_BASE->ICER[0] = 0xFFFFFFFF; + NVIC_BASE->ICER[1] = 0xFFFFFFFF; +} #ifdef __cplusplus } diff --git a/libmaple/pwr.c b/libmaple/pwr.c new file mode 100644 index 0000000..d63a92d --- /dev/null +++ b/libmaple/pwr.c @@ -0,0 +1,42 @@ +/****************************************************************************** + * The MIT License + * + * Copyright (c) 2011 LeafLabs, LLC. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + *****************************************************************************/ + +#include "pwr.h" +#include "rcc.h" + +pwr_dev pwr = { + .regs = PWR_BASE, +}; + +const pwr_dev *PWR = &pwr; + +/** + * Enables the power interface clock, and resets the power device. + */ +void pwr_init(void) { + rcc_clk_enable(RCC_PWR); + rcc_reset_dev(RCC_PWR); +} diff --git a/libmaple/pwr.h b/libmaple/pwr.h index 96a8356..5f68c34 100644 --- a/libmaple/pwr.h +++ b/libmaple/pwr.h @@ -29,18 +29,51 @@ * @brief Power control (PWR) defines. */ -#define PWR_BASE 0x40007000 - -#define PWR_CR (PWR_BASE + 0x0) -#define PWR_CR_DBP 8 /* Disable backup domain write protection bit */ -#define PWR_CR_PVDE 4 /* Power voltage detector enable bit */ -#define PWR_CR_CSBF 3 /* Clear standby flag bit */ -#define PWR_CR_CWUF 2 /* Clear wakeup flag bit */ -#define PWR_CR_PDDS 1 /* Power down deepsleep bit */ -#define PWR_CR_LPDS 0 /* Low-power deepsleep bit */ - -#define PWR_CSR (PWR_BASE + 0x4) -#define PWR_CSR_EWUP 8 /* Enable wakeup pin bit */ -#define PWR_CSR_PVDO 2 /* PVD output bit */ -#define PWR_CSR_SBF 1 /* Standby flag bit */ -#define PWR_CSR_WUF 0 /* Wakeup flag bit */ +#include "libmaple.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** Power interface register map. */ +typedef struct pwr_reg_map { + __io uint32 CR; /**< Control register */ + __io uint32 CSR; /**< Control and status register */ +} pwr_reg_map; + +/** Power peripheral register map base pointer. */ +#define PWR_BASE ((pwr_reg_map*)0x40007000) + +/** Power device type. */ +typedef struct pwr_dev { + pwr_reg_map *regs; /**< Register map */ +} pwr_dev; + +/** + * Power device. + */ +extern const pwr_dev *PWR; + +/* + * Register bit definitions + */ + +/* Control register */ +#define PWR_CR_DBP 8 /**< Disable backup domain write protection bit */ +#define PWR_CR_PVDE 4 /**< Power voltage detector enable bit */ +#define PWR_CR_CSBF 3 /**< Clear standby flag bit */ +#define PWR_CR_CWUF 2 /**< Clear wakeup flag bit */ +#define PWR_CR_PDDS 1 /**< Power down deepsleep bit */ +#define PWR_CR_LPDS 0 /**< Low-power deepsleep bit */ + +/* Control and status register */ +#define PWR_CSR_EWUP 8 /**< Enable wakeup pin bit */ +#define PWR_CSR_PVDO 2 /**< PVD output bit */ +#define PWR_CSR_SBF 1 /**< Standby flag bit */ +#define PWR_CSR_WUF 0 /**< Wakeup flag bit */ + +void pwr_init(void); + +#ifdef __cplusplus +} +#endif diff --git a/libmaple/rcc.c b/libmaple/rcc.c index 6905c22..37c1ec4 100644 --- a/libmaple/rcc.c +++ b/libmaple/rcc.c @@ -30,49 +30,69 @@ #include "libmaple.h" #include "flash.h" #include "rcc.h" +#include "bitband.h" -enum { - APB1, - APB2, - AHB -}; +#define APB1 RCC_APB1 +#define APB2 RCC_APB2 +#define AHB RCC_AHB struct rcc_dev_info { - const uint8 clk_domain; + const rcc_clk_domain clk_domain; const uint8 line_num; }; -/* device descriptor tables */ +/* Device descriptor table, maps rcc_clk_id onto bus and enable/reset + * register bit numbers. */ static const struct rcc_dev_info rcc_dev_table[] = { [RCC_GPIOA] = { .clk_domain = APB2, .line_num = 2 }, [RCC_GPIOB] = { .clk_domain = APB2, .line_num = 3 }, [RCC_GPIOC] = { .clk_domain = APB2, .line_num = 4 }, [RCC_GPIOD] = { .clk_domain = APB2, .line_num = 5 }, - [RCC_GPIOE] = { .clk_domain = APB2, .line_num = 6 }, // High-density only - [RCC_GPIOF] = { .clk_domain = APB2, .line_num = 7 }, // High-density only - [RCC_GPIOG] = { .clk_domain = APB2, .line_num = 8 }, // High-density only [RCC_AFIO] = { .clk_domain = APB2, .line_num = 0 }, [RCC_ADC1] = { .clk_domain = APB2, .line_num = 9 }, [RCC_ADC2] = { .clk_domain = APB2, .line_num = 10 }, + [RCC_ADC3] = { .clk_domain = APB2, .line_num = 15 }, [RCC_USART1] = { .clk_domain = APB2, .line_num = 14 }, [RCC_USART2] = { .clk_domain = APB1, .line_num = 17 }, [RCC_USART3] = { .clk_domain = APB1, .line_num = 18 }, - [RCC_UART4] = { .clk_domain = APB1, .line_num = 19 }, // High-density only - [RCC_UART5] = { .clk_domain = APB1, .line_num = 20 }, // High-density only [RCC_TIMER1] = { .clk_domain = APB2, .line_num = 11 }, [RCC_TIMER2] = { .clk_domain = APB1, .line_num = 0 }, [RCC_TIMER3] = { .clk_domain = APB1, .line_num = 1 }, [RCC_TIMER4] = { .clk_domain = APB1, .line_num = 2 }, - [RCC_TIMER5] = { .clk_domain = APB1, .line_num = 3 }, // High-density only - [RCC_TIMER6] = { .clk_domain = APB1, .line_num = 4 }, // High-density only - [RCC_TIMER7] = { .clk_domain = APB1, .line_num = 5 }, // High-density only - [RCC_TIMER8] = { .clk_domain = APB2, .line_num = 13 }, // High-density only [RCC_SPI1] = { .clk_domain = APB2, .line_num = 12 }, [RCC_SPI2] = { .clk_domain = APB1, .line_num = 14 }, - [RCC_FSMC] = { .clk_domain = AHB, .line_num = 8 }, // High-density only - [RCC_DAC] = { .clk_domain = APB1, .line_num = 29 }, // High-density only [RCC_DMA1] = { .clk_domain = AHB, .line_num = 0 }, - [RCC_DMA2] = { .clk_domain = AHB, .line_num = 1 }, // High-density only + [RCC_PWR] = { .clk_domain = APB1, .line_num = 28}, + [RCC_BKP] = { .clk_domain = APB1, .line_num = 27}, + [RCC_I2C1] = { .clk_domain = APB1, .line_num = 21 }, + [RCC_I2C2] = { .clk_domain = APB1, .line_num = 22 }, + [RCC_CRC] = { .clk_domain = AHB, .line_num = 6}, + [RCC_FLITF] = { .clk_domain = AHB, .line_num = 4}, + [RCC_SRAM] = { .clk_domain = AHB, .line_num = 2}, +#if defined(STM32_HIGH_DENSITY) || defined(STM32_XL_DENSITY) + [RCC_GPIOE] = { .clk_domain = APB2, .line_num = 6 }, + [RCC_GPIOF] = { .clk_domain = APB2, .line_num = 7 }, + [RCC_GPIOG] = { .clk_domain = APB2, .line_num = 8 }, + [RCC_UART4] = { .clk_domain = APB1, .line_num = 19 }, + [RCC_UART5] = { .clk_domain = APB1, .line_num = 20 }, + [RCC_TIMER5] = { .clk_domain = APB1, .line_num = 3 }, + [RCC_TIMER6] = { .clk_domain = APB1, .line_num = 4 }, + [RCC_TIMER7] = { .clk_domain = APB1, .line_num = 5 }, + [RCC_TIMER8] = { .clk_domain = APB2, .line_num = 13 }, + [RCC_FSMC] = { .clk_domain = AHB, .line_num = 8 }, + [RCC_DAC] = { .clk_domain = APB1, .line_num = 29 }, + [RCC_DMA2] = { .clk_domain = AHB, .line_num = 1 }, + [RCC_SDIO] = { .clk_domain = AHB, .line_num = 10 }, + [RCC_SPI3] = { .clk_domain = APB1, .line_num = 15 }, +#endif +#ifdef STM32_XL_DENSITY + [RCC_TIMER9] = { .clk_domain = APB2, .line_num = 19 }, + [RCC_TIMER10] = { .clk_domain = APB2, .line_num = 20 }, + [RCC_TIMER11] = { .clk_domain = APB2, .line_num = 21 }, + [RCC_TIMER12] = { .clk_domain = APB1, .line_num = 6 }, + [RCC_TIMER13] = { .clk_domain = APB1, .line_num = 7 }, + [RCC_TIMER14] = { .clk_domain = APB1, .line_num = 8 }, +#endif }; /** @@ -82,52 +102,84 @@ static const struct rcc_dev_info rcc_dev_table[] = { * @param pll_src pll clock source, must be HSE * @param pll_mul pll multiplier */ -void rcc_clk_init(uint32 sysclk_src, uint32 pll_src, uint32 pll_mul) { +void rcc_clk_init(rcc_sysclk_src sysclk_src, + rcc_pllsrc pll_src, + rcc_pll_multiplier pll_mul) { + uint32 cfgr; + uint32 cr; + /* Assume that we're going to clock the chip off the PLL, fed by * the HSE */ ASSERT(sysclk_src == RCC_CLKSRC_PLL && pll_src == RCC_PLLSRC_HSE); - uint32 cfgr = 0; - uint32 cr = RCC_READ_CR(); - - cfgr = (pll_src | pll_mul); - RCC_WRITE_CFGR(cfgr); + RCC_BASE->CFGR = pll_src | pll_mul; - /* Turn on the HSE */ + /* Turn on the HSE */ + /* FIXME WTF why doesn't bit-banding work here? */ + cr = RCC_BASE->CR; cr |= RCC_CR_HSEON; - RCC_WRITE_CR(cr); - while (!(RCC_READ_CR() & RCC_CR_HSERDY)) + RCC_BASE->CR = cr; + while (!(RCC_BASE->CR & RCC_CR_HSERDY)) ; /* Now the PLL */ cr |= RCC_CR_PLLON; - RCC_WRITE_CR(cr); - while (!(RCC_READ_CR() & RCC_CR_PLLRDY)) + RCC_BASE->CR = cr; + while (!(RCC_BASE->CR & RCC_CR_PLLRDY)) ; /* Finally, let's switch over to the PLL */ cfgr &= ~RCC_CFGR_SW; cfgr |= RCC_CFGR_SW_PLL; - RCC_WRITE_CFGR(cfgr); - while ((RCC_READ_CFGR() & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL) + RCC_BASE->CFGR = cfgr; + while ((RCC_BASE->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL) ; } /** * @brief Turn on the clock line on a device - * @param dev_num device to turn on + * @param device Clock ID of the device to turn on. + */ +void rcc_clk_enable(rcc_clk_id device) { + static const __io uint32* enable_regs[] = { + [APB1] = &RCC_BASE->APB1ENR, + [APB2] = &RCC_BASE->APB2ENR, + [AHB] = &RCC_BASE->AHBENR, + }; + + rcc_clk_domain clk_domain = rcc_dev_clk(device); + __io uint32* enr = (__io uint32*)enable_regs[clk_domain]; + uint8 lnum = rcc_dev_table[device].line_num; + + bb_peri_set_bit(enr, lnum, 1); +} + +/** + * @brief reset a device + * @param device Clock ID of the device to reset. */ -void rcc_clk_enable(uint32 dev_num) { - static const uint32 enable_regs[] = { - [APB1] = RCC_APB1ENR, - [APB2] = RCC_APB2ENR, - [AHB] = RCC_AHBENR, +void rcc_reset_dev(rcc_clk_id device) { + static const __io uint32* reset_regs[] = { + [APB1] = &RCC_BASE->APB1RSTR, + [APB2] = &RCC_BASE->APB2RSTR, }; - uint8 clk_domain = rcc_dev_table[dev_num].clk_domain; + rcc_clk_domain clk_domain = rcc_dev_clk(device); + __io void* addr = (__io void*)reset_regs[clk_domain]; + uint8 lnum = rcc_dev_table[device].line_num; + + bb_peri_set_bit(addr, lnum, 1); + bb_peri_set_bit(addr, lnum, 0); +} - __set_bits(enable_regs[clk_domain], BIT(rcc_dev_table[dev_num].line_num)); +/** + * @brief Get a device's clock domain + * @param device Device whose clock domain to return + * @return Device's clock source + */ +rcc_clk_domain rcc_dev_clk(rcc_clk_id device) { + return rcc_dev_table[device].clk_domain; } /** @@ -135,7 +187,7 @@ void rcc_clk_enable(uint32 dev_num) { * @param prescaler prescaler to set * @param divider prescaler divider */ -void rcc_set_prescaler(uint32 prescaler, uint32 divider) { +void rcc_set_prescaler(rcc_prescaler prescaler, uint32 divider) { static const uint32 masks[] = { [RCC_PRESCALER_AHB] = RCC_CFGR_HPRE, [RCC_PRESCALER_APB1] = RCC_CFGR_PPRE1, @@ -144,25 +196,8 @@ void rcc_set_prescaler(uint32 prescaler, uint32 divider) { [RCC_PRESCALER_ADC] = RCC_CFGR_ADCPRE, }; - uint32 cfgr = RCC_READ_CFGR(); - + uint32 cfgr = RCC_BASE->CFGR; cfgr &= ~masks[prescaler]; cfgr |= divider; - RCC_WRITE_CFGR(cfgr); -} - -/** - * @brief reset a device - * @param dev_num device to reset - */ -void rcc_reset_dev(uint32 dev_num) { - static const uint32 reset_regs[] = { - [APB1] = RCC_APB1RSTR, - [APB2] = RCC_APB2RSTR, - }; - - uint8 clk_domain = rcc_dev_table[dev_num].clk_domain; - - __set_bits(reset_regs[clk_domain], BIT(rcc_dev_table[dev_num].line_num)); - __clear_bits(reset_regs[clk_domain], BIT(rcc_dev_table[dev_num].line_num)); + RCC_BASE->CFGR = cfgr; } diff --git a/libmaple/rcc.h b/libmaple/rcc.h index 4b1f35d..f0f026b 100644 --- a/libmaple/rcc.h +++ b/libmaple/rcc.h @@ -27,6 +27,8 @@ * @brief reset and clock control definitions and prototypes */ +#include "libmaple_types.h" + #ifndef _RCC_H_ #define _RCC_H_ @@ -34,154 +36,528 @@ extern "C"{ #endif -/* registers */ -#define RCC_BASE 0x40021000 -#define RCC_CR (RCC_BASE + 0x0) -#define RCC_CFGR (RCC_BASE + 0x4) -#define RCC_CIR (RCC_BASE + 0x8) -#define RCC_APB2RSTR (RCC_BASE + 0xC) -#define RCC_APB1RSTR (RCC_BASE + 0x10) -#define RCC_AHBENR (RCC_BASE + 0x14) -#define RCC_APB2ENR (RCC_BASE + 0x18) -#define RCC_APB1ENR (RCC_BASE + 0x1C) -#define RCC_BDCR (RCC_BASE + 0x20) -#define RCC_CSR (RCC_BASE + 0x24) -#define RCC_AHBSTR (RCC_BASE + 0x28) -#define RCC_CFGR2 (RCC_BASE + 0x2C) - -#define RCC_CFGR_USBPRE (0x1 << 22) -#define RCC_CFGR_ADCPRE (0x3 << 14) -#define RCC_CFGR_PPRE1 (0x7 << 8) -#define RCC_CFGR_PPRE2 (0x7 << 11) -#define RCC_CFGR_HPRE (0xF << 4) -#define RCC_CFGR_PLLSRC (0x1 << 16) - -#define RCC_CFGR_SWS (0x3 << 2) -#define RCC_CFGR_SWS_PLL (0x2 << 2) -#define RCC_CFGR_SWS_HSE (0x1 << 2) - -#define RCC_CFGR_SW (0x3 << 0) -#define RCC_CFGR_SW_PLL (0x2 << 0) -#define RCC_CFGR_SW_HSE (0x1 << 0) - -/* CR status bits */ -#define RCC_CR_HSEON (0x1 << 16) -#define RCC_CR_HSERDY (0x1 << 17) -#define RCC_CR_PLLON (0x1 << 24) -#define RCC_CR_PLLRDY (0x1 << 25) - -#define RCC_WRITE_CFGR(val) __write(RCC_CFGR, val) -#define RCC_READ_CFGR() __read(RCC_CFGR) - -#define RCC_WRITE_CR(val) __write(RCC_CR, val) -#define RCC_READ_CR() __read(RCC_CR) - -/* sysclk source */ -#define RCC_CLKSRC_HSI (0x0) -#define RCC_CLKSRC_HSE (0x1) -#define RCC_CLKSRC_PLL (0x2) - -/* pll entry clock source */ -#define RCC_PLLSRC_HSE (0x1 << 16) -#define RCC_PLLSRC_HSI_DIV_2 (0x0 << 16) - -/* adc prescaler dividers */ -#define RCC_ADCPRE_PCLK_DIV_2 (0x0 << 14) -#define RCC_ADCPRE_PCLK_DIV_4 (0x1 << 14) -#define RCC_ADCPRE_PCLK_DIV_6 (0x2 << 14) -#define RCC_ADCPRE_PCLK_DIV_8 (0x3 << 14) - -/* apb1 prescaler dividers */ -#define RCC_APB1_HCLK_DIV_1 (0x0 << 8) -#define RCC_APB1_HCLK_DIV_2 (0x4 << 8) -#define RCC_APB1_HCLK_DIV_4 (0x5 << 8) -#define RCC_APB1_HCLK_DIV_8 (0x6 << 8) -#define RCC_APB1_HCLK_DIV_16 (0x7 << 8) - -/* apb2 prescaler dividers */ -#define RCC_APB2_HCLK_DIV_1 (0x0 << 11) -#define RCC_APB2_HCLK_DIV_2 (0x4 << 11) -#define RCC_APB2_HCLK_DIV_4 (0x5 << 11) -#define RCC_APB2_HCLK_DIV_8 (0x6 << 11) -#define RCC_APB2_HCLK_DIV_16 (0x7 << 11) - -/* ahb prescaler dividers */ -#define RCC_AHB_SYSCLK_DIV_1 (0x0 << 4) -#define RCC_AHB_SYSCLK_DIV_2 (0x8 << 4) -#define RCC_AHB_SYSCLK_DIV_4 (0x9 << 4) -#define RCC_AHB_SYSCLK_DIV_8 (0xA << 4) -#define RCC_AHB_SYSCLK_DIV_16 (0xB << 4) -#define RCC_AHB_SYSCLK_DIV_32 (0xC << 4) -#define RCC_AHB_SYSCLK_DIV_64 (0xD << 4) -#define RCC_AHB_SYSCLK_DIV_128 (0xD << 4) -#define RCC_AHB_SYSCLK_DIV_256 (0xE << 4) -#define RCC_AHB_SYSCLK_DIV_512 (0xF << 4) - -/* pll multipliers */ -#define RCC_PLLMUL_2 (0x0 << 18) -#define RCC_PLLMUL_3 (0x1 << 18) -#define RCC_PLLMUL_4 (0x2 << 18) -#define RCC_PLLMUL_5 (0x3 << 18) -#define RCC_PLLMUL_6 (0x4 << 18) -#define RCC_PLLMUL_7 (0x5 << 18) -#define RCC_PLLMUL_8 (0x6 << 18) -#define RCC_PLLMUL_9 (0x7 << 18) -#define RCC_PLLMUL_10 (0x8 << 18) -#define RCC_PLLMUL_11 (0x9 << 18) -#define RCC_PLLMUL_12 (0xA << 18) -#define RCC_PLLMUL_13 (0xB << 18) -#define RCC_PLLMUL_14 (0xC << 18) -#define RCC_PLLMUL_15 (0xD << 18) -#define RCC_PLLMUL_16 (0xE << 18) - - -/* prescalers */ -enum { - RCC_PRESCALER_AHB, - RCC_PRESCALER_APB1, - RCC_PRESCALER_APB2, - RCC_PRESCALER_USB, - RCC_PRESCALER_ADC -}; +/** RCC register map type */ +typedef struct rcc_reg_map { + __io uint32 CR; /**< Clock control register */ + __io uint32 CFGR; /**< Clock configuration register */ + __io uint32 CIR; /**< Clock interrupt register */ + __io uint32 APB2RSTR; /**< APB2 peripheral reset register */ + __io uint32 APB1RSTR; /**< APB1 peripheral reset register */ + __io uint32 AHBENR; /**< AHB peripheral clock enable register */ + __io uint32 APB2ENR; /**< APB2 peripheral clock enable register */ + __io uint32 APB1ENR; /**< APB1 peripheral clock enable register */ + __io uint32 BDCR; /**< Backup domain control register */ + __io uint32 CSR; /**< Control/status register */ +} rcc_reg_map; + +/** RCC register map base pointer */ +#define RCC_BASE ((rcc_reg_map*)0x40021000) + +/* + * Register bit definitions + */ + +/* Clock control register */ + +#define RCC_CR_PLLRDY_BIT 25 +#define RCC_CR_PLLON_BIT 24 +#define RCC_CR_CSSON_BIT 19 +#define RCC_CR_HSEBYP_BIT 18 +#define RCC_CR_HSERDY_BIT 17 +#define RCC_CR_HSEON_BIT 16 +#define RCC_CR_HSIRDY_BIT 1 +#define RCC_CR_HSION_BIT 0 + +#define RCC_CR_PLLRDY BIT(RCC_CR_PLLRDY_BIT) +#define RCC_CR_PLLON BIT(RCC_CR_PLLON_BIT) +#define RCC_CR_CSSON BIT(RCC_CR_CSSON_BIT) +#define RCC_CR_HSEBYP BIT(RCC_CR_HSEBYP_BIT) +#define RCC_CR_HSERDY BIT(RCC_CR_HSERDY_BIT) +#define RCC_CR_HSEON BIT(RCC_CR_HSEON_BIT) +#define RCC_CR_HSICAL (0xFF << 8) +#define RCC_CR_HSITRIM (0x1F << 3) +#define RCC_CR_HSIRDY BIT(RCC_CR_HSIRDY_BIT) +#define RCC_CR_HSION BIT(RCC_CR_HSION_BIT) + +/* Clock configuration register */ + +#define RCC_CFGR_USBPRE_BIT 22 +#define RCC_CFGR_PLLXTPRE_BIT 17 +#define RCC_CFGR_PLLSRC_BIT 16 + +#define RCC_CFGR_MCO (0x3 << 24) +#define RCC_CFGR_USBPRE BIT(RCC_CFGR_USBPRE_BIT) +#define RCC_CFGR_PLLMUL (0xF << 18) +#define RCC_CFGR_PLLXTPRE BIT(RCC_CFGR_PLLXTPRE_BIT) +#define RCC_CFGR_PLLSRC BIT(RCC_CFGR_PLLSRC_BIT) +#define RCC_CFGR_ADCPRE (0x3 << 14) +#define RCC_CFGR_PPRE2 (0x7 << 11) +#define RCC_CFGR_PPRE1 (0x7 << 8) +#define RCC_CFGR_HPRE (0xF << 4) +#define RCC_CFGR_SWS (0x3 << 2) +#define RCC_CFGR_SWS_PLL (0x2 << 2) +#define RCC_CFGR_SWS_HSE (0x1 << 2) +#define RCC_CFGR_SW 0x3 +#define RCC_CFGR_SW_PLL 0x2 +#define RCC_CFGR_SW_HSE 0x1 + +/* Clock interrupt register */ + +#define RCC_CIR_CSSC_BIT 23 +#define RCC_CIR_PLLRDYC_BIT 20 +#define RCC_CIR_HSERDYC_BIT 19 +#define RCC_CIR_HSIRDYC_BIT 18 +#define RCC_CIR_LSERDYC_BIT 17 +#define RCC_CIR_LSIRDYC_BIT 16 +#define RCC_CIR_PLLRDYIE_BIT 12 +#define RCC_CIR_HSERDYIE_BIT 11 +#define RCC_CIR_HSIRDYIE_BIT 10 +#define RCC_CIR_LSERDYIE_BIT 9 +#define RCC_CIR_LSIRDYIE_BIT 8 +#define RCC_CIR_CSSF_BIT 7 +#define RCC_CIR_PLLRDYF_BIT 4 +#define RCC_CIR_HSERDYF_BIT 3 +#define RCC_CIR_HSIRDYF_BIT 2 +#define RCC_CIR_LSERDYF_BIT 1 +#define RCC_CIR_LSIRDYF_BIT 0 + +#define RCC_CIR_CSSC BIT(RCC_CIR_CSSC_BIT) +#define RCC_CIR_PLLRDYC BIT(RCC_CIR_PLLRDYC_BIT) +#define RCC_CIR_HSERDYC BIT(RCC_CIR_HSERDYC_BIT) +#define RCC_CIR_HSIRDYC BIT(RCC_CIR_HSIRDYC_BIT) +#define RCC_CIR_LSERDYC BIT(RCC_CIR_LSERDYC_BIT) +#define RCC_CIR_LSIRDYC BIT(RCC_CIR_LSIRDYC_BIT) +#define RCC_CIR_PLLRDYIE BIT(RCC_CIR_PLLRDYIE_BIT) +#define RCC_CIR_HSERDYIE BIT(RCC_CIR_HSERDYIE_BIT) +#define RCC_CIR_HSIRDYIE BIT(RCC_CIR_HSIRDYIE_BIT) +#define RCC_CIR_LSERDYIE BIT(RCC_CIR_LSERDYIE_BIT) +#define RCC_CIR_LSIRDYIE BIT(RCC_CIR_LSIRDYIE_BIT) +#define RCC_CIR_CSSF BIT(RCC_CIR_CSSF_BIT) +#define RCC_CIR_PLLRDYF BIT(RCC_CIR_PLLRDYF_BIT) +#define RCC_CIR_HSERDYF BIT(RCC_CIR_HSERDYF_BIT) +#define RCC_CIR_HSIRDYF BIT(RCC_CIR_HSIRDYF_BIT) +#define RCC_CIR_LSERDYF BIT(RCC_CIR_LSERDYF_BIT) +#define RCC_CIR_LSIRDYF BIT(RCC_CIR_LSIRDYF_BIT) + +/* APB2 peripheral reset register */ + +#define RCC_APB2RSTR_TIM11RST_BIT 21 +#define RCC_APB2RSTR_TIM10RST_BIT 20 +#define RCC_APB2RSTR_TIM9RST_BIT 19 +#define RCC_APB2RSTR_ADC3RST_BIT 15 +#define RCC_APB2RSTR_USART1RST_BIT 14 +#define RCC_APB2RSTR_TIM8RST_BIT 13 +#define RCC_APB2RSTR_SPI1RST_BIT 12 +#define RCC_APB2RSTR_TIM1RST_BIT 11 +#define RCC_APB2RSTR_ADC2RST_BIT 10 +#define RCC_APB2RSTR_ADC1RST_BIT 9 +#define RCC_APB2RSTR_IOPGRST_BIT 8 +#define RCC_APB2RSTR_IOPFRST_BIT 7 +#define RCC_APB2RSTR_IOPERST_BIT 6 +#define RCC_APB2RSTR_IOPDRST_BIT 5 +#define RCC_APB2RSTR_IOPCRST_BIT 4 +#define RCC_APB2RSTR_IOPBRST_BIT 3 +#define RCC_APB2RSTR_IOPARST_BIT 2 +#define RCC_APB2RSTR_AFIORST_BIT 0 + +#define RCC_APB2RSTR_TIM11RST BIT(RCC_APB2RSTR_TIM11RST_BIT) +#define RCC_APB2RSTR_TIM10RST BIT(RCC_APB2RSTR_TIM10RST_BIT) +#define RCC_APB2RSTR_TIM9RST BIT(RCC_APB2RSTR_TIM9RST_BIT) +#define RCC_APB2RSTR_ADC3RST BIT(RCC_APB2RSTR_ADC3RST_BIT) +#define RCC_APB2RSTR_USART1RST BIT(RCC_APB2RSTR_USART1RST_BIT) +#define RCC_APB2RSTR_TIM8RST BIT(RCC_APB2RSTR_TIM8RST_BIT) +#define RCC_APB2RSTR_SPI1RST BIT(RCC_APB2RSTR_SPI1RST_BIT) +#define RCC_APB2RSTR_TIM1RST BIT(RCC_APB2RSTR_TIM1RST_BIT) +#define RCC_APB2RSTR_ADC2RST BIT(RCC_APB2RSTR_ADC2RST_BIT) +#define RCC_APB2RSTR_ADC1RST BIT(RCC_APB2RSTR_ADC1RST_BIT) +#define RCC_APB2RSTR_IOPGRST BIT(RCC_APB2RSTR_IOPGRST_BIT) +#define RCC_APB2RSTR_IOPFRST BIT(RCC_APB2RSTR_IOPFRST_BIT) +#define RCC_APB2RSTR_IOPERST BIT(RCC_APB2RSTR_IOPERST_BIT) +#define RCC_APB2RSTR_IOPDRST BIT(RCC_APB2RSTR_IOPDRST_BIT) +#define RCC_APB2RSTR_IOPCRST BIT(RCC_APB2RSTR_IOPCRST_BIT) +#define RCC_APB2RSTR_IOPBRST BIT(RCC_APB2RSTR_IOPBRST_BIT) +#define RCC_APB2RSTR_IOPARST BIT(RCC_APB2RSTR_IOPARST_BIT) +#define RCC_APB2RSTR_AFIORST BIT(RCC_APB2RSTR_AFIORST_BIT) + +/* APB1 peripheral reset register */ + +#define RCC_APB1RSTR_DACRST_BIT 29 +#define RCC_APB1RSTR_PWRRST_BIT 28 +#define RCC_APB1RSTR_BKPRST_BIT 27 +#define RCC_APB1RSTR_CANRST_BIT 25 +#define RCC_APB1RSTR_USBRST_BIT 23 +#define RCC_APB1RSTR_I2C2RST_BIT 22 +#define RCC_APB1RSTR_I2C1RST_BIT 21 +#define RCC_APB1RSTR_UART5RST_BIT 20 +#define RCC_APB1RSTR_UART4RST_BIT 19 +#define RCC_APB1RSTR_USART3RST_BIT 18 +#define RCC_APB1RSTR_USART2RST_BIT 17 +#define RCC_APB1RSTR_SPI3RST_BIT 15 +#define RCC_APB1RSTR_SPI2RST_BIT 14 +#define RCC_APB1RSTR_WWDRST_BIT 11 +#define RCC_APB1RSTR_TIM14RST_BIT 8 +#define RCC_APB1RSTR_TIM13RST_BIT 7 +#define RCC_APB1RSTR_TIM12RST_BIT 6 +#define RCC_APB1RSTR_TIM7RST_BIT 5 +#define RCC_APB1RSTR_TIM6RST_BIT 4 +#define RCC_APB1RSTR_TIM5RST_BIT 3 +#define RCC_APB1RSTR_TIM4RST_BIT 2 +#define RCC_APB1RSTR_TIM3RST_BIT 1 +#define RCC_APB1RSTR_TIM2RST_BIT 0 + +#define RCC_APB1RSTR_DACRST BIT(RCC_APB1RSTR_DACRST_BIT) +#define RCC_APB1RSTR_PWRRST BIT(RCC_APB1RSTR_PWRRST_BIT) +#define RCC_APB1RSTR_BKPRST BIT(RCC_APB1RSTR_BKPRST_BIT) +#define RCC_APB1RSTR_CANRST BIT(RCC_APB1RSTR_CANRST_BIT) +#define RCC_APB1RSTR_USBRST BIT(RCC_APB1RSTR_USBRST_BIT) +#define RCC_APB1RSTR_I2C2RST BIT(RCC_APB1RSTR_I2C2RST_BIT) +#define RCC_APB1RSTR_I2C1RST BIT(RCC_APB1RSTR_I2C1RST_BIT) +#define RCC_APB1RSTR_UART5RST BIT(RCC_APB1RSTR_UART5RST_BIT) +#define RCC_APB1RSTR_UART4RST BIT(RCC_APB1RSTR_UART4RST_BIT) +#define RCC_APB1RSTR_USART3RST BIT(RCC_APB1RSTR_USART3RST_BIT) +#define RCC_APB1RSTR_USART2RST BIT(RCC_APB1RSTR_USART2RST_BIT) +#define RCC_APB1RSTR_SPI3RST BIT(RCC_APB1RSTR_SPI3RST_BIT) +#define RCC_APB1RSTR_SPI2RST BIT(RCC_APB1RSTR_SPI2RST_BIT) +#define RCC_APB1RSTR_WWDRST BIT(RCC_APB1RSTR_WWDRST_BIT) +#define RCC_APB1RSTR_TIM14RST BIT(RCC_APB1RSTR_TIM14RST_BIT) +#define RCC_APB1RSTR_TIM13RST BIT(RCC_APB1RSTR_TIM13RST_BIT) +#define RCC_APB1RSTR_TIM12RST BIT(RCC_APB1RSTR_TIM12RST_BIT) +#define RCC_APB1RSTR_TIM7RST BIT(RCC_APB1RSTR_TIM7RST_BIT) +#define RCC_APB1RSTR_TIM6RST BIT(RCC_APB1RSTR_TIM6RST_BIT) +#define RCC_APB1RSTR_TIM5RST BIT(RCC_APB1RSTR_TIM5RST_BIT) +#define RCC_APB1RSTR_TIM4RST BIT(RCC_APB1RSTR_TIM4RST_BIT) +#define RCC_APB1RSTR_TIM3RST BIT(RCC_APB1RSTR_TIM3RST_BIT) +#define RCC_APB1RSTR_TIM2RST BIT(RCC_APB1RSTR_TIM2RST_BIT) + +/* AHB peripheral clock enable register */ + +#define RCC_AHBENR_SDIOEN_BIT 10 +#define RCC_AHBENR_FSMCEN_BIT 8 +#define RCC_AHBENR_CRCEN_BIT 7 +#define RCC_AHBENR_FLITFEN_BIT 4 +#define RCC_AHBENR_SRAMEN_BIT 2 +#define RCC_AHBENR_DMA2EN_BIT 1 +#define RCC_AHBENR_DMA1EN_BIT 0 + +#define RCC_AHBENR_SDIOEN BIT(RCC_AHBENR_SDIOEN_BIT) +#define RCC_AHBENR_FSMCEN BIT(RCC_AHBENR_FSMCEN_BIT) +#define RCC_AHBENR_CRCEN BIT(RCC_AHBENR_CRCEN_BIT) +#define RCC_AHBENR_FLITFEN BIT(RCC_AHBENR_FLITFEN_BIT) +#define RCC_AHBENR_SRAMEN BIT(RCC_AHBENR_SRAMEN_BIT) +#define RCC_AHBENR_DMA2EN BIT(RCC_AHBENR_DMA2EN_BIT) +#define RCC_AHBENR_DMA1EN BIT(RCC_AHBENR_DMA1EN_BIT) + +/* APB2 peripheral clock enable register */ + +#define RCC_APB2ENR_TIM11EN_BIT 21 +#define RCC_APB2ENR_TIM10EN_BIT 20 +#define RCC_APB2ENR_TIM9EN_BIT 19 +#define RCC_APB2ENR_ADC3EN_BIT 15 +#define RCC_APB2ENR_USART1EN_BIT 14 +#define RCC_APB2ENR_TIM8EN_BIT 13 +#define RCC_APB2ENR_SPI1EN_BIT 12 +#define RCC_APB2ENR_TIM1EN_BIT 11 +#define RCC_APB2ENR_ADC2EN_BIT 10 +#define RCC_APB2ENR_ADC1EN_BIT 9 +#define RCC_APB2ENR_IOPGEN_BIT 8 +#define RCC_APB2ENR_IOPFEN_BIT 7 +#define RCC_APB2ENR_IOPEEN_BIT 6 +#define RCC_APB2ENR_IOPDEN_BIT 5 +#define RCC_APB2ENR_IOPCEN_BIT 4 +#define RCC_APB2ENR_IOPBEN_BIT 3 +#define RCC_APB2ENR_IOPAEN_BIT 2 +#define RCC_APB2ENR_AFIOEN_BIT 0 + +#define RCC_APB2ENR_TIM11EN BIT(RCC_APB2ENR_TIM11EN_BIT) +#define RCC_APB2ENR_TIM10EN BIT(RCC_APB2ENR_TIM10EN_BIT) +#define RCC_APB2ENR_TIM9EN BIT(RCC_APB2ENR_TIM9EN_BIT) +#define RCC_APB2ENR_ADC3EN BIT(RCC_APB2ENR_ADC3EN_BIT) +#define RCC_APB2ENR_USART1EN BIT(RCC_APB2ENR_USART1EN_BIT) +#define RCC_APB2ENR_TIM8EN BIT(RCC_APB2ENR_TIM8EN_BIT) +#define RCC_APB2ENR_SPI1EN BIT(RCC_APB2ENR_SPI1EN_BIT) +#define RCC_APB2ENR_TIM1EN BIT(RCC_APB2ENR_TIM1EN_BIT) +#define RCC_APB2ENR_ADC2EN BIT(RCC_APB2ENR_ADC2EN_BIT) +#define RCC_APB2ENR_ADC1EN BIT(RCC_APB2ENR_ADC1EN_BIT) +#define RCC_APB2ENR_IOPGEN BIT(RCC_APB2ENR_IOPGEN_BIT) +#define RCC_APB2ENR_IOPFEN BIT(RCC_APB2ENR_IOPFEN_BIT) +#define RCC_APB2ENR_IOPEEN BIT(RCC_APB2ENR_IOPEEN_BIT) +#define RCC_APB2ENR_IOPDEN BIT(RCC_APB2ENR_IOPDEN_BIT) +#define RCC_APB2ENR_IOPCEN BIT(RCC_APB2ENR_IOPCEN_BIT) +#define RCC_APB2ENR_IOPBEN BIT(RCC_APB2ENR_IOPBEN_BIT) +#define RCC_APB2ENR_IOPAEN BIT(RCC_APB2ENR_IOPAEN_BIT) +#define RCC_APB2ENR_AFIOEN BIT(RCC_APB2ENR_AFIOEN_BIT) + +/* APB1 peripheral clock enable register */ + +#define RCC_APB1ENR_DACEN_BIT 29 +#define RCC_APB1ENR_PWREN_BIT 28 +#define RCC_APB1ENR_BKPEN_BIT 27 +#define RCC_APB1ENR_CANEN_BIT 25 +#define RCC_APB1ENR_USBEN_BIT 23 +#define RCC_APB1ENR_I2C2EN_BIT 22 +#define RCC_APB1ENR_I2C1EN_BIT 21 +#define RCC_APB1ENR_UART5EN_BIT 20 +#define RCC_APB1ENR_UART4EN_BIT 19 +#define RCC_APB1ENR_USART3EN_BIT 18 +#define RCC_APB1ENR_USART2EN_BIT 17 +#define RCC_APB1ENR_SPI3EN_BIT 15 +#define RCC_APB1ENR_SPI2EN_BIT 14 +#define RCC_APB1ENR_WWDEN_BIT 11 +#define RCC_APB1ENR_TIM14EN_BIT 8 +#define RCC_APB1ENR_TIM13EN_BIT 7 +#define RCC_APB1ENR_TIM12EN_BIT 6 +#define RCC_APB1ENR_TIM7EN_BIT 5 +#define RCC_APB1ENR_TIM6EN_BIT 4 +#define RCC_APB1ENR_TIM5EN_BIT 3 +#define RCC_APB1ENR_TIM4EN_BIT 2 +#define RCC_APB1ENR_TIM3EN_BIT 1 +#define RCC_APB1ENR_TIM2EN_BIT 0 + +#define RCC_APB1ENR_DACEN BIT(RCC_APB1ENR_DACEN_BIT) +#define RCC_APB1ENR_PWREN BIT(RCC_APB1ENR_PWREN_BIT) +#define RCC_APB1ENR_BKPEN BIT(RCC_APB1ENR_BKPEN_BIT) +#define RCC_APB1ENR_CANEN BIT(RCC_APB1ENR_CANEN_BIT) +#define RCC_APB1ENR_USBEN BIT(RCC_APB1ENR_USBEN_BIT) +#define RCC_APB1ENR_I2C2EN BIT(RCC_APB1ENR_I2C2EN_BIT) +#define RCC_APB1ENR_I2C1EN BIT(RCC_APB1ENR_I2C1EN_BIT) +#define RCC_APB1ENR_UART5EN BIT(RCC_APB1ENR_UART5EN_BIT) +#define RCC_APB1ENR_UART4EN BIT(RCC_APB1ENR_UART4EN_BIT) +#define RCC_APB1ENR_USART3EN BIT(RCC_APB1ENR_USART3EN_BIT) +#define RCC_APB1ENR_USART2EN BIT(RCC_APB1ENR_USART2EN_BIT) +#define RCC_APB1ENR_SPI3EN BIT(RCC_APB1ENR_SPI3EN_BIT) +#define RCC_APB1ENR_SPI2EN BIT(RCC_APB1ENR_SPI2EN_BIT) +#define RCC_APB1ENR_WWDEN BIT(RCC_APB1ENR_WWDEN_BIT) +#define RCC_APB1ENR_TIM14EN BIT(RCC_APB1ENR_TIM14EN_BIT) +#define RCC_APB1ENR_TIM13EN BIT(RCC_APB1ENR_TIM13EN_BIT) +#define RCC_APB1ENR_TIM12EN BIT(RCC_APB1ENR_TIM12EN_BIT) +#define RCC_APB1ENR_TIM7EN BIT(RCC_APB1ENR_TIM7EN_BIT) +#define RCC_APB1ENR_TIM6EN BIT(RCC_APB1ENR_TIM6EN_BIT) +#define RCC_APB1ENR_TIM5EN BIT(RCC_APB1ENR_TIM5EN_BIT) +#define RCC_APB1ENR_TIM4EN BIT(RCC_APB1ENR_TIM4EN_BIT) +#define RCC_APB1ENR_TIM3EN BIT(RCC_APB1ENR_TIM3EN_BIT) +#define RCC_APB1ENR_TIM2EN BIT(RCC_APB1ENR_TIM2EN_BIT) + +/* Backup domain control register */ + +#define RCC_BDCR_BDRST_BIT 16 +#define RCC_BDCR_RTCEN_BIT 15 +#define RCC_BDCR_LSEBYP_BIT 2 +#define RCC_BDCR_LSERDY_BIT 1 +#define RCC_BDCR_LSEON_BIT 0 + +#define RCC_BDCR_BDRST BIT(RCC_BDCR_BDRST_BIT) +#define RCC_BDCR_RTCEN BIT(RCC_BDCR_RTC_BIT) +#define RCC_BDCR_RTCSEL (0x3 << 8) +#define RCC_BDCR_RTCSEL_NONE (0x0 << 8) +#define RCC_BDCR_RTCSEL_LSE (0x1 << 8) +#define RCC_BDCR_RTCSEL_HSE (0x3 << 8) +#define RCC_BDCR_LSEBYP BIT(RCC_BDCR_LSEBYP_BIT) +#define RCC_BDCR_LSERDY BIT(RCC_BDCR_LSERDY_BIT) +#define RCC_BDCR_LSEON BIT(RCC_BDCR_LSEON_BIT) + +/* Control/status register */ + +#define RCC_CSR_LPWRRSTF_BIT 31 +#define RCC_CSR_WWDGRSTF_BIT 30 +#define RCC_CSR_IWDGRSTF_BIT 29 +#define RCC_CSR_SFTRSTF_BIT 28 +#define RCC_CSR_PORRSTF_BIT 27 +#define RCC_CSR_PINRSTF_BIT 26 +#define RCC_CSR_RMVF_BIT 24 +#define RCC_CSR_LSIRDY_BIT 1 +#define RCC_CSR_LSION_BIT 0 + +#define RCC_CSR_LPWRRSTF BIT(RCC_CSR_LPWRRSTF_BIT) +#define RCC_CSR_WWDGRSTF BIT(RCC_CSR_WWDGRSTF_BIT) +#define RCC_CSR_IWDGRSTF BIT(RCC_CSR_IWDGRSTF_BIT) +#define RCC_CSR_SFTRSTF BIT(RCC_CSR_SFTRSTF_BIT) +#define RCC_CSR_PORRSTF BIT(RCC_CSR_PORRSTF_BIT) +#define RCC_CSR_PINRSTF BIT(RCC_CSR_PINRSTF_BIT) +#define RCC_CSR_RMVF BIT(RCC_CSR_RMVF_BIT) +#define RCC_CSR_LSIRDY BIT(RCC_CSR_LSIRDY_BIT) +#define RCC_CSR_LSION BIT(RCC_CSR_LSION_BIT) -// RCC Devices -enum { +/* + * Convenience routines + */ + +/** + * SYSCLK sources + * @see rcc_clk_init() + */ +typedef enum rcc_sysclk_src { + RCC_CLKSRC_HSI = 0x0, + RCC_CLKSRC_HSE = 0x1, + RCC_CLKSRC_PLL = 0x2, +} rcc_sysclk_src; + +/** + * PLL entry clock source + * @see rcc_clk_init() + */ +typedef enum rcc_pllsrc { + RCC_PLLSRC_HSE = (0x1 << 16), + RCC_PLLSRC_HSI_DIV_2 = (0x0 << 16) +} rcc_pllsrc; + +/** + * PLL multipliers + * @see rcc_clk_init() + */ +typedef enum rcc_pll_multiplier { + RCC_PLLMUL_2 = (0x0 << 18), + RCC_PLLMUL_3 = (0x1 << 18), + RCC_PLLMUL_4 = (0x2 << 18), + RCC_PLLMUL_5 = (0x3 << 18), + RCC_PLLMUL_6 = (0x4 << 18), + RCC_PLLMUL_7 = (0x5 << 18), + RCC_PLLMUL_8 = (0x6 << 18), + RCC_PLLMUL_9 = (0x7 << 18), + RCC_PLLMUL_10 = (0x8 << 18), + RCC_PLLMUL_11 = (0x9 << 18), + RCC_PLLMUL_12 = (0xA << 18), + RCC_PLLMUL_13 = (0xB << 18), + RCC_PLLMUL_14 = (0xC << 18), + RCC_PLLMUL_15 = (0xD << 18), + RCC_PLLMUL_16 = (0xE << 18), +} rcc_pll_multiplier; + +/** + * @brief Identifies bus and clock line for a device + * + * Also generally useful as a unique identifier for that device. + */ +typedef enum { RCC_GPIOA, RCC_GPIOB, RCC_GPIOC, RCC_GPIOD, - RCC_GPIOE, // High-density devices only (Maple Native) - RCC_GPIOF, // High-density devices only (Maple Native) - RCC_GPIOG, // High-density devices only (Maple Native) RCC_AFIO, RCC_ADC1, RCC_ADC2, + RCC_ADC3, RCC_USART1, RCC_USART2, RCC_USART3, - RCC_UART4, // High-density devices only (Maple Native) - RCC_UART5, // High-density devices only (Maple Native) RCC_TIMER1, RCC_TIMER2, RCC_TIMER3, RCC_TIMER4, - RCC_TIMER5, // High-density devices only (Maple Native) - RCC_TIMER6, // High-density devices only (Maple Native) - RCC_TIMER7, // High-density devices only (Maple Native) - RCC_TIMER8, // High-density devices only (Maple Native) RCC_SPI1, RCC_SPI2, - RCC_FSMC, // High-density devices only (Maple Native) - RCC_DAC, // High-density devices only (Maple Native) RCC_DMA1, - RCC_DMA2, // High-density devices only (Maple Native) -}; + RCC_PWR, + RCC_BKP, + RCC_I2C1, + RCC_I2C2, + RCC_CRC, + RCC_FLITF, + RCC_SRAM, +#if defined(STM32_HIGH_DENSITY) || defined(STM32_XL_DENSITY) + RCC_GPIOE, + RCC_GPIOF, + RCC_GPIOG, + RCC_UART4, + RCC_UART5, + RCC_TIMER5, + RCC_TIMER6, + RCC_TIMER7, + RCC_TIMER8, + RCC_FSMC, + RCC_DAC, + RCC_DMA2, + RCC_SDIO, + RCC_SPI3, +#endif +#ifdef STM32_XL_DENSITY + RCC_TIMER9, + RCC_TIMER10, + RCC_TIMER11, + RCC_TIMER12, + RCC_TIMER13, + RCC_TIMER14, +#endif +} rcc_clk_id; + +void rcc_clk_init(rcc_sysclk_src sysclk_src, + rcc_pllsrc pll_src, + rcc_pll_multiplier pll_mul); +void rcc_clk_enable(rcc_clk_id device); +void rcc_reset_dev(rcc_clk_id device); + +typedef enum rcc_clk_domain { + RCC_APB1, + RCC_APB2, + RCC_AHB +} rcc_clk_domain; + +rcc_clk_domain rcc_dev_clk(rcc_clk_id device); +/** + * Prescaler identifiers + * @see rcc_set_prescaler() + */ +typedef enum rcc_prescaler { + RCC_PRESCALER_AHB, + RCC_PRESCALER_APB1, + RCC_PRESCALER_APB2, + RCC_PRESCALER_USB, + RCC_PRESCALER_ADC +} rcc_prescaler; + +/** + * ADC prescaler dividers + * @see rcc_set_prescaler() + */ +typedef enum adc_prescaler_divider { + RCC_ADCPRE_PCLK_DIV_2 = 0x0 << 14, + RCC_ADCPRE_PCLK_DIV_4 = 0x1 << 14, + RCC_ADCPRE_PCLK_DIV_6 = 0x2 << 14, + RCC_ADCPRE_PCLK_DIV_8 = 0x3 << 14, +} adc_prescaler_divider; + +/** + * APB1 prescaler dividers + * @see rcc_set_prescaler() + */ +typedef enum apb1_prescaler_divider { + RCC_APB1_HCLK_DIV_1 = 0x0 << 8, + RCC_APB1_HCLK_DIV_2 = 0x4 << 8, + RCC_APB1_HCLK_DIV_4 = 0x5 << 8, + RCC_APB1_HCLK_DIV_8 = 0x6 << 8, + RCC_APB1_HCLK_DIV_16 = 0x7 << 8, +} apb1_prescaler_divider; + +/** + * APB2 prescaler dividers + * @see rcc_set_prescaler() + */ +typedef enum apb2_prescaler_divider { + RCC_APB2_HCLK_DIV_1 = 0x0 << 11, + RCC_APB2_HCLK_DIV_2 = 0x4 << 11, + RCC_APB2_HCLK_DIV_4 = 0x5 << 11, + RCC_APB2_HCLK_DIV_8 = 0x6 << 11, + RCC_APB2_HCLK_DIV_16 = 0x7 << 11, +} apb2_prescaler_divider; + +/** + * AHB prescaler dividers + * @see rcc_set_prescaler() + */ +typedef enum ahb_prescaler_divider { + RCC_AHB_SYSCLK_DIV_1 = 0x0 << 4, + RCC_AHB_SYSCLK_DIV_2 = 0x8 << 4, + RCC_AHB_SYSCLK_DIV_4 = 0x9 << 4, + RCC_AHB_SYSCLK_DIV_8 = 0xA << 4, + RCC_AHB_SYSCLK_DIV_16 = 0xB << 4, + RCC_AHB_SYSCLK_DIV_32 = 0xC << 4, + RCC_AHB_SYSCLK_DIV_64 = 0xD << 4, + RCC_AHB_SYSCLK_DIV_128 = 0xD << 4, + RCC_AHB_SYSCLK_DIV_256 = 0xE << 4, + RCC_AHB_SYSCLK_DIV_512 = 0xF << 4, +} ahb_prescaler_divider; -void rcc_clk_init(uint32 sysclk_src, uint32 pll_src, uint32 pll_mul); -void rcc_clk_enable(uint32 dev); -void rcc_reset_dev(uint32 dev); -void rcc_set_prescaler(uint32 prescaler, uint32 divider); +void rcc_set_prescaler(rcc_prescaler prescaler, uint32 divider); #ifdef __cplusplus } // extern "C" diff --git a/libmaple/ring_buffer.h b/libmaple/ring_buffer.h index a44088e..2536617 100644 --- a/libmaple/ring_buffer.h +++ b/libmaple/ring_buffer.h @@ -1,3 +1,29 @@ +/****************************************************************************** + * The MIT License + * + * Copyright (c) 2011 LeafLabs, LLC. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + *****************************************************************************/ + /** * @file ring_buffer.h * @brief Simple circular buffer @@ -9,25 +35,26 @@ #ifndef _RING_BUFFER_H_ #define _RING_BUFFER_H_ +#include "libmaple_types.h" + #ifdef __cplusplus extern "C"{ #endif -/* The buffer is empty when head == tail. +/** + * Ring buffer type. + * + * The buffer is empty when head == tail. * * The buffer is full when the head is one byte in front of the tail, * modulo buffer length. * * One byte is left free to distinguish empty from full. */ typedef struct ring_buffer { - /** Buffer items are stored into */ - volatile uint8 *buf; - /** Index of the next item to remove */ - uint16 head; - /** Index where the next item will get inserted */ - uint16 tail; - /** Buffer capacity minus one */ - uint16 size; + volatile uint8 *buf; /**< Buffer items are stored into */ + uint16 head; /**< Index of the next item to remove */ + uint16 tail; /**< Index where the next item will get inserted */ + uint16 size; /**< Buffer capacity minus one */ } ring_buffer; /** @@ -42,17 +69,19 @@ typedef struct ring_buffer { * * @param buf Buffer to store items into */ -__attribute__((unused)) -static void rb_init(ring_buffer *rb, uint16 size, uint8 *buf) { +static inline void rb_init(ring_buffer *rb, uint16 size, uint8 *buf) { rb->head = 0; rb->tail = 0; rb->size = size - 1; rb->buf = buf; } -/** Return the number of elements stored in the ring buffer. */ +/** + * @brief Return the number of elements stored in the ring buffer. + * @param rb Buffer whose elements to count. + */ static inline uint16 rb_full_count(ring_buffer *rb) { - volatile ring_buffer *arb = rb; + __io ring_buffer *arb = rb; int32 size = arb->tail - arb->head; if (arb->tail < arb->head) { size += arb->size + 1; @@ -60,19 +89,37 @@ static inline uint16 rb_full_count(ring_buffer *rb) { return (uint16)size; } -/** Return true if and only if the ring buffer is full. */ +/** + * @brief Returns true if and only if the ring buffer is full. + * @param rb Buffer to test. + */ static inline int rb_is_full(ring_buffer *rb) { return (rb->tail + 1 == rb->head) || (rb->tail == rb->size && rb->head == 0); } -/** Append element onto the end of the ring buffer. */ +/** + * @brief Returns true if and only if the ring buffer is empty. + * @param rb Buffer to test. + */ +static inline int rb_is_empty(ring_buffer *rb) { + return rb->head == rb->tail; +} + +/** + * Append element onto the end of a ring buffer. + * @param rb Buffer to append onto. + * @param element Value to append. + */ static inline void rb_insert(ring_buffer *rb, uint8 element) { rb->buf[rb->tail] = element; rb->tail = (rb->tail == rb->size) ? 0 : rb->tail + 1; } -/** Remove and return the first item from the ring buffer. */ +/** + * @brief Remove and return the first item from a ring buffer. + * @param rb Buffer to remove from, must contain at least one element. + */ static inline uint8 rb_remove(ring_buffer *rb) { uint8 ch = rb->buf[rb->head]; rb->head = (rb->head == rb->size) ? 0 : rb->head + 1; @@ -80,8 +127,24 @@ static inline uint8 rb_remove(ring_buffer *rb) { } /** - * If rb is not full, appends element and returns true; otherwise, - * does nothing and returns false. */ + * @brief Attempt to remove the first item from a ring buffer. + * + * If the ring buffer is nonempty, removes and returns its first item. + * If it is empty, does nothing and returns a negative value. + * + * @param rb Buffer to attempt to remove from. + */ +static inline int16 rb_safe_remove(ring_buffer *rb) { + return rb_is_empty(rb) ? -1 : rb_remove(rb); +} + +/** + * @brief Attempt to insert an element into a ring buffer. + * + * @param rb Buffer to insert into. + * @param element Value to insert into rb. + * @sideeffect If rb is not full, appends element onto buffer. + * @return If element was appended, then true; otherwise, false. */ static inline int rb_safe_insert(ring_buffer *rb, uint8 element) { if (rb_is_full(rb)) { return 0; @@ -91,12 +154,16 @@ static inline int rb_safe_insert(ring_buffer *rb, uint8 element) { } /** - * Append an item onto the end of a non-full ring buffer. If the - * buffer is full, removes its first item, then inserts the new + * @brief Append an item onto the end of a non-full ring buffer. + * + * If the buffer is full, removes its first item, then inserts the new * element at the end. * - * On success, returns -1. If an element was popped, returns the - * popped value. */ + * @param rb Ring buffer to insert into. + * @param element Value to insert into ring buffer. + * @return On success, returns -1. If an element was popped, returns + * the popped value. + */ static inline int rb_push_insert(ring_buffer *rb, uint8 element) { int ret = -1; if (rb_is_full(rb)) { @@ -106,7 +173,10 @@ static inline int rb_push_insert(ring_buffer *rb, uint8 element) { return ret; } -/** Discard all items from the buffer */ +/** + * @brief Discard all items from a ring buffer. + * @param rb Ring buffer to discard all items from. + */ static inline void rb_reset(ring_buffer *rb) { rb->tail = rb->head; } @@ -115,7 +185,5 @@ static inline void rb_reset(ring_buffer *rb) { } // extern "C" #endif - - #endif diff --git a/libmaple/rules.mk b/libmaple/rules.mk index b87595d..2eaea55 100644 --- a/libmaple/rules.mk +++ b/libmaple/rules.mk @@ -9,7 +9,7 @@ BUILDDIRS += $(BUILD_PATH)/$(d)/usb/usb_lib LIBMAPLE_INCLUDES := -I$(LIBMAPLE_PATH) -I$(LIBMAPLE_PATH)/usb -I$(LIBMAPLE_PATH)/usb/usb_lib # Local flags -CFLAGS_$(d) = -I$(d) $(LIBMAPLE_INCLUDES) -D$(VECT_BASE_ADDR) +CFLAGS_$(d) = -I$(d) $(LIBMAPLE_INCLUDES) # Local rules and targets cSRCS_$(d) := adc.c \ @@ -22,11 +22,13 @@ cSRCS_$(d) := adc.c \ gpio.c \ iwdg.c \ nvic.c \ + pwr.c \ + i2c.c \ rcc.c \ spi.c \ syscalls.c \ systick.c \ - timers.c \ + timer.c \ usart.c \ util.c \ usb/descriptors.c \ diff --git a/libmaple/scb.h b/libmaple/scb.h new file mode 100644 index 0000000..1911435 --- /dev/null +++ b/libmaple/scb.h @@ -0,0 +1,60 @@ +/* ***************************************************************************** + * The MIT License + * + * Copyright (c) 2010 Perry Hung. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * ****************************************************************************/ + +/** + * @brief System control block header + */ + +#ifndef _SCB_H_ +#define _SCB_H_ + +/** System control block register map */ +typedef struct scb_reg_map { + __io uint32 CPUID; /**< CPU ID Base Register */ + __io uint32 ICSR; /**< Interrupt Control State Register */ + __io uint32 VTOR; /**< Vector Table Offset Register */ + __io uint32 AIRCR; /**< Application Interrupt / Reset Control Register */ + __io uint32 SCR; /**< System Control Register */ + __io uint32 CCR; /**< Configuration Control Register */ + __io uint8 SHP[12]; /**< System Handlers Priority Registers + (4-7, 8-11, 12-15) */ + __io uint32 SHCSR; /**< System Handler Control and State Register */ + __io uint32 CFSR; /**< Configurable Fault Status Register */ + __io uint32 HFSR; /**< Hard Fault Status Register */ + __io uint32 DFSR; /**< Debug Fault Status Register */ + __io uint32 MMFAR; /**< Mem Manage Address Register */ + __io uint32 BFAR; /**< Bus Fault Address Register */ + __io uint32 AFSR; /**< Auxiliary Fault Status Register */ + __io uint32 PFR[2]; /**< Processor Feature Register */ + __io uint32 DFR; /**< Debug Feature Register */ + __io uint32 ADR; /**< Auxiliary Feature Register */ + __io uint32 MMFR[4]; /**< Memory Model Feature Register */ + __io uint32 ISAR[5]; /**< ISA Feature Register */ +} scb_reg_map; + +/** System control block register map base pointer */ +#define SCB_BASE ((struct scb_reg_map*)0xE000ED00) + +#endif + diff --git a/libmaple/spi.c b/libmaple/spi.c index 8bba0d6..e58ae91 100644 --- a/libmaple/spi.c +++ b/libmaple/spi.c @@ -3,159 +3,235 @@ * * Copyright (c) 2010 Perry Hung. * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. *****************************************************************************/ /** - * @brief libmaple serial peripheral interface (SPI) definitions - * - * Initial implementation for the SPI interface. - * - * This driver implements only the bare essentials of a polling driver at the - * moment. Master mode only, 8-bit data frames, and polling. - * - * The caller is responsible for controlling the chip select line. - * - * TODO: interrupt based driver, DMA. - * + * @file spi.c + * @author Marti Bolivar <mbolivar@leaflabs.com> + * @brief Serial Peripheral Interface (SPI) support. + * Currently, there is no Integrated Interchip Sound (I2S) support. */ -#include "libmaple.h" -#include "gpio.h" -#include "rcc.h" #include "spi.h" +#include "bitband.h" -typedef struct spi_dev { - SPI *base; - GPIO_Port *port; - uint8 sck_pin; - uint8 miso_pin; - uint8 mosi_pin; -} spi_dev; - -static const spi_dev spi_dev1 = { - .base = (SPI*)SPI1_BASE, - .port = GPIOA_BASE, - .sck_pin = 5, - .miso_pin = 6, - .mosi_pin = 7 -}; +static void spi_reconfigure(spi_dev *dev, uint32 cr1_config); + +/* + * SPI devices + */ -static const spi_dev spi_dev2 = { - .base = (SPI*)SPI2_BASE, - .port = GPIOB_BASE, - .sck_pin = 13, - .miso_pin = 14, - .mosi_pin = 15 +static spi_dev spi1 = { + .regs = SPI1_BASE, + .clk_id = RCC_SPI1, + .irq_num = NVIC_SPI1, }; +spi_dev *SPI1 = &spi1; -static void spi_gpio_cfg(const spi_dev *dev); +static spi_dev spi2 = { + .regs = SPI2_BASE, + .clk_id = RCC_SPI2, + .irq_num = NVIC_SPI2, +}; +spi_dev *SPI2 = &spi2; -/** - * @brief Initialize a spi peripheral - * @param spi_num which spi to turn on, SPI1 or SPI2? - * @param prescale prescale factor on the input clock. - * @param endian data frame format (LSBFIRST?) - * @param mode SPI mode number - */ -void spi_init(uint32 spi_num, - uint32 prescale, - uint32 endian, - uint32 mode) { - ASSERT(spi_num == 1 || spi_num == 2); - ASSERT(mode < 4); - - SPI *spi; - uint32 cr1 = 0; - - switch (spi_num) { - case 1: - /* limit to 18 mhz max speed */ - ASSERT(prescale != CR1_BR_PRESCALE_2); - spi = (SPI*)SPI1_BASE; - rcc_clk_enable(RCC_SPI1); - spi_gpio_cfg(&spi_dev1); - break; - case 2: - spi = (SPI*)SPI2_BASE; - rcc_clk_enable(RCC_SPI2); - spi_gpio_cfg(&spi_dev2); - break; - } +#ifdef STM32_HIGH_DENSITY +static spi_dev spi3 = { + .regs = SPI3_BASE, + .clk_id = RCC_SPI3, + .irq_num = NVIC_SPI3, +}; +spi_dev *SPI3 = &spi3; +#endif - cr1 = prescale | endian | mode | CR1_MSTR | CR1_SSI | CR1_SSM; - spi->CR1 = cr1; +/* + * SPI convenience routines + */ - /* Peripheral enable */ - spi->CR1 |= CR1_SPE; +/** + * @brief Initialize and reset a SPI device. + * @param dev Device to initialize and reset. + */ +void spi_init(spi_dev *dev) { + rcc_clk_enable(dev->clk_id); + rcc_reset_dev(dev->clk_id); } /** - * @brief SPI synchronous 8-bit write, blocking. - * @param spi_num which spi to send on - * @return data shifted back from the slave + * @brief Configure GPIO bit modes for use as a SPI port's pins. + * @param as_master If true, configure bits for use as a bus master. + * Otherwise, configure bits for use as slave. + * @param nss_dev NSS pin's GPIO device + * @param comm_dev SCK, MISO, MOSI pins' GPIO device + * @param nss_bit NSS pin's GPIO bit on nss_dev + * @param sck_bit SCK pin's GPIO bit on comm_dev + * @param miso_bit MISO pin's GPIO bit on comm_dev + * @param mosi_bit MOSI pin's GPIO bit on comm_dev */ -uint8 spi_tx_byte(uint32 spi_num, uint8 data) { - SPI *spi; +void spi_gpio_cfg(uint8 as_master, + gpio_dev *nss_dev, + uint8 nss_bit, + gpio_dev *comm_dev, + uint8 sck_bit, + uint8 miso_bit, + uint8 mosi_bit) { + if (as_master) { + gpio_set_mode(nss_dev, nss_bit, GPIO_AF_OUTPUT_PP); + gpio_set_mode(comm_dev, sck_bit, GPIO_AF_OUTPUT_PP); + gpio_set_mode(comm_dev, miso_bit, GPIO_INPUT_FLOATING); + gpio_set_mode(comm_dev, mosi_bit, GPIO_AF_OUTPUT_PP); + } else { + gpio_set_mode(nss_dev, nss_bit, GPIO_INPUT_FLOATING); + gpio_set_mode(comm_dev, sck_bit, GPIO_INPUT_FLOATING); + gpio_set_mode(comm_dev, miso_bit, GPIO_AF_OUTPUT_PP); + gpio_set_mode(comm_dev, mosi_bit, GPIO_INPUT_FLOATING); + } +} - spi = (spi_num == 1) ? (SPI*)SPI1_BASE : (SPI*)SPI2_BASE; +/** + * @brief Configure and enable a SPI device as bus master. + * + * The device's peripheral will be disabled before being reconfigured. + * + * @param dev Device to configure as bus master + * @param baud Bus baud rate + * @param mode SPI mode + * @param flags Logical OR of spi_cfg_flag values. + * @see spi_cfg_flag + */ +void spi_master_enable(spi_dev *dev, + spi_baud_rate baud, + spi_mode mode, + uint32 flags) { + spi_reconfigure(dev, baud | flags | SPI_CR1_MSTR | mode); +} - while (!(spi->SR & SR_TXE)) - ; +/** + * @brief Configure and enable a SPI device as a bus slave. + * + * The device's peripheral will be disabled before being reconfigured. + * + * @param dev Device to configure as a bus slave + * @param mode SPI mode + * @param flags Logical OR of spi_cfg_flag values. + * @see spi_cfg_flag + */ +void spi_slave_enable(spi_dev *dev, spi_mode mode, uint32 flags) { + spi_reconfigure(dev, flags | mode); +} - spi->DR = data; +/** + * @brief Nonblocking SPI transmit. + * @param dev SPI port to use for transmission + * @param buf Buffer to transmit. The sizeof buf's elements are + * inferred from dev's data frame format (i.e., are + * correctly treated as 8-bit or 16-bit quantities). + * @param len Maximum number of elements to transmit. + * @return Number of elements transmitted. + */ +uint32 spi_tx(spi_dev *dev, const void *buf, uint32 len) { + uint32 txed = 0; + uint8 byte_frame = spi_dff(dev) == SPI_DFF_8_BIT; + while (spi_is_tx_empty(dev) && (txed < len)) { + if (byte_frame) { + dev->regs->DR = ((const uint8*)buf)[txed++]; + } else { + dev->regs->DR = ((const uint16*)buf)[txed++]; + } + } + return txed; +} - while (!(spi->SR & SR_RXNE)) - ; +/** + * @brief Call a function on each SPI port + * @param fn Function to call. + */ +void spi_foreach(void (*fn)(spi_dev*)) { + fn(SPI1); + fn(SPI2); +#ifdef STM32_HIGH_DENSITY + fn(SPI3); +#endif +} - return spi->DR; +/** + * @brief Enable a SPI peripheral + * @param dev Device to enable + */ +void spi_peripheral_enable(spi_dev *dev) { + bb_peri_set_bit(&dev->regs->CR1, SPI_CR1_SPE_BIT, 1); } -uint8 spi_tx(uint32 spi_num, uint8 *buf, uint32 len) { - SPI *spi; - uint32 i = 0; - uint8 rc; +/** + * @brief Disable a SPI peripheral + * @param dev Device to disable + */ +void spi_peripheral_disable(spi_dev *dev) { + bb_peri_set_bit(&dev->regs->CR1, SPI_CR1_SPE_BIT, 0); +} - ASSERT(spi_num == 1 || spi_num == 2); - spi = (spi_num == 1) ? (SPI*)SPI1_BASE : (SPI*)SPI2_BASE; +/** + * @brief Enable DMA requests whenever the transmit buffer is empty + * @param dev SPI device on which to enable TX DMA requests + */ +void spi_tx_dma_enable(spi_dev *dev) { + bb_peri_set_bit(&dev->regs->CR2, SPI_CR2_TXDMAEN_BIT, 1); +} - if (!len) { - return 0; - } +/** + * @brief Disable DMA requests whenever the transmit buffer is empty + * @param dev SPI device on which to disable TX DMA requests + */ +void spi_tx_dma_disable(spi_dev *dev) { + bb_peri_set_bit(&dev->regs->CR2, SPI_CR2_TXDMAEN_BIT, 0); +} - while (i < len) { - while (!(spi->SR & SR_TXE)) - ; +/** + * @brief Enable DMA requests whenever the receive buffer is empty + * @param dev SPI device on which to enable RX DMA requests + */ +void spi_rx_dma_enable(spi_dev *dev) { + bb_peri_set_bit(&dev->regs->CR2, SPI_CR2_RXDMAEN_BIT, 1); +} - spi->DR = buf[i]; +/** + * @brief Disable DMA requests whenever the receive buffer is empty + * @param dev SPI device on which to disable RX DMA requests + */ +void spi_rx_dma_disable(spi_dev *dev) { + bb_peri_set_bit(&dev->regs->CR2, SPI_CR2_RXDMAEN_BIT, 0); +} - while (!(spi->SR & SR_RXNE)) - ; +/* + * SPI auxiliary routines + */ - rc = spi->DR; - i++; - } - return rc; +static void spi_reconfigure(spi_dev *dev, uint32 cr1_config) { + spi_irq_disable(dev, SPI_INTERRUPTS_ALL); + spi_peripheral_disable(dev); + dev->regs->CR1 = cr1_config; + spi_peripheral_enable(dev); } -static void spi_gpio_cfg(const spi_dev *dev) { - gpio_set_mode(dev->port, dev->sck_pin, GPIO_MODE_AF_OUTPUT_PP); - gpio_set_mode(dev->port, dev->miso_pin, GPIO_MODE_AF_OUTPUT_PP); - gpio_set_mode(dev->port, dev->mosi_pin, GPIO_MODE_AF_OUTPUT_PP); -} +/* + * IRQ handlers (TODO) + */ diff --git a/libmaple/spi.h b/libmaple/spi.h index db8aa9c..37ca6ef 100644 --- a/libmaple/spi.h +++ b/libmaple/spi.h @@ -24,96 +24,434 @@ /** * @file spi.h - * @brief libmaple serial peripheral interface (SPI) prototypes and - * declarations + * @author Marti Bolivar <mbolivar@leaflabs.com> + * @brief Serial Peripheral Interface (SPI) and Integrated + * Interchip Sound (I2S) peripheral support. + * + * I2S support is currently limited to register maps and bit definitions. */ #ifndef _SPI_H_ #define _SPI_H_ +#include "libmaple_types.h" +#include "rcc.h" +#include "nvic.h" +#include "gpio.h" +#include "util.h" + #ifdef __cplusplus extern "C" { #endif -/* peripheral addresses */ -#define SPI1_BASE 0x40013000 -#define SPI2_BASE 0x40003800 -#define SPI3_BASE 0x40003C00 - -/* baud rate prescaler bits */ -#define CR1_BR 0x00000038 -#define CR1_BR_PRESCALE_2 0x00000000 -#define CR1_BR_PRESCALE_4 0x00000008 -#define CR1_BR_PRESCALE_8 0x00000010 -#define CR1_BR_PRESCALE_16 0x00000018 -#define CR1_BR_PRESCALE_32 0x00000020 -#define CR1_BR_PRESCALE_64 0x00000028 -#define CR1_BR_PRESCALE_128 0x00000030 -#define CR1_BR_PRESCALE_256 0x00000038 - -#define CR1_LSBFIRST BIT(7) // data frame format -#define CR1_MSTR BIT(2) // master selection -#define CR1_SSM BIT(9) // software slave management -#define CR1_SSI BIT(8) // internal slave select -#define CR1_SPE BIT(6) // peripheral enable - -/* Status register bits */ -#define SR_RXNE BIT(0) // rx buffer not empty -#define SR_TXE BIT(1) // transmit buffer empty -#define SR_BSY BIT(7) // busy flag - -typedef struct SPI { - __io uint16 CR1; - uint16 pad0; - __io uint8 CR2; - uint8 pad1[3]; - __io uint8 SR; - uint8 pad2[3]; - __io uint16 DR; - uint16 pad3; - __io uint16 CRCPR; - uint16 pad4; - __io uint16 RXCRCR; - uint16 pad5; - __io uint16 TXCRCR; - uint16 pad6; -} SPI; - -enum { - SPI_MSBFIRST = 0, - SPI_LSBFIRST = BIT(7), -}; - -enum { - SPI_PRESCALE_2 = (0x0 << 3), - SPI_PRESCALE_4 = (0x1 << 3), - SPI_PRESCALE_8 = (0x2 << 3), - SPI_PRESCALE_16 = (0x3 << 3), - SPI_PRESCALE_32 = (0x4 << 3), - SPI_PRESCALE_64 = (0x5 << 3), - SPI_PRESCALE_128 = (0x6 << 3), - SPI_PRESCALE_256 = (0x7 << 3) -}; - -void spi_init(uint32 spi_num, - uint32 prescale, - uint32 endian, - uint32 mode); -uint8 spi_tx_byte(uint32 spi_num, uint8 data); -uint8 spi_tx(uint32 spi_num, uint8 *buf, uint32 len); - -static inline uint8 spi_rx(uint32 spi_num) { - SPI *spi; - - ASSERT(spi_num == 1 || spi_num == 2); - spi = (spi_num == 1) ? (SPI*)SPI1_BASE : (SPI*)SPI2_BASE; - - return spi->DR; +/* + * Register maps + */ + +/** SPI register map type. */ +typedef struct spi_reg_map { + __io uint32 CR1; /**< Control register 1 */ + __io uint32 CR2; /**< Control register 2 */ + __io uint32 SR; /**< Status register */ + __io uint32 DR; /**< Data register */ + __io uint32 CRCPR; /**< CRC polynomial register */ + __io uint32 RXCRCR; /**< RX CRC register */ + __io uint32 TXCRCR; /**< TX CRC register */ + __io uint32 I2SCFGR; /**< I2S configuration register */ + __io uint32 I2SPR; /**< I2S prescaler register */ +} spi_reg_map; + +/** SPI1 register map base pointer */ +#define SPI1_BASE ((struct spi_reg_map*)0x40013000) +/** SPI2 register map base pointer */ +#define SPI2_BASE ((struct spi_reg_map*)0x40003800) +/** SPI3 register map base pointer */ +#define SPI3_BASE ((struct spi_reg_map*)0x40003C00) + +/* + * Register bit definitions + */ + +/* Control register 1 */ + +#define SPI_CR1_BIDIMODE_BIT 15 +#define SPI_CR1_BIDIOE_BIT 14 +#define SPI_CR1_CRCEN_BIT 13 +#define SPI_CR1_CRCNEXT_BIT 12 +#define SPI_CR1_DFF_BIT 11 +#define SPI_CR1_RXONLY_BIT 10 +#define SPI_CR1_SSM_BIT 9 +#define SPI_CR1_SSI_BIT 8 +#define SPI_CR1_LSBFIRST_BIT 7 +#define SPI_CR1_SPE_BIT 6 +#define SPI_CR1_MSTR_BIT 2 +#define SPI_CR1_CPOL_BIT 1 +#define SPI_CR1_CPHA_BIT 0 + +#define SPI_CR1_BIDIMODE BIT(SPI_CR1_BIDIMODE_BIT) +#define SPI_CR1_BIDIMODE_2_LINE (0x0 << SPI_CR1_BIDIMODE_BIT) +#define SPI_CR1_BIDIMODE_1_LINE (0x1 << SPI_CR1_BIDIMODE_BIT) +#define SPI_CR1_BIDIOE BIT(SPI_CR1_BIDIOE_BIT) +#define SPI_CR1_CRCEN BIT(SPI_CR1_CRCEN_BIT) +#define SPI_CR1_CRCNEXT BIT(SPI_CR1_CRCNEXT_BIT) +#define SPI_CR1_DFF BIT(SPI_CR1_DFF_BIT) +#define SPI_CR1_DFF_8_BIT (0x0 << SPI_CR1_DFF_BIT) +#define SPI_CR1_DFF_16_BIT (0x1 << SPI_CR1_DFF_BIT) +#define SPI_CR1_RXONLY BIT(SPI_CR1_RXONLY_BIT) +#define SPI_CR1_SSM BIT(SPI_CR1_SSM_BIT) +#define SPI_CR1_SSI BIT(SPI_CR1_SSI_BIT) +#define SPI_CR1_LSBFIRST BIT(SPI_CR1_LSBFIRST_BIT) +#define SPI_CR1_SPE BIT(SPI_CR1_SPE_BIT) +#define SPI_CR1_BR (0x7 << 3) +#define SPI_CR1_BR_PCLK_DIV_2 (0x0 << 3) +#define SPI_CR1_BR_PCLK_DIV_4 (0x1 << 3) +#define SPI_CR1_BR_PCLK_DIV_8 (0x2 << 3) +#define SPI_CR1_BR_PCLK_DIV_16 (0x3 << 3) +#define SPI_CR1_BR_PCLK_DIV_32 (0x4 << 3) +#define SPI_CR1_BR_PCLK_DIV_64 (0x5 << 3) +#define SPI_CR1_BR_PCLK_DIV_128 (0x6 << 3) +#define SPI_CR1_BR_PCLK_DIV_256 (0x7 << 3) +#define SPI_CR1_MSTR BIT(SPI_CR1_MSTR_BIT) +#define SPI_CR1_CPOL BIT(SPI_CR1_CPOL_BIT) +#define SPI_CR1_CPOL_LOW (0x0 << SPI_CR1_CPOL_BIT) +#define SPI_CR1_CPOL_HIGH (0x1 << SPI_CR1_CPOL_BIT) +#define SPI_CR1_CPHA BIT(SPI_CR1_CPHA_BIT) + +/* Control register 2 */ + +/* RM0008-ism: SPI CR2 has "TXDMAEN" and "RXDMAEN" bits, while the + * USARTs have CR3 "DMAR" and "DMAT" bits. */ + +#define SPI_CR2_TXEIE_BIT 7 +#define SPI_CR2_RXNEIE_BIT 6 +#define SPI_CR2_ERRIE_BIT 5 +#define SPI_CR2_SSOE_BIT 2 +#define SPI_CR2_TXDMAEN_BIT 1 +#define SPI_CR2_RXDMAEN_BIT 0 + +#define SPI_CR2_TXEIE BIT(SPI_CR2_TXEIE_BIT) +#define SPI_CR2_RXNEIE BIT(SPI_CR2_RXNEIE_BIT) +#define SPI_CR2_ERRIE BIT(SPI_CR2_ERRIE_BIT) +#define SPI_CR2_SSOE BIT(SPI_CR2_SSOE_BIT) +#define SPI_CR2_TXDMAEN BIT(SPI_CR2_TXDMAEN_BIT) +#define SPI_CR2_RXDMAEN BIT(SPI_CR2_RXDMAEN_BIT) + +/* Status register */ + +#define SPI_SR_BSY_BIT 7 +#define SPI_SR_OVR_BIT 6 +#define SPI_SR_MODF_BIT 5 +#define SPI_SR_CRCERR_BIT 4 +#define SPI_SR_UDR_BIT 3 +#define SPI_SR_CHSIDE_BIT 2 +#define SPI_SR_TXE_BIT 1 +#define SPI_SR_RXNE_BIT 0 + +#define SPI_SR_BSY BIT(SPI_SR_BSY_BIT) +#define SPI_SR_OVR BIT(SPI_SR_OVR_BIT) +#define SPI_SR_MODF BIT(SPI_SR_MODF_BIT) +#define SPI_SR_CRCERR BIT(SPI_SR_CRCERR_BIT) +#define SPI_SR_UDR BIT(SPI_SR_UDR_BIT) +#define SPI_SR_CHSIDE BIT(SPI_SR_CHSIDE_BIT) +#define SPI_SR_CHSIDE_LEFT (0x0 << SPI_SR_CHSIDE_BIT) +#define SPI_SR_CHSIDE_RIGHT (0x1 << SPI_SR_CHSIDE_BIT) +#define SPI_SR_TXE BIT(SPI_SR_TXE_BIT) +#define SPI_SR_RXNE BIT(SPI_SR_RXNE_BIT) + +/* I2S configuration register */ + +/* RM0008-ism: CR1 has "CPOL", I2SCFGR has "CKPOL". */ + +#define SPI_I2SCFGR_I2SMOD_BIT 11 +#define SPI_I2SCFGR_I2SE_BIT 10 +#define SPI_I2SCFGR_PCMSYNC_BIT 7 +#define SPI_I2SCFGR_CKPOL_BIT 3 +#define SPI_I2SCFGR_CHLEN_BIT 0 + +#define SPI_I2SCFGR_I2SMOD BIT(SPI_I2SCFGR_I2SMOD_BIT) +#define SPI_I2SCFGR_I2SMOD_SPI (0x0 << SPI_I2SCFGR_I2SMOD_BIT) +#define SPI_I2SCFGR_I2SMOD_I2S (0x1 << SPI_I2SCFGR_I2SMOD_BIT) +#define SPI_I2SCFGR_I2SE BIT(SPI_I2SCFGR_I2SE_BIT) +#define SPI_I2SCFGR_I2SCFG (0x3 << 8) +#define SPI_I2SCFGR_I2SCFG_SLAVE_TX (0x0 << 8) +#define SPI_I2SCFGR_I2SCFG_SLAVE_RX (0x1 << 8) +#define SPI_I2SCFGR_I2SCFG_MASTER_TX (0x2 << 8) +#define SPI_I2SCFGR_I2SCFG_MASTER_RX (0x3 << 8) +#define SPI_I2SCFGR_PCMSYNC BIT(SPI_I2SCFGR_PCMSYNC_BIT) +#define SPI_I2SCFGR_PCMSYNC_SHORT (0x0 << SPI_I2SCFGR_PCMSYNC_BIT) +#define SPI_I2SCFGR_PCMSYNC_LONG (0x1 << SPI_I2SCFGR_PCMSYNC_BIT) +#define SPI_I2SCFGR_I2SSTD (0x3 << 4) +#define SPI_I2SCFGR_I2SSTD_PHILLIPS (0x0 << 4) +#define SPI_I2SCFGR_I2SSTD_MSB (0x1 << 4) +#define SPI_I2SCFGR_I2SSTD_LSB (0x2 << 4) +#define SPI_I2SCFGR_I2SSTD_PCM (0x3 << 4) +#define SPI_I2SCFGR_CKPOL BIT(SPI_I2SCFGR_CKPOL_BIT) +#define SPI_I2SCFGR_CKPOL_LOW (0x0 << SPI_I2SCFGR_CKPOL_BIT) +#define SPI_I2SCFGR_CKPOL_HIGH (0x1 << SPI_I2SCFGR_CKPOL_BIT) +#define SPI_I2SCFGR_DATLEN (0x3 << 1) +#define SPI_I2SCFGR_DATLEN_16_BIT (0x0 << 1) +#define SPI_I2SCFGR_DATLEN_24_BIT (0x1 << 1) +#define SPI_I2SCFGR_DATLEN_32_BIT (0x2 << 1) +#define SPI_I2SCFGR_CHLEN BIT(SPI_I2SCFGR_CHLEN_BIT) +#define SPI_I2SCFGR_CHLEN_16_BIT (0x0 << SPI_I2SCFGR_CHLEN_BIT) +#define SPI_I2SCFGR_CHLEN_32_BIT (0x1 << SPI_I2SCFGR_CHLEN_BIT) + +/* + * Devices + */ + +/** SPI device type */ +typedef struct spi_dev { + spi_reg_map *regs; + rcc_clk_id clk_id; + nvic_irq_num irq_num; +} spi_dev; + +/** SPI device 1 */ +extern spi_dev *SPI1; +/** SPI device 2 */ +extern spi_dev *SPI2; +#ifdef STM32_HIGH_DENSITY +/** SPI device 3 */ +extern spi_dev *SPI3; +#endif + +/* + * SPI Convenience functions + */ + +void spi_init(spi_dev *dev); + +void spi_gpio_cfg(uint8 as_master, + gpio_dev *nss_dev, + uint8 nss_bit, + gpio_dev *comm_dev, + uint8 sck_bit, + uint8 miso_bit, + uint8 mosi_bit); + +/** + * @brief SPI mode configuration. + * + * Determines a combination of clock polarity (CPOL), which determines + * idle state of the clock line, and clock phase (CPHA), which + * determines which clock edge triggers data capture. + */ +typedef enum { + SPI_MODE_0, /**< Clock line idles low (0), data capture on first + clock transition. */ + SPI_MODE_1, /**< Clock line idles low (0), data capture on second + clock transition */ + SPI_MODE_2, /**< Clock line idles high (1), data capture on first + clock transition. */ + SPI_MODE_3 /**< Clock line idles high (1), data capture on + second clock transition. */ +} spi_mode; + +/** + * @brief SPI baud rate configuration, as a divisor of f_PCLK, the + * PCLK clock frequency. + */ +typedef enum { + SPI_BAUD_PCLK_DIV_2 = SPI_CR1_BR_PCLK_DIV_2, /**< f_PCLK/2 */ + SPI_BAUD_PCLK_DIV_4 = SPI_CR1_BR_PCLK_DIV_4, /**< f_PCLK/4 */ + SPI_BAUD_PCLK_DIV_8 = SPI_CR1_BR_PCLK_DIV_8, /**< f_PCLK/8 */ + SPI_BAUD_PCLK_DIV_16 = SPI_CR1_BR_PCLK_DIV_16, /**< f_PCLK/16 */ + SPI_BAUD_PCLK_DIV_32 = SPI_CR1_BR_PCLK_DIV_32, /**< f_PCLK/32 */ + SPI_BAUD_PCLK_DIV_64 = SPI_CR1_BR_PCLK_DIV_64, /**< f_PCLK/64 */ + SPI_BAUD_PCLK_DIV_128 = SPI_CR1_BR_PCLK_DIV_128, /**< f_PCLK/128 */ + SPI_BAUD_PCLK_DIV_256 = SPI_CR1_BR_PCLK_DIV_256, /**< f_PCLK/256 */ +} spi_baud_rate; + +/** + * @brief SPI initialization flags. + * @see spi_master_enable() + * @see spi_slave_enable() + */ +typedef enum { + SPI_BIDIMODE = SPI_CR1_BIDIMODE, /**< Bidirectional mode enable */ + SPI_BIDIOE = SPI_CR1_BIDIOE, /**< Output enable in bidirectional + mode */ + SPI_CRCEN = SPI_CR1_CRCEN, /**< Cyclic redundancy check (CRC) + enable */ + SPI_DFF_8_BIT = SPI_CR1_DFF_8_BIT, /**< 8-bit data frame format (this is + the default) */ + SPI_DFF_16_BIT = SPI_CR1_DFF_16_BIT, /**< 16-bit data frame format */ + SPI_RX_ONLY = SPI_CR1_RXONLY, /**< Receive only */ + SPI_SW_SLAVE = SPI_CR1_SSM, /**< Software slave management */ + SPI_SOFT_SS = SPI_CR1_SSI, /**< Software (internal) slave + select. This flag only has an + effect when used in combination + with SPI_SW_SLAVE. */ + SPI_FRAME_LSB = SPI_CR1_LSBFIRST, /**< LSB-first (little-endian) frame + format */ + SPI_FRAME_MSB = 0, /**< MSB-first (big-endian) frame + format (this is the default) */ +} spi_cfg_flag; + +void spi_master_enable(spi_dev *dev, + spi_baud_rate baud, + spi_mode mode, + uint32 flags); + +void spi_slave_enable(spi_dev *dev, + spi_mode mode, + uint32 flags); + +uint32 spi_tx(spi_dev *dev, const void *buf, uint32 len); + +void spi_foreach(void (*fn)(spi_dev (*dev))); + +void spi_peripheral_enable(spi_dev *dev); +void spi_peripheral_disable(spi_dev *dev); + +void spi_tx_dma_enable(spi_dev *dev); +void spi_tx_dma_disable(spi_dev *dev); + +void spi_rx_dma_enable(spi_dev *dev); +void spi_rx_dma_disable(spi_dev *dev); + +/** + * @brief Determine if a SPI peripheral is enabled. + * @param dev SPI device + * @return True, if and only if dev's peripheral is enabled. + */ +static inline uint8 spi_is_enabled(spi_dev *dev) { + return dev->regs->CR1 & SPI_CR1_SPE_BIT; +} + +/** + * @brief Disable all SPI peripherals + */ +static inline void spi_peripheral_disable_all(void) { + spi_foreach(spi_peripheral_disable); +} + +/** Available SPI interrupts */ +typedef enum { + SPI_TXE_INTERRUPT = SPI_CR2_TXEIE, /**< TX buffer empty interrupt */ + SPI_RXNE_INTERRUPT = SPI_CR2_RXNEIE, /**< RX buffer not empty interrupt */ + SPI_ERR_INTERRUPT = SPI_CR2_ERRIE /**< + * Error interrupt (CRC, overrun, + * and mode fault errors for SPI; + * underrun, overrun errors for I2S) + */ +} spi_interrupt; + +/** + * @brief Mask for all spi_interrupt values + * @see spi_interrupt + */ +#define SPI_INTERRUPTS_ALL (SPI_TXE_INTERRUPT | \ + SPI_RXNE_INTERRUPT | \ + SPI_ERR_INTERRUPT) + +/** + * @brief Enable SPI interrupt requests + * @param dev SPI device + * @param interrupt_flags Bitwise OR of spi_interrupt values to enable + * @see spi_interrupt + */ +static inline void spi_irq_enable(spi_dev *dev, uint32 interrupt_flags) { + dev->regs->CR2 |= interrupt_flags; + nvic_irq_enable(dev->irq_num); +} + +/** + * @brief Disable SPI interrupt requests + * @param dev SPI device + * @param interrupt_flags Bitwise OR of spi_interrupt values to disable + * @see spi_interrupt + */ +static inline void spi_irq_disable(spi_dev *dev, uint32 interrupt_flags) { + dev->regs->CR2 &= ~interrupt_flags; +} + +/** + * @brief Get the data frame format flags with which a SPI port is + * configured. + * @param dev SPI device whose data frame format to get. + * @return SPI_DFF_8_BIT, if dev has an 8-bit data frame format. + * Otherwise, SPI_DFF_16_BIT. + */ +static inline spi_cfg_flag spi_dff(spi_dev *dev) { + return ((dev->regs->CR1 & SPI_CR1_DFF) == SPI_CR1_DFF_8_BIT ? + SPI_DFF_8_BIT : + SPI_DFF_16_BIT); +} + +/** + * @brief Determine whether the device's peripheral receive (RX) + * register is empty. + * @param dev SPI device + * @return true, iff dev's RX register is empty. + */ +static inline uint8 spi_is_rx_nonempty(spi_dev *dev) { + return dev->regs->SR & SPI_SR_RXNE; +} + +/** + * @brief Retrieve the contents of the device's peripheral receive + * (RX) register. + * + * You may only call this function when the RX register is nonempty. + * Calling this function clears the contents of the RX register. + * + * @param dev SPI device + * @return Contents of dev's peripheral RX register + * @see spi_is_rx_reg_nonempty() + */ +static inline uint16 spi_rx_reg(spi_dev *dev) { + return (uint16)dev->regs->DR; +} + +/** + * @brief Determine whether the device's peripheral transmit (TX) + * register is empty. + * @param dev SPI device + * @return true, iff dev's TX register is empty. + */ +static inline uint8 spi_is_tx_empty(spi_dev *dev) { + return dev->regs->SR & SPI_SR_TXE; +} + +/** + * @brief Load a value into the device's peripheral transmit (TX) register. + * + * You may only call this function when the TX register is empty. + * Calling this function loads val into the peripheral's TX register. + * If the device is properly configured, this will initiate a + * transmission, the completion of which will cause the TX register to + * be empty again. + * + * @param dev SPI device + * @param val Value to load into the TX register. If the SPI data + * frame format is 8 bit, the value must be right-aligned. + * @see spi_is_tx_reg_empty() + * @see spi_init() + * @see spi_master_enable() + * @see spi_slave_enable() + */ +static inline void spi_tx_reg(spi_dev *dev, uint16 val) { + dev->regs->DR = val; +} + +/** + * @brief Determine whether the device's peripheral busy (SPI_SR_BSY) + * flag is set. + * @param dev SPI device + * @return true, iff dev's BSY flag is set. + */ +static inline uint8 spi_is_busy(spi_dev *dev) { + return dev->regs->SR & SPI_SR_BSY; } +/* + * I2S convenience functions (TODO) + */ + #ifdef __cplusplus } #endif #endif - diff --git a/libmaple/stm32.h b/libmaple/stm32.h new file mode 100644 index 0000000..21c18df --- /dev/null +++ b/libmaple/stm32.h @@ -0,0 +1,22 @@ +/** + * @brief General STM32 specific definitions + */ + +#ifndef _STM32_H_ +#define _STM32_H_ + +#define PCLK1 36000000U +#define PCLK2 72000000U + +#ifdef STM32_MEDIUM_DENSITY + #define NR_INTERRUPTS 43 +#else +#ifdef STM32_HIGH_DENSITY + #define NR_INTERRUPTS 60 +#else +#error "No STM32 board type defined!" +#endif +#endif + +#endif + diff --git a/libmaple/systick.c b/libmaple/systick.c index b056001..c04f4f3 100644 --- a/libmaple/systick.c +++ b/libmaple/systick.c @@ -26,44 +26,46 @@ * @brief System timer interrupt handler and initialization routines */ -#include "libmaple.h" #include "systick.h" -#define SYSTICK_RELOAD 0xE000E014 // Reload value register -#define SYSTICK_CNT 0xE000E018 // Current value register -#define SYSTICK_CALIB 0xE000E01C // Calibration value register - -#define SYSTICK_SRC_HCLK BIT(2) // Use core clock -#define SYSTICK_TICKINT BIT(1) // Interrupt on systick countdown -#define SYSTICK_ENABLE BIT(0) // Turn on the counter - volatile uint32 systick_timer_millis; +/** + * @brief Initialize and enable SysTick. + * + * Clocks the system timer with the core clock, turns it on, and + * enables interrupts. + * + * @param reload_val Appropriate reload counter to tick every 1 ms. + */ void systick_init(uint32 reload_val) { - /* Set the reload counter to tick every 1ms */ - __write(SYSTICK_RELOAD, reload_val); - - /* Clock the system timer with the core clock and turn it on, - * interrrupt every 1ms to keep track of millis() */ - __write(SYSTICK_CSR, SYSTICK_SRC_HCLK | - SYSTICK_ENABLE | - SYSTICK_TICKINT); + SYSTICK_BASE->RVR = reload_val; + systick_enable(); } +/** + * Clock the system timer with the core clock, but don't turn it + * on or enable interrupt. + */ void systick_disable() { - /* clock the system timer with the core clock, but don't turn it - on or enable interrupt. */ - __write(SYSTICK_CSR, SYSTICK_SRC_HCLK); + SYSTICK_BASE->CSR = SYSTICK_CSR_CLKSOURCE_CORE; } -void systick_resume() { - /* re-enable init registers without changing reload val */ - __write(SYSTICK_CSR, SYSTICK_SRC_HCLK | - SYSTICK_ENABLE | - SYSTICK_TICKINT); +/** + * Clock the system timer with the core clock and turn it on; + * interrupt every 1 ms, for systick_timer_millis. + */ +void systick_enable() { + /* re-enables init registers without changing reload val */ + SYSTICK_BASE->CSR = (SYSTICK_CSR_CLKSOURCE_CORE | + SYSTICK_CSR_ENABLE | + SYSTICK_CSR_TICKINT_PEND); } -/** SysTick interrupt handler. Bumps up the tick counter. */ -void SysTickHandler(void) { +/* + * SysTick ISR + */ + +void __exc_systick(void) { systick_timer_millis++; } diff --git a/libmaple/systick.h b/libmaple/systick.h index 33a3cf8..35b4cb9 100644 --- a/libmaple/systick.h +++ b/libmaple/systick.h @@ -31,30 +31,60 @@ #ifndef _SYSTICK_H_ #define _SYSTICK_H_ -#include "libmaple.h" +#include "libmaple_types.h" +#include "util.h" #ifdef __cplusplus extern "C"{ #endif -#define SYSTICK_CSR 0xE000E010 // Control and status register -#define SYSTICK_CNT 0xE000E018 // Current value register +/** SysTick register map type */ +typedef struct systick_reg_map { + __io uint32 CSR; /**< Control and status register */ + __io uint32 RVR; /**< Reload value register */ + __io uint32 CNT; /**< Current value register ("count") */ + __io uint32 CVR; /**< Calibration value register */ +} systick_reg_map; -#define SYSTICK_CSR_COUNTFLAG BIT(16) +/** SysTick register map base pointer */ +#define SYSTICK_BASE ((struct systick_reg_map*)0xE000E010) -/** System elapsed time in milliseconds */ +/* + * Register bit definitions. + */ + +/* Control and status register */ + +#define SYSTICK_CSR_COUNTFLAG BIT(16) +#define SYSTICK_CSR_CLKSOURCE BIT(2) +#define SYSTICK_CSR_CLKSOURCE_EXTERNAL 0 +#define SYSTICK_CSR_CLKSOURCE_CORE BIT(2) +#define SYSTICK_CSR_TICKINT BIT(1) +#define SYSTICK_CSR_TICKINT_PEND BIT(1) +#define SYSTICK_CSR_TICKINT_NO_PEND 0 +#define SYSTICK_CSR_ENABLE BIT(0) +#define SYSTICK_CSR_ENABLE_MULTISHOT BIT(0) +#define SYSTICK_CSR_ENABLE_DISABLED 0 + +/* Calibration value register */ + +#define SYSTICK_CVR_NOREF BIT(31) +#define SYSTICK_CVR_SKEW BIT(30) +#define SYSTICK_CVR_TENMS 0xFFFFFF + +/** System elapsed time, in milliseconds */ extern volatile uint32 systick_timer_millis; void systick_init(uint32 reload_val); void systick_disable(); -void systick_resume(); +void systick_enable(); static inline uint32 systick_get_count(void) { - return __read(SYSTICK_CNT); + return SYSTICK_BASE->CNT; } static inline uint32 systick_check_underflow(void) { - return (__read(SYSTICK_CSR) & SYSTICK_CSR_COUNTFLAG); + return SYSTICK_BASE->CSR & SYSTICK_CSR_COUNTFLAG; } #ifdef __cplusplus diff --git a/libmaple/timer.c b/libmaple/timer.c new file mode 100644 index 0000000..b22ffb4 --- /dev/null +++ b/libmaple/timer.c @@ -0,0 +1,443 @@ +/****************************************************************************** + * The MIT License + * + * Copyright (c) 2011 LeafLabs, LLC. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + *****************************************************************************/ + +/** + * @file timer.c + * @author Marti Bolivar <mbolivar@leaflabs.com> + * @brief New-style timer interface + */ + +#include "timer.h" + +/* Just like the corresponding DIER bits: + * [0] = Update handler; + * [1,2,3,4] = capture/compare 1,2,3,4 handlers, respectively; + * [5] = COM; + * [6] = TRG; + * [7] = BRK. */ +#define NR_ADV_HANDLERS 8 +/* Update, capture/compare 1,2,3,4; <junk>; trigger. */ +#define NR_GEN_HANDLERS 6 +/* Update only. */ +#define NR_BAS_HANDLERS 1 + +static timer_dev timer1 = { + .regs = { .adv = TIMER1_BASE }, + .clk_id = RCC_TIMER1, + .type = TIMER_ADVANCED, + .handlers = { [NR_ADV_HANDLERS - 1] = 0 }, +}; +timer_dev *TIMER1 = &timer1; + +static timer_dev timer2 = { + .regs = { .gen = TIMER2_BASE }, + .clk_id = RCC_TIMER2, + .type = TIMER_GENERAL, + .handlers = { [NR_GEN_HANDLERS - 1] = 0 }, +}; +timer_dev *TIMER2 = &timer2; + +static timer_dev timer3 = { + .regs = { .gen = TIMER3_BASE }, + .clk_id = RCC_TIMER3, + .type = TIMER_GENERAL, + .handlers = { [NR_GEN_HANDLERS - 1] = 0 }, +}; +timer_dev *TIMER3 = &timer3; + +static timer_dev timer4 = { + .regs = { .gen = TIMER4_BASE }, + .clk_id = RCC_TIMER4, + .type = TIMER_GENERAL, + .handlers = { [NR_GEN_HANDLERS - 1] = 0 }, +}; +timer_dev *TIMER4 = &timer4; + +#ifdef STM32_HIGH_DENSITY +static timer_dev timer5 = { + .regs = { .gen = TIMER5_BASE }, + .clk_id = RCC_TIMER5, + .type = TIMER_GENERAL, + .handlers = { [NR_GEN_HANDLERS - 1] = 0 }, +}; +timer_dev *TIMER5 = &timer5; + +static timer_dev timer6 = { + .regs = { .bas = TIMER6_BASE }, + .clk_id = RCC_TIMER6, + .type = TIMER_BASIC, + .handlers = { [NR_BAS_HANDLERS - 1] = 0 }, +}; +timer_dev *TIMER6 = &timer6; + +static timer_dev timer7 = { + .regs = { .bas = TIMER7_BASE }, + .clk_id = RCC_TIMER7, + .type = TIMER_BASIC, + .handlers = { [NR_BAS_HANDLERS - 1] = 0 }, +}; +timer_dev *TIMER7 = &timer7; + +static timer_dev timer8 = { + .regs = { .adv = TIMER8_BASE }, + .clk_id = RCC_TIMER8, + .type = TIMER_ADVANCED, + .handlers = { [NR_ADV_HANDLERS - 1] = 0 }, +}; +timer_dev *TIMER8 = &timer8; +#endif + +/* + * Convenience routines + */ + +static void disable_channel(timer_dev *dev, uint8 channel); +static void pwm_mode(timer_dev *dev, uint8 channel); +static void output_compare_mode(timer_dev *dev, uint8 channel); + +static inline void enable_irq(timer_dev *dev, uint8 interrupt); + +/** + * Initialize a timer, and reset its register map. + * @param dev Timer to initialize + */ +void timer_init(timer_dev *dev) { + rcc_clk_enable(dev->clk_id); + rcc_reset_dev(dev->clk_id); +} + +/** + * @brief Disable a timer. + * + * The timer will stop counting, all DMA requests and interrupts will + * be disabled, and no state changes will be output. + * + * @param dev Timer to disable. + */ +void timer_disable(timer_dev *dev) { + (dev->regs).bas->CR1 = 0; + (dev->regs).bas->DIER = 0; + switch (dev->type) { + case TIMER_ADVANCED: /* fall-through */ + case TIMER_GENERAL: + (dev->regs).gen->CCER = 0; + break; + case TIMER_BASIC: + break; + } +} + +/** + * Sets the mode of an individual timer channel. + * + * Note that not all timers can be configured in every mode. For + * example, basic timers cannot be configured to output compare mode. + * Be sure to use a timer which is appropriate for the mode you want. + * + * @param dev Timer whose channel mode to set + * @param channel Relevant channel + * @param mode New timer mode for channel + */ +void timer_set_mode(timer_dev *dev, uint8 channel, timer_mode mode) { + ASSERT_FAULT(channel > 0 && channel <= 4); + + /* TODO decide about the basic timers */ + ASSERT(dev->type != TIMER_BASIC); + if (dev->type == TIMER_BASIC) + return; + + switch (mode) { + case TIMER_DISABLED: + disable_channel(dev, channel); + break; + case TIMER_PWM: + pwm_mode(dev, channel); + break; + case TIMER_OUTPUT_COMPARE: + output_compare_mode(dev, channel); + break; + } +} + +/** + * @brief Call a function on timer devices. + * @param fn Function to call on each timer device. + */ +void timer_foreach(void (*fn)(timer_dev*)) { + fn(TIMER1); + fn(TIMER2); + fn(TIMER3); + fn(TIMER4); +#ifdef STM32_HIGH_DENSITY + fn(TIMER5); + fn(TIMER6); + fn(TIMER7); + fn(TIMER8); +#endif +} + +/** + * @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_interrupt(dev, interrupt); + enable_irq(dev, interrupt); +} + +/** + * @brief Detach a timer interrupt. + * @param dev Timer device + * @param interrupt Interrupt number to detach; this may be any + * timer_interrupt_id or timer_channel value appropriate + * for the timer. + * @see timer_interrupt_id + * @see timer_channel + */ +void timer_detach_interrupt(timer_dev *dev, uint8 interrupt) { + timer_disable_interrupt(dev, interrupt); + dev->handlers[interrupt] = NULL; +} + +/* + * IRQ handlers + */ + +static inline void dispatch_adv_brk(timer_dev *dev); +static inline void dispatch_adv_up(timer_dev *dev); +static inline void dispatch_adv_trg_com(timer_dev *dev); +static inline void dispatch_adv_cc(timer_dev *dev); +static inline void dispatch_general(timer_dev *dev); +static inline void dispatch_basic(timer_dev *dev); + +void __irq_tim1_brk(void) { + dispatch_adv_brk(TIMER1); +} + +void __irq_tim1_up(void) { + dispatch_adv_up(TIMER1); +} + +void __irq_tim1_trg_com(void) { + dispatch_adv_trg_com(TIMER1); +} + +void __irq_tim1_cc(void) { + dispatch_adv_cc(TIMER1); +} + +void __irq_tim2(void) { + dispatch_general(TIMER2); +} + +void __irq_tim3(void) { + dispatch_general(TIMER3); +} + +void __irq_tim4(void) { + dispatch_general(TIMER4); +} + +#if defined(STM32_HIGH_DENSITY) || defined(STM32_XL_DENSITY) + +void __irq_tim5(void) { + dispatch_general(TIMER5); +} + +void __irq_tim6(void) { + dispatch_basic(TIMER6); +} + +void __irq_tim7(void) { + dispatch_basic(TIMER7); +} + +void __irq_tim8_brk(void) { + dispatch_adv_brk(TIMER8); +} + +void __irq_tim8_up(void) { + dispatch_adv_up(TIMER8); +} + +void __irq_tim8_trg_com(void) { + dispatch_adv_trg_com(TIMER8); +} + +void __irq_tim8_cc(void) { + dispatch_adv_cc(TIMER8); +} +#endif + +static inline void dispatch_irq(timer_dev *dev, uint8 iid, uint8 sr_bit); +static inline void dispatch_cc_irqs(timer_dev *dev); + +static inline void dispatch_adv_brk(timer_dev *dev) { + dispatch_irq(dev, TIMER_BREAK_INTERRUPT, TIMER_SR_BIF_BIT); +} + +static inline void dispatch_adv_up(timer_dev *dev) { + dispatch_irq(dev, TIMER_UPDATE_INTERRUPT, TIMER_SR_UIF_BIT); +} + +static inline void dispatch_adv_trg_com(timer_dev *dev) { + dispatch_irq(dev, TIMER_TRG_INTERRUPT, TIMER_SR_TIF_BIT); + dispatch_irq(dev, TIMER_COM_INTERRUPT, TIMER_SR_COMIF_BIT); +} + +static inline void dispatch_adv_cc(timer_dev *dev) { + dispatch_cc_irqs(dev); +} + +static inline void dispatch_general(timer_dev *dev) { + dispatch_irq(dev, TIMER_TRG_INTERRUPT, TIMER_SR_TIF_BIT); + dispatch_cc_irqs(dev); + dispatch_irq(dev, TIMER_UPDATE_INTERRUPT, TIMER_SR_UIF_BIT); +} + +static inline void dispatch_basic(timer_dev *dev) { + dispatch_irq(dev, TIMER_UPDATE_INTERRUPT, TIMER_SR_UIF_BIT); +} + +static inline void dispatch_irq(timer_dev *dev, uint8 iid, uint8 sr_bit) { + __io uint32 *sr = &(dev->regs).bas->SR; + if (bb_peri_get_bit(sr, sr_bit)) { + if (dev->handlers[iid]) + (dev->handlers[iid])(); + bb_peri_set_bit(sr, sr_bit, 0); + } +} + +static inline void dispatch_cc_irqs(timer_dev *dev) { + uint32 sr = (dev->regs).gen->SR; + uint32 sr_clear = 0; + uint32 b; + + ASSERT_FAULT(sr & (TIMER_SR_CC1IF | TIMER_SR_CC2IF | + TIMER_SR_CC3IF | TIMER_SR_CC4IF)); + + for (b = TIMER_SR_CC1IF_BIT; b <= TIMER_SR_CC4IF_BIT; b++) { + uint32 mask = BIT(b); + if (sr & mask) { + if (dev->handlers[b]) + (dev->handlers[b])(); + sr_clear |= mask; + } + } + + (dev->regs).gen->SR &= ~sr_clear; +} + +/* + * Utilities + */ + +static void disable_channel(timer_dev *dev, uint8 channel) { + timer_detach_interrupt(dev, channel); + timer_cc_disable(dev, channel); +} + +static void pwm_mode(timer_dev *dev, uint8 channel) { + timer_disable_interrupt(dev, channel); + timer_oc_set_mode(dev, channel, TIMER_OC_MODE_PWM_1, TIMER_OC_PE); + timer_cc_enable(dev, channel); +} + +static void output_compare_mode(timer_dev *dev, uint8 channel) { + timer_oc_set_mode(dev, channel, TIMER_OC_MODE_ACTIVE_ON_MATCH, 0); + timer_cc_enable(dev, channel); +} + +static void enable_advanced_irq(timer_dev *dev, timer_interrupt_id id); +static void enable_nonmuxed_irq(timer_dev *dev); + +static inline void enable_irq(timer_dev *dev, timer_interrupt_id iid) { + if (dev->type == TIMER_ADVANCED) { + enable_advanced_irq(dev, iid); + } else { + enable_nonmuxed_irq(dev); + } +} + +static void enable_advanced_irq(timer_dev *dev, timer_interrupt_id id) { + uint8 is_timer1 = dev->clk_id == RCC_TIMER1; + + switch (id) { + case TIMER_UPDATE_INTERRUPT: + nvic_irq_enable(is_timer1 ? NVIC_TIMER1_UP : NVIC_TIMER8_UP); + break; + case TIMER_CC1_INTERRUPT: + case TIMER_CC2_INTERRUPT: + case TIMER_CC3_INTERRUPT: + case TIMER_CC4_INTERRUPT: + nvic_irq_enable(is_timer1 ? NVIC_TIMER1_CC : NVIC_TIMER8_CC); + break; + case TIMER_COM_INTERRUPT: + case TIMER_TRG_INTERRUPT: + nvic_irq_enable(is_timer1 ? NVIC_TIMER1_TRG_COM : NVIC_TIMER8_TRG_COM); + break; + case TIMER_BREAK_INTERRUPT: + nvic_irq_enable(is_timer1 ? NVIC_TIMER1_BRK : NVIC_TIMER8_BRK); + break; + } +} + +static void enable_nonmuxed_irq(timer_dev *dev) { + switch (dev->clk_id) { + case RCC_TIMER2: + nvic_irq_enable(NVIC_TIMER2); + break; + case RCC_TIMER3: + nvic_irq_enable(NVIC_TIMER3); + break; + case RCC_TIMER4: + nvic_irq_enable(NVIC_TIMER4); + break; +#ifdef STM32_HIGH_DENSITY + case RCC_TIMER5: + nvic_irq_enable(NVIC_TIMER5); + break; + case RCC_TIMER6: + nvic_irq_enable(NVIC_TIMER6); + break; + case RCC_TIMER7: + nvic_irq_enable(NVIC_TIMER7); + break; +#endif + default: + ASSERT_FAULT(0); + break; + } +} diff --git a/libmaple/timer.h b/libmaple/timer.h new file mode 100644 index 0000000..befc026 --- /dev/null +++ b/libmaple/timer.h @@ -0,0 +1,1011 @@ +/****************************************************************************** + * The MIT License + * + * Copyright (c) 2011 LeafLabs, LLC. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + *****************************************************************************/ + +/** + * @file timer.h + * @author Marti Bolivar <mbolivar@leaflabs.com> + * @brief New-style timer interface. + * + * Replaces old timers.h implementation. + */ + +#ifndef _TIMERS_H_ +#define _TIMERS_H_ + +#include "libmaple.h" +#include "rcc.h" +#include "nvic.h" +#include "bitband.h" + +#ifdef __cplusplus +extern "C"{ +#endif + +/* + * Register maps and devices + */ + +/** Advanced control timer register map type */ +typedef struct timer_adv_reg_map { + __io uint32 CR1; /**< Control register 1 */ + __io uint32 CR2; /**< Control register 2 */ + __io uint32 SMCR; /**< Slave mode control register */ + __io uint32 DIER; /**< DMA/Interrupt enable register */ + __io uint32 SR; /**< Status register */ + __io uint32 EGR; /**< Event generation register */ + __io uint32 CCMR1; /**< Capture/compare mode register 1 */ + __io uint32 CCMR2; /**< Capture/compare mode register 2 */ + __io uint32 CCER; /**< Capture/compare enable register */ + __io uint32 CNT; /**< Counter */ + __io uint32 PSC; /**< Prescaler */ + __io uint32 ARR; /**< Auto-reload register */ + __io uint32 RCR; /**< Repetition counter register */ + __io uint32 CCR1; /**< Capture/compare register 1 */ + __io uint32 CCR2; /**< Capture/compare register 2 */ + __io uint32 CCR3; /**< Capture/compare register 3 */ + __io uint32 CCR4; /**< Capture/compare register 4 */ + __io uint32 BDTR; /**< Break and dead-time register */ + __io uint32 DCR; /**< DMA control register */ + __io uint32 DMAR; /**< DMA address for full transfer */ +} timer_adv_reg_map; + +/** General purpose timer register map type */ +typedef struct timer_gen_reg_map { + __io uint32 CR1; /**< Control register 1 */ + __io uint32 CR2; /**< Control register 2 */ + __io uint32 SMCR; /**< Slave mode control register */ + __io uint32 DIER; /**< DMA/Interrupt enable register */ + __io uint32 SR; /**< Status register */ + __io uint32 EGR; /**< Event generation register */ + __io uint32 CCMR1; /**< Capture/compare mode register 1 */ + __io uint32 CCMR2; /**< Capture/compare mode register 2 */ + __io uint32 CCER; /**< Capture/compare enable register */ + __io uint32 CNT; /**< Counter */ + __io uint32 PSC; /**< Prescaler */ + __io uint32 ARR; /**< Auto-reload register */ + const uint32 RESERVED1; /**< Reserved */ + __io uint32 CCR1; /**< Capture/compare register 1 */ + __io uint32 CCR2; /**< Capture/compare register 2 */ + __io uint32 CCR3; /**< Capture/compare register 3 */ + __io uint32 CCR4; /**< Capture/compare register 4 */ + const uint32 RESERVED2; /**< Reserved */ + __io uint32 DCR; /**< DMA control register */ + __io uint32 DMAR; /**< DMA address for full transfer */ +} timer_gen_reg_map; + +/** Basic timer register map type */ +typedef struct timer_bas_reg_map { + __io uint32 CR1; /**< Control register 1 */ + __io uint32 CR2; /**< Control register 2 */ + const uint32 RESERVED1; /**< Reserved */ + __io uint32 DIER; /**< DMA/Interrupt enable register */ + __io uint32 SR; /**< Status register */ + __io uint32 EGR; /**< Event generation register */ + const uint32 RESERVED2; /**< Reserved */ + const uint32 RESERVED3; /**< Reserved */ + const uint32 RESERVED4; /**< Reserved */ + __io uint32 CNT; /**< Counter */ + __io uint32 PSC; /**< Prescaler */ + __io uint32 ARR; /**< Auto-reload register */ +} timer_bas_reg_map; + +/** Timer 1 register map base pointer */ +#define TIMER1_BASE ((struct timer_adv_reg_map*)0x40012C00) +/** Timer 2 register map base pointer */ +#define TIMER2_BASE ((struct timer_gen_reg_map*)0x40000000) +/** Timer 3 register map base pointer */ +#define TIMER3_BASE ((struct timer_gen_reg_map*)0x40000400) +/** Timer 4 register map base pointer */ +#define TIMER4_BASE ((struct timer_gen_reg_map*)0x40000800) +#ifdef STM32_HIGH_DENSITY +/** Timer 5 register map base pointer */ +#define TIMER5_BASE ((struct timer_gen_reg_map*)0x40000C00) +/** Timer 6 register map base pointer */ +#define TIMER6_BASE ((struct timer_bas_reg_map*)0x40001000) +/** Timer 7 register map base pointer */ +#define TIMER7_BASE ((struct timer_bas_reg_map*)0x40001400) +/** Timer 8 register map base pointer */ +#define TIMER8_BASE ((struct timer_adv_reg_map*)0x40013400) +#endif + +/* + * Timer devices + */ + +typedef union { + timer_adv_reg_map *adv; + timer_gen_reg_map *gen; + timer_bas_reg_map *bas; +} timer_reg_map_union; + +typedef enum { + TIMER_ADVANCED, + TIMER_GENERAL, + TIMER_BASIC +} timer_type; + +/** Timer device type */ +typedef struct timer_dev { + timer_reg_map_union regs; + rcc_clk_id clk_id; + timer_type type; + voidFuncPtr handlers[]; +} timer_dev; + +/** Timer 1 device (advanced) */ +extern timer_dev *TIMER1; +/** Timer 2 device (general-purpose) */ +extern timer_dev *TIMER2; +/** Timer 3 device (general-purpose) */ +extern timer_dev *TIMER3; +/** Timer 4 device (general-purpose) */ +extern timer_dev *TIMER4; +#ifdef STM32_HIGH_DENSITY +/** Timer 5 device (general-purpose) */ +extern timer_dev *TIMER5; +/** Timer 6 device (basic) */ +extern timer_dev *TIMER6; +/** Timer 7 device (basic) */ +extern timer_dev *TIMER7; +/** Timer 8 device (advanced) */ +extern timer_dev *TIMER8; +#endif + +/* + * Register bit definitions + */ + +/* Control register 1 (CR1) */ + +#define TIMER_CR1_ARPE_BIT 7 +#define TIMER_CR1_DIR_BIT 4 +#define TIMER_CR1_OPM_BIT 3 +#define TIMER_CR1_URS_BIT 2 +#define TIMER_CR1_UDIS_BIT 1 +#define TIMER_CR1_CEN_BIT 0 + +#define TIMER_CR1_CKD (0x3 << 8) +#define TIMER_CR1_CKD_1TCKINT (0x0 << 8) +#define TIMER_CR1_CKD_2TCKINT (0x1 << 8) +#define TIMER_CR1_CKD_4TICKINT (0x2 << 8) +#define TIMER_CR1_ARPE BIT(TIMER_CR1_ARPE_BIT) +#define TIMER_CR1_CKD_CMS (0x3 << 5) +#define TIMER_CR1_CKD_CMS_EDGE (0x0 << 5) +#define TIMER_CR1_CKD_CMS_CENTER1 (0x1 << 5) +#define TIMER_CR1_CKD_CMS_CENTER2 (0x2 << 5) +#define TIMER_CR1_CKD_CMS_CENTER3 (0x3 << 5) +#define TIMER_CR1_DIR BIT(TIMER_CR1_DIR_BIT) +#define TIMER_CR1_OPM BIT(TIMER_CR1_OPM_BIT) +#define TIMER_CR1_URS BIT(TIMER_CR1_URS_BIT) +#define TIMER_CR1_UDIS BIT(TIMER_CR1_UDIS_BIT) +#define TIMER_CR1_CEN BIT(TIMER_CR1_CEN_BIT) + +/* Control register 2 (CR2) */ + +#define TIMER_CR2_OIS4_BIT 14 +#define TIMER_CR2_OIS3N_BIT 13 +#define TIMER_CR2_OIS3_BIT 12 +#define TIMER_CR2_OIS2N_BIT 11 +#define TIMER_CR2_OIS2_BIT 10 +#define TIMER_CR2_OIS1N_BIT 9 +#define TIMER_CR2_OIS1_BIT 8 +#define TIMER_CR2_TI1S_BIT 7 /* tills? yikes */ +#define TIMER_CR2_CCDS_BIT 3 +#define TIMER_CR2_CCUS_BIT 2 +#define TIMER_CR2_CCPC_BIT 0 + +#define TIMER_CR2_OIS4 BIT(TIMER_CR2_OIS4_BIT) +#define TIMER_CR2_OIS3N BIT(TIMER_CR2_OIS3N_BIT) +#define TIMER_CR2_OIS3 BIT(TIMER_CR2_OIS3_BIT) +#define TIMER_CR2_OIS2N BIT(TIMER_CR2_OIS2N_BIT) +#define TIMER_CR2_OIS2 BIT(TIMER_CR2_OIS2_BIT) +#define TIMER_CR2_OIS1N BIT(TIMER_CR2_OIS1N_BIT) +#define TIMER_CR2_OIS1 BIT(TIMER_CR2_OIS1_BIT) +#define TIMER_CR2_TI1S BIT(TIMER_CR2_TI1S_BIT) +#define TIMER_CR2_MMS (0x7 << 4) +#define TIMER_CR2_MMS_RESET (0x0 << 4) +#define TIMER_CR2_MMS_ENABLE (0x1 << 4) +#define TIMER_CR2_MMS_UPDATE (0x2 << 4) +#define TIMER_CR2_MMS_COMPARE_PULSE (0x3 << 4) +#define TIMER_CR2_MMS_COMPARE_OC1REF (0x4 << 4) +#define TIMER_CR2_MMS_COMPARE_OC2REF (0x5 << 4) +#define TIMER_CR2_MMS_COMPARE_OC3REF (0x6 << 4) +#define TIMER_CR2_MMS_COMPARE_OC4REF (0x7 << 4) +#define TIMER_CR2_CCDS BIT(TIMER_CR2_CCDS_BIT) +#define TIMER_CR2_CCUS BIT(TIMER_CR2_CCUS_BIT) +#define TIMER_CR2_CCPC BIT(TIMER_CR2_CCPC_BIT) + +/* Slave mode control register (SMCR) */ + +#define TIMER_SMCR_ETP_BIT 15 +#define TIMER_SMCR_ECE_BIT 14 +#define TIMER_SMCR_MSM_BIT 7 + +#define TIMER_SMCR_ETP BIT(TIMER_SMCR_ETP_BIT) +#define TIMER_SMCR_ECE BIT(TIMER_SMCR_ECE_BIT) +#define TIMER_SMCR_ETPS (0x3 << 12) +#define TIMER_SMCR_ETPS_OFF (0x0 << 12) +#define TIMER_SMCR_ETPS_DIV2 (0x1 << 12) +#define TIMER_SMCR_ETPS_DIV4 (0x2 << 12) +#define TIMER_SMCR_ETPS_DIV8 (0x3 << 12) +#define TIMER_SMCR_ETF (0xF << 12) +#define TIMER_SMCR_MSM BIT(TIMER_SMCR_MSM_BIT) +#define TIMER_SMCR_TS (0x3 << 4) +#define TIMER_SMCR_TS_ITR0 (0x0 << 4) +#define TIMER_SMCR_TS_ITR1 (0x1 << 4) +#define TIMER_SMCR_TS_ITR2 (0x2 << 4) +#define TIMER_SMCR_TS_ITR3 (0x3 << 4) +#define TIMER_SMCR_TS_TI1F_ED (0x4 << 4) +#define TIMER_SMCR_TS_TI1FP1 (0x5 << 4) +#define TIMER_SMCR_TS_TI2FP2 (0x6 << 4) +#define TIMER_SMCR_TS_ETRF (0x7 << 4) +#define TIMER_SMCR_SMS 0x3 +#define TIMER_SMCR_SMS_DISABLED 0x0 +#define TIMER_SMCR_SMS_ENCODER1 0x1 +#define TIMER_SMCR_SMS_ENCODER2 0x2 +#define TIMER_SMCR_SMS_ENCODER3 0x3 +#define TIMER_SMCR_SMS_RESET 0x4 +#define TIMER_SMCR_SMS_GATED 0x5 +#define TIMER_SMCR_SMS_TRIGGER 0x6 +#define TIMER_SMCR_SMS_EXTERNAL 0x7 + +/* DMA/Interrupt enable register (DIER) */ + +#define TIMER_DIER_TDE_BIT 14 +#define TIMER_DIER_CC4DE_BIT 12 +#define TIMER_DIER_CC3DE_BIT 11 +#define TIMER_DIER_CC2DE_BIT 10 +#define TIMER_DIER_CC1DE_BIT 9 +#define TIMER_DIER_UDE_BIT 8 +#define TIMER_DIER_TIE_BIT 6 +#define TIMER_DIER_CC4IE_BIT 4 +#define TIMER_DIER_CC3IE_BIT 3 +#define TIMER_DIER_CC2IE_BIT 2 +#define TIMER_DIER_CC1IE_BIT 1 +#define TIMER_DIER_UIE_BIT 0 + +#define TIMER_DIER_TDE BIT(TIMER_DIER_TDE_BIT) +#define TIMER_DIER_CC4DE BIT(TIMER_DIER_CC4DE_BIT) +#define TIMER_DIER_CC3DE BIT(TIMER_DIER_CC3DE_BIT) +#define TIMER_DIER_CC2DE BIT(TIMER_DIER_CC2DE_BIT) +#define TIMER_DIER_CC1DE BIT(TIMER_DIER_CC1DE_BIT) +#define TIMER_DIER_UDE BIT(TIMER_DIER_UDE_BIT) +#define TIMER_DIER_TIE BIT(TIMER_DIER_TIE_BIT) +#define TIMER_DIER_CC4IE BIT(TIMER_DIER_CC4IE_BIT) +#define TIMER_DIER_CC3IE BIT(TIMER_DIER_CC3IE_BIT) +#define TIMER_DIER_CC2IE BIT(TIMER_DIER_CC2IE_BIT) +#define TIMER_DIER_CC1IE BIT(TIMER_DIER_CC1IE_BIT) +#define TIMER_DIER_UIE BIT(TIMER_DIER_UIE_BIT) + +/* Status register (SR) */ + +#define TIMER_SR_CC4OF_BIT 12 +#define TIMER_SR_CC3OF_BIT 11 +#define TIMER_SR_CC2OF_BIT 10 +#define TIMER_SR_CC1OF_BIT 9 +#define TIMER_SR_BIF_BIT 7 +#define TIMER_SR_TIF_BIT 6 +#define TIMER_SR_COMIF_BIT 5 +#define TIMER_SR_CC4IF_BIT 4 +#define TIMER_SR_CC3IF_BIT 3 +#define TIMER_SR_CC2IF_BIT 2 +#define TIMER_SR_CC1IF_BIT 1 +#define TIMER_SR_UIF_BIT 0 + +#define TIMER_SR_CC4OF BIT(TIMER_SR_CC4OF_BIT) +#define TIMER_SR_CC3OF BIT(TIMER_SR_CC3OF_BIT) +#define TIMER_SR_CC2OF BIT(TIMER_SR_CC2OF_BIT) +#define TIMER_SR_CC1OF BIT(TIMER_SR_CC1OF_BIT) +#define TIMER_SR_BIF BIT(TIMER_SR_BIF_BIT) +#define TIMER_SR_TIF BIT(TIMER_SR_TIF_BIT) +#define TIMER_SR_COMIF BIT(TIMER_SR_COMIF_BIT) +#define TIMER_SR_CC4IF BIT(TIMER_SR_CC4IF_BIT) +#define TIMER_SR_CC3IF BIT(TIMER_SR_CC3IF_BIT) +#define TIMER_SR_CC2IF BIT(TIMER_SR_CC2IF_BIT) +#define TIMER_SR_CC1IF BIT(TIMER_SR_CC1IF_BIT) +#define TIMER_SR_UIF BIT(TIMER_SR_UIF_BIT) + +/* Event generation register (EGR) */ + +#define TIMER_EGR_TG_BIT 6 +#define TIMER_EGR_CC4G_BIT 4 +#define TIMER_EGR_CC3G_BIT 3 +#define TIMER_EGR_CC2G_BIT 2 +#define TIMER_EGR_CC1G_BIT 1 +#define TIMER_EGR_UG_BIT 0 + +#define TIMER_EGR_TG BIT(TIMER_EGR_TG_BIT) +#define TIMER_EGR_CC4G BIT(TIMER_EGR_CC4G_BIT) +#define TIMER_EGR_CC3G BIT(TIMER_EGR_CC3G_BIT) +#define TIMER_EGR_CC2G BIT(TIMER_EGR_CC2G_BIT) +#define TIMER_EGR_CC1G BIT(TIMER_EGR_CC1G_BIT) +#define TIMER_EGR_UG BIT(TIMER_EGR_UG_BIT) + +/* Capture/compare mode registers, common values */ + +#define TIMER_CCMR_CCS_OUTPUT 0x0 +#define TIMER_CCMR_CCS_INPUT_TI1 0x1 +#define TIMER_CCMR_CCS_INPUT_TI2 0x2 +#define TIMER_CCMR_CCS_INPUT_TRC 0x3 + +/* Capture/compare mode register 1 (CCMR1) */ + +#define TIMER_CCMR1_OC2CE_BIT 15 +#define TIMER_CCMR1_OC2PE_BIT 11 +#define TIMER_CCMR1_OC2FE_BIT 10 +#define TIMER_CCMR1_OC1CE_BIT 7 +#define TIMER_CCMR1_OC1PE_BIT 3 +#define TIMER_CCMR1_OC1FE_BIT 2 + +#define TIMER_CCMR1_OC2CE BIT(TIMER_CCMR1_OC2CE_BIT) +#define TIMER_CCMR1_OC2M (0x3 << 12) +#define TIMER_CCMR1_IC2F (0xF << 12) +#define TIMER_CCMR1_OC2PE BIT(TIMER_CCMR1_OC2PE_BIT) +#define TIMER_CCMR1_OC2FE BIT(TIMER_CCMR1_OC2FE_BIT) +#define TIMER_CCMR1_IC2PSC (0x3 << 10) +#define TIMER_CCMR1_CC2S (0x3 << 8) +#define TIMER_CCMR1_CC2S_OUTPUT (TIMER_CCMR_CCS_OUTPUT << 8) +#define TIMER_CCMR1_CC2S_INPUT_TI1 (TIMER_CCMR_CCS_INPUT_TI1 << 8) +#define TIMER_CCMR1_CC2S_INPUT_TI2 (TIMER_CCMR_CCS_INPUT_TI2 << 8) +#define TIMER_CCMR1_CC2S_INPUT_TRC (TIMER_CCMR_CCS_INPUT_TRC << 8) +#define TIMER_CCMR1_OC1CE BIT(TIMER_CCMR1_OC1CE_BIT) +#define TIMER_CCMR1_OC1M (0x3 << 4) +#define TIMER_CCMR1_IC1F (0xF << 4) +#define TIMER_CCMR1_OC1PE BIT(TIMER_CCMR1_OC1PE_BIT) +#define TIMER_CCMR1_OC1FE BIT(TIMER_CCMR1_OC1FE_BIT) +#define TIMER_CCMR1_IC1PSC (0x3 << 2) +#define TIMER_CCMR1_CC1S 0x3 +#define TIMER_CCMR1_CC1S_OUTPUT TIMER_CCMR_CCS_OUTPUT +#define TIMER_CCMR1_CC1S_INPUT_TI1 TIMER_CCMR_CCS_INPUT_TI1 +#define TIMER_CCMR1_CC1S_INPUT_TI2 TIMER_CCMR_CCS_INPUT_TI2 +#define TIMER_CCMR1_CC1S_INPUT_TRC TIMER_CCMR_CCS_INPUT_TRC + +/* Capture/compare mode register 2 (CCMR2) */ + +#define TIMER_CCMR2_OC4CE_BIT 15 +#define TIMER_CCMR2_OC4PE_BIT 11 +#define TIMER_CCMR2_OC4FE_BIT 10 +#define TIMER_CCMR2_OC3CE_BIT 7 +#define TIMER_CCMR2_OC3PE_BIT 3 +#define TIMER_CCMR2_OC3FE_BIT 2 + +#define TIMER_CCMR2_OC4CE BIT(TIMER_CCMR2_OC4CE_BIT) +#define TIMER_CCMR2_OC4M (0x3 << 12) +#define TIMER_CCMR2_IC2F (0xF << 12) +#define TIMER_CCMR2_OC4PE BIT(TIMER_CCMR2_OC4PE_BIT) +#define TIMER_CCMR2_OC4FE BIT(TIMER_CCMR2_OC4FE_BIT) +#define TIMER_CCMR2_IC2PSC (0x3 << 10) +#define TIMER_CCMR2_CC4S (0x3 << 8) +#define TIMER_CCMR1_CC4S_OUTPUT (TIMER_CCMR_CCS_OUTPUT << 8) +#define TIMER_CCMR1_CC4S_INPUT_TI1 (TIMER_CCMR_CCS_INPUT_TI1 << 8) +#define TIMER_CCMR1_CC4S_INPUT_TI2 (TIMER_CCMR_CCS_INPUT_TI2 << 8) +#define TIMER_CCMR1_CC4S_INPUT_TRC (TIMER_CCMR_CCS_INPUT_TRC << 8) +#define TIMER_CCMR2_OC3CE BIT(TIMER_CCMR2_OC3CE_BIT) +#define TIMER_CCMR2_OC3M (0x3 << 4) +#define TIMER_CCMR2_IC1F (0xF << 4) +#define TIMER_CCMR2_OC3PE BIT(TIMER_CCMR2_OC3PE_BIT) +#define TIMER_CCMR2_OC3FE BIT(TIMER_CCMR2_OC3FE_BIT) +#define TIMER_CCMR2_IC1PSC (0x3 << 2) +#define TIMER_CCMR2_CC3S 0x3 +#define TIMER_CCMR1_CC3S_OUTPUT TIMER_CCMR_CCS_OUTPUT +#define TIMER_CCMR1_CC3S_INPUT_TI1 TIMER_CCMR_CCS_INPUT_TI1 +#define TIMER_CCMR1_CC3S_INPUT_TI2 TIMER_CCMR_CCS_INPUT_TI2 +#define TIMER_CCMR1_CC3S_INPUT_TRC TIMER_CCMR_CCS_INPUT_TRC + +/* Capture/compare enable register (CCER) */ + +#define TIMER_CCER_CC4P_BIT 13 +#define TIMER_CCER_CC4E_BIT 12 +#define TIMER_CCER_CC3P_BIT 9 +#define TIMER_CCER_CC3E_BIT 8 +#define TIMER_CCER_CC2P_BIT 5 +#define TIMER_CCER_CC2E_BIT 4 +#define TIMER_CCER_CC1P_BIT 1 +#define TIMER_CCER_CC1E_BIT 0 + +#define TIMER_CCER_CC4P BIT(TIMER_CCER_CC4P_BIT) +#define TIMER_CCER_CC4E BIT(TIMER_CCER_CC4E_BIT) +#define TIMER_CCER_CC3P BIT(TIMER_CCER_CC3P_BIT) +#define TIMER_CCER_CC3E BIT(TIMER_CCER_CC3E_BIT) +#define TIMER_CCER_CC2P BIT(TIMER_CCER_CC2P_BIT) +#define TIMER_CCER_CC2E BIT(TIMER_CCER_CC2E_BIT) +#define TIMER_CCER_CC1P BIT(TIMER_CCER_CC1P_BIT) +#define TIMER_CCER_CC1E BIT(TIMER_CCER_CC1E_BIT) + +/* Break and dead-time register (BDTR) */ + +#define TIMER_BDTR_MOE_BIT 15 +#define TIMER_BDTR_AOE_BIT 14 +#define TIMER_BDTR_BKP_BIT 13 +#define TIMER_BDTR_BKE_BIT 12 +#define TIMER_BDTR_OSSR_BIT 11 +#define TIMER_BDTR_OSSI_BIT 10 + +#define TIMER_BDTR_MOE BIT(TIMER_BDTR_MOE_BIT) +#define TIMER_BDTR_AOE BIT(TIMER_BDTR_AOE_BIT) +#define TIMER_BDTR_BKP BIT(TIMER_BDTR_BKP_BIT) +#define TIMER_BDTR_BKE BIT(TIMER_BDTR_BKE_BIT) +#define TIMER_BDTR_OSSR BIT(TIMER_BDTR_OSSR_BIT) +#define TIMER_BDTR_OSSI BIT(TIMER_BDTR_OSSI_BIT) +#define TIMER_BDTR_LOCK (0x3 << 8) +#define TIMER_BDTR_LOCK_OFF (0x0 << 8) +#define TIMER_BDTR_LOCK_LEVEL1 (0x1 << 8) +#define TIMER_BDTR_LOCK_LEVEL2 (0x2 << 8) +#define TIMER_BDTR_LOCK_LEVEL3 (0x3 << 8) +#define TIMER_BDTR_DTG 0xFF + +/* DMA control register (DCR) */ + +#define TIMER_DCR_DBL (0x1F << 8) +#define TIMER_DCR_DBL_1BYTE (0x0 << 8) +#define TIMER_DCR_DBL_2BYTE (0x1 << 8) +#define TIMER_DCR_DBL_3BYTE (0x2 << 8) +#define TIMER_DCR_DBL_4BYTE (0x3 << 8) +#define TIMER_DCR_DBL_5BYTE (0x4 << 8) +#define TIMER_DCR_DBL_6BYTE (0x5 << 8) +#define TIMER_DCR_DBL_7BYTE (0x6 << 8) +#define TIMER_DCR_DBL_8BYTE (0x7 << 8) +#define TIMER_DCR_DBL_9BYTE (0x8 << 8) +#define TIMER_DCR_DBL_10BYTE (0x9 << 8) +#define TIMER_DCR_DBL_11BYTE (0xA << 8) +#define TIMER_DCR_DBL_12BYTE (0xB << 8) +#define TIMER_DCR_DBL_13BYTE (0xC << 8) +#define TIMER_DCR_DBL_14BYTE (0xD << 8) +#define TIMER_DCR_DBL_15BYTE (0xE << 8) +#define TIMER_DCR_DBL_16BYTE (0xF << 8) +#define TIMER_DCR_DBL_17BYTE (0x10 << 8) +#define TIMER_DCR_DBL_18BYTE (0x11 << 8) +#define TIMER_DCR_DBA 0x1F +#define TIMER_DCR_DBA_CR1 0x0 +#define TIMER_DCR_DBA_CR2 0x1 +#define TIMER_DCR_DBA_SMCR 0x2 +#define TIMER_DCR_DBA_DIER 0x3 +#define TIMER_DCR_DBA_SR 0x4 +#define TIMER_DCR_DBA_EGR 0x5 +#define TIMER_DCR_DBA_CCMR1 0x6 +#define TIMER_DCR_DBA_CCMR2 0x7 +#define TIMER_DCR_DBA_CCER 0x8 +#define TIMER_DCR_DBA_CNT 0x9 +#define TIMER_DCR_DBA_PSC 0xA +#define TIMER_DCR_DBA_ARR 0xB +#define TIMER_DCR_DBA_RCR 0xC +#define TIMER_DCR_DBA_CCR1 0xD +#define TIMER_DCR_DBA_CCR2 0xE +#define TIMER_DCR_DBA_CCR3 0xF +#define TIMER_DCR_DBA_CCR4 0x10 +#define TIMER_DCR_DBA_BDTR 0x11 +#define TIMER_DCR_DBA_DCR 0x12 +#define TIMER_DCR_DBA_DMAR 0x13 + +/* + * Convenience routines + */ + +/** + * Used to configure the behavior of a timer channel. Note that not + * all timers can be configured in every mode. + */ +/* TODO TIMER_PWM_CENTER_ALIGNED, TIMER_INPUT_CAPTURE, TIMER_ONE_PULSE */ +typedef enum timer_mode { + TIMER_DISABLED, /**< In this mode, the timer stops counting, + channel interrupts are detached, and no state + changes are output. */ + TIMER_PWM, /**< PWM output mode. This is the default mode for pins + after initialization. */ + /* TIMER_PWM_CENTER_ALIGNED, /\**< Center-aligned PWM output mode. *\/ */ + TIMER_OUTPUT_COMPARE, /**< In this mode, the timer counts from 0 + to its reload value repeatedly; every + time the counter value reaches one of + the channel compare values, the + corresponding interrupt is fired. */ + /* TIMER_INPUT_CAPTURE, /\**< In this mode, the timer can measure the */ + /* pulse lengths of input signals. *\/ */ + /* TIMER_ONE_PULSE /\**< In this mode, the timer can generate a single */ + /* pulse on a GPIO pin for a specified amount of */ + /* time. *\/ */ +} timer_mode; + +/** Timer channel numbers */ +typedef enum timer_channel { + TIMER_CH1 = 1, /**< Channel 1 */ + TIMER_CH2 = 2, /**< Channel 2 */ + TIMER_CH3 = 3, /**< Channel 3 */ + TIMER_CH4 = 4 /**< Channel 4 */ +} timer_channel; + +/* + * Note: Don't require timer_channel arguments! We want to be able to say + * + * for (int channel = 1; channel <= 4; channel++) { + * ... + * } + * + * without the compiler yelling at us. + */ + +void timer_init(timer_dev *dev); +void timer_disable(timer_dev *dev); +void timer_set_mode(timer_dev *dev, uint8 channel, timer_mode mode); +void timer_foreach(void (*fn)(timer_dev*)); + +/** + * @brief Timer interrupt number. + * + * Not all timers support all of these values; see the descriptions + * for each value. + */ +typedef enum timer_interrupt_id { + TIMER_UPDATE_INTERRUPT, /**< Update interrupt, available on all timers. */ + TIMER_CC1_INTERRUPT, /**< Capture/compare 1 interrupt, available + on general and advanced timers only. */ + TIMER_CC2_INTERRUPT, /**< Capture/compare 2 interrupt, general and + advanced timers only. */ + TIMER_CC3_INTERRUPT, /**< Capture/compare 3 interrupt, general and + advanced timers only. */ + TIMER_CC4_INTERRUPT, /**< Capture/compare 4 interrupt, general and + advanced timers only. */ + TIMER_COM_INTERRUPT, /**< COM interrupt, advanced timers only */ + TIMER_TRG_INTERRUPT, /**< Trigger interrupt, general and advanced + timers only */ + TIMER_BREAK_INTERRUPT /**< Break interrupt, advanced timers only. */ +} timer_interrupt_id; + +void timer_attach_interrupt(timer_dev *dev, + uint8 interrupt, + voidFuncPtr handler); +void timer_detach_interrupt(timer_dev *dev, uint8 interrupt); + +/** + * Initialize all timer devices on the chip. + */ +static inline void timer_init_all(void) { + timer_foreach(timer_init); +} + +/** + * Disables all timers on the device. + */ +static inline void timer_disable_all(void) { + timer_foreach(timer_disable); +} + +/** + * @brief Stop a timer's counter from changing. + * + * Does not affect the timer's mode or other settings. + * + * @param dev Device whose counter to pause. + */ +static inline void timer_pause(timer_dev *dev) { + *bb_perip(&(dev->regs).bas->CR1, TIMER_CR1_CEN_BIT) = 0; +} + +/** + * @brief Start a timer's counter. + * + * Does not affect the timer's mode or other settings. + * + * @param dev Device whose counter to resume + */ +static inline void timer_resume(timer_dev *dev) { + *bb_perip(&(dev->regs).bas->CR1, TIMER_CR1_CEN_BIT) = 1; +} + +/** + * @brief Returns the timer's counter value. + * + * This value is likely to be inaccurate if the counter is running + * with a low prescaler. + * + * @param dev Timer whose counter to return + */ +static inline uint16 timer_get_count(timer_dev *dev) { + return (uint16)(dev->regs).bas->CNT; +} + +/** + * @brief Sets the counter value for the given timer. + * @param dev Timer whose counter to set + * @param value New counter value + */ +static inline void timer_set_count(timer_dev *dev, uint16 value) { + (dev->regs).bas->CNT = value; +} + +/** + * @brief Returns the given timer's prescaler. + * + * Note that if the timer's prescaler is set (e.g. via + * timer_set_prescaler() or accessing a TIMx_PSC register), the value + * returned by this function will reflect the new setting, but the + * timer's counter will only reflect the new prescaler at the next + * update event. + * + * @param dev Timer whose prescaler to return + * @see timer_generate_update() + */ +static inline uint16 timer_get_prescaler(timer_dev *dev) { + return (uint16)(dev->regs).bas->PSC; +} + +/** + * @brief Set a timer's prescale value. + * + * The new value will not take effect until the next update event. + * + * @param dev Timer whose prescaler to set + * @param psc New prescaler value + * @see timer_generate_update() + */ +static inline void timer_set_prescaler(timer_dev *dev, uint16 psc) { + (dev->regs).bas->PSC = psc; +} + +/** + * @brief Returns a timer's reload value. + * @param dev Timer whose reload value to return + */ +static inline uint16 timer_get_reload(timer_dev *dev) { + return (uint16)(dev->regs).bas->ARR; +} + +/** + * @brief Set a timer's reload value. + * @param dev Timer whose reload value to set + * @param arr New reload value to use. Takes effect at next update event. + * @see timer_generate_update() + */ +static inline void timer_set_reload(timer_dev *dev, uint16 arr) { + (dev->regs).bas->ARR = arr; +} + +/** + * @brief Get the compare value for the given timer channel. + * @param dev Timer device, must have type TIMER_ADVANCED or TIMER_GENERAL. + * @param channel Channel whose compare value to get. + */ +static inline uint16 timer_get_compare(timer_dev *dev, uint8 channel) { + __io uint32 *ccr = &(dev->regs).gen->CCR1 + (channel - 1); + return *ccr; +} + +/** + * @brief Set the compare value for the given timer channel. + * @param dev Timer device, must have type TIMER_ADVANCED or TIMER_GENERAL. + * @param channel Channel whose compare value to set. + * @param value New compare value. + */ +static inline void timer_set_compare(timer_dev *dev, + uint8 channel, + uint16 value) { + __io uint32 *ccr = &(dev->regs).gen->CCR1 + (channel - 1); + *ccr = value; +} + +/** + * @brief Generate an update event for the given timer. + * + * Normally, this will cause the prescaler and auto-reload values in + * the PSC and ARR registers to take immediate effect. However, this + * function will do nothing if the UDIS bit is set in the timer's CR1 + * register (UDIS is cleared by default). + * + * @param dev Timer device to generate an update for. + */ +static inline void timer_generate_update(timer_dev *dev) { + *bb_perip(&(dev->regs).bas->EGR, TIMER_EGR_UG_BIT) = 1; +} + +/** + * @brief Enable a timer's trigger DMA request + * @param dev Timer device, must have type TIMER_ADVANCED or TIMER_GENERAL + */ +static inline void timer_trigger_dma_enable_request(timer_dev *dev) { + *bb_perip(&(dev->regs).gen->DIER, TIMER_DIER_TDE_BIT) = 1; +} + +/** + * @brief Disable a timer's trigger DMA request + * @param dev Timer device, must have type TIMER_ADVANCED or TIMER_GENERAL + */ +static inline void timer_trigger_dma_disable_request(timer_dev *dev) { + *bb_perip(&(dev->regs).gen->DIER, TIMER_DIER_TDE_BIT) = 0; +} + +/** + * @brief Enable a timer channel's DMA request. + * @param dev Timer device, must have type TIMER_ADVANCED or TIMER_GENERAL + * @param channel Channel whose DMA request to enable. + */ +static inline void timer_dma_enable_request(timer_dev *dev, uint8 channel) { + *bb_perip(&(dev->regs).gen->DIER, channel + 8) = 1; +} + +/** + * @brief Disable a timer channel's DMA request. + * @param dev Timer device, must have type TIMER_ADVANCED or TIMER_GENERAL. + * @param channel Channel whose DMA request to disable. + */ +static inline void timer_dma_disable_request(timer_dev *dev, uint8 channel) { + *bb_perip(&(dev->regs).gen->DIER, channel + 8) = 0; +} + +/** + * @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 + */ +static inline void timer_enable_interrupt(timer_dev *dev, uint8 interrupt) { + *bb_perip(&(dev->regs).adv->DIER, interrupt) = 1; +} + +/** + * @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 + */ +static inline void timer_disable_interrupt(timer_dev *dev, uint8 interrupt) { + *bb_perip(&(dev->regs).adv->DIER, interrupt) = 0; +} + +/** + * @brief Enable a timer channel's capture/compare signal. + * + * If the channel is configured as output, the corresponding output + * compare signal will be output on the corresponding output pin. If + * the channel is configured as input, enables capture of the counter + * value into the input capture/compare register. + * + * @param dev Timer device, must have type TIMER_ADVANCED or TIMER_GENERAL. + * @param channel Channel to enable, from 1 to 4. + */ +static inline void timer_cc_enable(timer_dev *dev, uint8 channel) { + *bb_perip(&(dev->regs).gen->CCER, 4 * (channel - 1)) = 1; +} + +/** + * @brief Disable a timer channel's output compare or input capture signal. + * @param dev Timer device, must have type TIMER_ADVANCED or TIMER_GENERAL. + * @param channel Channel to disable, from 1 to 4. + * @see timer_cc_enable() + */ +static inline void timer_cc_disable(timer_dev *dev, uint8 channel) { + *bb_perip(&(dev->regs).gen->CCER, 4 * (channel - 1)) = 0; +} + +/** + * @brief Get a channel's capture/compare output polarity + * @param dev Timer device, must have type TIMER_ADVANCED or TIMER_GENERAL. + * @param channel Channel whose capture/compare output polarity to get. + * @return Polarity, either 0 or 1. + * @see timer_cc_set_polarity() + */ +static inline uint8 timer_cc_get_polarity(timer_dev *dev, uint8 channel) { + return *bb_perip(&(dev->regs).gen->CCER, 4 * (channel - 1) + 1); +} + +/** + * @brief Set a timer channel's capture/compare output polarity. + * + * If the timer channel is configured as output: polarity == 0 means + * the output channel will be active high; polarity == 1 means active + * low. + * + * If the timer channel is configured as input: polarity == 0 means + * capture is done on the rising edge of ICn; when used as an external + * trigger, ICn is non-inverted. polarity == 1 means capture is done + * on the falling edge of ICn; when used as an external trigger, ICn + * is inverted. + * + * @param dev Timer device, must have type TIMER_ADVANCED or TIMER_GENERAL. + * @param channel Channel whose capture/compare output polarity to set. + * @param pol New polarity, 0 or 1. + */ +static inline void timer_cc_set_polarity(timer_dev *dev, + uint8 channel, + uint8 pol) { + *bb_perip(&(dev->regs).gen->CCER, 4 * (channel - 1) + 1) = pol; +} + +/** + * @brief Get a timer's DMA burst length. + * @param dev Timer device, must have type TIMER_ADVANCED or TIMER_GENERAL. + * @return Number of bytes to be transferred per DMA request, from 1 to 18. + */ +static inline uint8 timer_get_dma_burst_length(timer_dev *dev) { + uint32 dbl = ((dev->regs).gen->DCR & TIMER_DCR_DBL) >> 8; + return dbl + 1; /* 0 means 1 byte, etc. */ +} + +/** + * @brief Set a timer's DMA burst length. + * @param dev Timer device, must have type TIMER_ADVANCED or TIMER_GENERAL. + * @param length DMA burst length; i.e., number of bytes to transfer + * per DMA request, from 1 to 18. + */ +static inline void timer_set_dma_burst_length(timer_dev *dev, uint8 length) { + uint32 tmp = (dev->regs).gen->DCR; + tmp &= ~TIMER_DCR_DBL; + tmp |= (length - 1) << 8; + (dev->regs).gen->DCR = tmp; +} + +/** + * @brief Timer DMA base address. + * + * Defines the base address for DMA transfers. + */ +typedef enum timer_dma_base_address { + TIMER_DMA_BASE_CR1 = TIMER_DCR_DBA_CR1, /**< Base is control register 1 */ + TIMER_DMA_BASE_CR2 = TIMER_DCR_DBA_CR2, /**< Base is control register 2 */ + TIMER_DMA_BASE_SMCR = TIMER_DCR_DBA_SMCR, /**< Base is slave mode + control register */ + TIMER_DMA_BASE_DIER = TIMER_DCR_DBA_DIER, /**< Base is DMA interrupt enable + register */ + TIMER_DMA_BASE_SR = TIMER_DCR_DBA_SR, /**< Base is status register */ + TIMER_DMA_BASE_EGR = TIMER_DCR_DBA_EGR, /**< Base is event generation + register */ + TIMER_DMA_BASE_CCMR1 = TIMER_DCR_DBA_CCMR1, /**< Base is capture/compare + mode register 1 */ + TIMER_DMA_BASE_CCMR2 = TIMER_DCR_DBA_CCMR2, /**< Base is capture/compare + mode register 2 */ + TIMER_DMA_BASE_CCER = TIMER_DCR_DBA_CCER, /**< Base is capture/compare + enable register */ + TIMER_DMA_BASE_CNT = TIMER_DCR_DBA_CNT, /**< Base is counter */ + TIMER_DMA_BASE_PSC = TIMER_DCR_DBA_PSC, /**< Base is prescaler */ + TIMER_DMA_BASE_ARR = TIMER_DCR_DBA_ARR, /**< Base is auto-reload + register */ + TIMER_DMA_BASE_RCR = TIMER_DCR_DBA_RCR, /**< Base is repetition + counter register */ + TIMER_DMA_BASE_CCR1 = TIMER_DCR_DBA_CCR1, /**< Base is capture/compare + register 1 */ + TIMER_DMA_BASE_CCR2 = TIMER_DCR_DBA_CCR2, /**< Base is capture/compare + register 2 */ + TIMER_DMA_BASE_CCR3 = TIMER_DCR_DBA_CCR3, /**< Base is capture/compare + register 3 */ + TIMER_DMA_BASE_CCR4 = TIMER_DCR_DBA_CCR4, /**< Base is capture/compare + register 4 */ + TIMER_DMA_BASE_BDTR = TIMER_DCR_DBA_BDTR, /**< Base is break and + dead-time register */ + TIMER_DMA_BASE_DCR = TIMER_DCR_DBA_DCR, /**< Base is DMA control + register */ + TIMER_DMA_BASE_DMAR = TIMER_DCR_DBA_DMAR /**< Base is DMA address for + full transfer */ +} timer_dma_base_address; + +/** + * @brief Get the timer's DMA base address. + * + * Some restrictions apply; see ST RM0008. + * + * @param dev Timer device, must have type TIMER_ADVANCED or TIMER_GENERAL. + * @return DMA base address + */ +static inline timer_dma_base_address +timer_get_dma_base_address(timer_dev *dev) { + uint32 dcr = (dev->regs).gen->DCR; + return (timer_dma_base_address)(dcr & TIMER_DCR_DBA); +} + +/** + * @brief Set the timer's DMA base address. + * + * Some restrictions apply; see ST RM0008. + * + * @param dev Timer device, must have type TIMER_ADVANCED or TIMER_GENERAL. + * @param dma_base DMA base address. + */ +static inline void +timer_set_dma_base_address(timer_dev *dev, timer_dma_base_address dma_base) { + uint32 tmp = (dev->regs).gen->DCR; + tmp &= ~TIMER_DCR_DBA; + tmp |= dma_base; + (dev->regs).gen->DCR = tmp; +} + +/** + * Timer output compare modes. + */ +typedef enum timer_oc_mode { + TIMER_OC_MODE_FROZEN = 0 << 4, /**< Frozen: comparison between output + compare register and counter has no + effect on the outputs. */ + TIMER_OC_MODE_ACTIVE_ON_MATCH = 1 << 4, /**< OCxREF signal is forced + high when the count matches + the channel capture/compare + register. */ + TIMER_OC_MODE_INACTIVE_ON_MATCH = 2 << 4, /**< OCxREF signal is forced + low when the counter matches + the channel capture/compare + register. */ + TIMER_OC_MODE_TOGGLE = 3 << 4, /**< OCxREF toggles when counter + matches the cannel capture/compare + register. */ + TIMER_OC_MODE_FORCE_INACTIVE = 4 << 4, /**< OCxREF is forced low. */ + TIMER_OC_MODE_FORCE_ACTIVE = 5 << 4, /**< OCxREF is forced high. */ + TIMER_OC_MODE_PWM_1 = 6 << 4, /**< PWM mode 1. In upcounting, channel is + active as long as count is less than + channel capture/compare register, else + inactive. In downcounting, channel is + inactive as long as count exceeds + capture/compare register, else + active. */ + TIMER_OC_MODE_PWM_2 = 7 << 4 /**< PWM mode 2. In upcounting, channel is + inactive as long as count is less than + capture/compare register, else active. + In downcounting, channel is active as + long as count exceeds capture/compare + register, else inactive. */ +} timer_oc_mode; + +/** + * Timer output compare mode flags. + * @see timer_oc_set_mode() + */ +typedef enum timer_oc_mode_flags { + TIMER_OC_CE = BIT(7), /**< Output compare clear enable. */ + TIMER_OC_PE = BIT(3), /**< Output compare preload enable. */ + TIMER_OC_FE = BIT(2) /**< Output compare fast enable. */ +} timer_oc_mode_flags; + +/** + * @brief Configure a channel's output compare mode. + * + * @param dev Timer device, must have type TIMER_ADVANCED or TIMER_GENERAL. + * @param channel Channel to configure in output compare mode. + * @param mode Timer mode to set. + * @param flags OR of timer_oc_mode_flags. + * @see timer_oc_mode + * @see timer_oc_mode_flags + */ +static inline void timer_oc_set_mode(timer_dev *dev, + uint8 channel, + timer_oc_mode mode, + uint8 flags) { + uint8 bit0 = channel & 1; + uint8 bit1 = (channel >> 1) & 1; + /* channel == 1,2 -> CCMR1; channel == 3,4 -> CCMR2 */ + __io uint32 *ccmr = &(dev->regs).gen->CCMR1 + bit1; + /* channel == 1,3 -> shift = 0, channel == 2,4 -> shift = 8 */ + uint8 shift = 8 * (1 - bit0); + + uint32 tmp = *ccmr; + tmp &= ~(0xFF << shift); + tmp |= (mode | flags | TIMER_CCMR_CCS_OUTPUT) << shift; + *ccmr = tmp; +} + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif diff --git a/libmaple/timers.c b/libmaple/timers.c deleted file mode 100644 index 29aeeba..0000000 --- a/libmaple/timers.c +++ /dev/null @@ -1,530 +0,0 @@ -/****************************************************************************** - * The MIT License - * - * Copyright (c) 2010 Perry Hung. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - *****************************************************************************/ - -/** - * @brief General timer routines - */ - -/* TODO: actually support timer5 and timer8 */ - -#include "libmaple.h" -#include "rcc.h" -#include "nvic.h" -#include "timers.h" - -/* Timer descriptor table */ -struct timer_dev timer_dev_table[] = { - [TIMER1] = { - .base = (timer_port*)TIMER1_BASE, - .rcc_dev_num = RCC_TIMER1, - .nvic_dev_num = NVIC_TIMER1 - }, - [TIMER2] = { - .base = (timer_port*)TIMER2_BASE, - .rcc_dev_num = RCC_TIMER2, - .nvic_dev_num = NVIC_TIMER2 - }, - [TIMER3] = { - .base = (timer_port*)TIMER3_BASE, - .rcc_dev_num = RCC_TIMER3, - .nvic_dev_num = NVIC_TIMER3 - }, - [TIMER4] = { - .base = (timer_port*)TIMER4_BASE, - .rcc_dev_num = RCC_TIMER4, - .nvic_dev_num = NVIC_TIMER4 - }, -#if NR_TIMERS >= 8 - /* High density devices only (eg, Maple Native) */ - [TIMER5] = { - .base = (timer_port*)TIMER5_BASE, - .rcc_dev_num = RCC_TIMER5, - .nvic_dev_num = NVIC_TIMER5 - }, - [TIMER8] = { - .base = (timer_port*)TIMER8_BASE, - .rcc_dev_num = RCC_TIMER8, - .nvic_dev_num = NVIC_TIMER8 - }, -#endif -}; - -/* This function should probably be rewriten to take (timer_num, mode) - * and have prescaler set elsewhere. The mode can be passed through to - * set_mode at the end */ -void timer_init(timer_dev_num timer_num, uint16 prescale) { - /* TODO: doesn't catch 6+7 */ - - timer_port *timer = timer_dev_table[timer_num].base; - uint8 is_advanced = 0; - - if (timer_num == TIMER1) { - is_advanced = 1; - } -#if NR_TIMERS >= 8 - if (timer_num == TIMER8) { - is_advanced = 1; - } -#endif - - rcc_clk_enable(timer_dev_table[timer_num].rcc_dev_num); - - timer->CR1 = ARPE; // No clock division - // Do not buffer auto-reload preload - // Edge aligned - // Upcounter - // Do not stop counter at update event - // Update events enabled (etc, see bits [1:2]) - // Counter disabled for now - - timer->PSC = prescale; // Prescaling by prescale (duh) - timer->ARR = 0xFFFF; // Max reload cont - - /* initialize all the channels to 50% duty cycle, - * TODO: none of them actually get output unless the gpio pin - * is set, this will probably consume a bit more power but - * we'll worry about that later. */ - timer->CCR1 = 0x8FFF; // PWM start value - timer->CCMR1 |= 0x68; // PWM mode 1, enable preload register. - - timer->CCR2 = 0x8FFF; // PWM start value - timer->CCMR1 |= (0x68 << 8);// PWM mode 1, enable preload register. - - timer->CCR3 = 0x8FFF; // PWM start value - timer->CCMR2 |= 0x68; // PWM mode 1, enable preload register. - - timer->CCR4 = 0x8FFF; // PWM start value - timer->CCMR2 |= (0x68 << 8);// PWM mode 1, enable preload register. - - /* Advanced timer? */ - if (is_advanced) { - timer->BDTR = 0x8000; // moe enable - } - - timer->SR = 0; // clear it - timer->DIER = 0; // disable update interrupt - timer->EGR = 1; // Initialize update event and shadow registers - timer->CR1 |= 1; // Enable timer -} - -/* Stops the counter; the mode and settings are not modified */ -void timer_pause(timer_dev_num timer_num) { - timer_port *timer = timer_dev_table[timer_num].base; - - timer->CR1 &= ~(0x0001); // CEN -} - -/* Starts the counter; the mode and settings are not modified */ -void timer_resume(timer_dev_num timer_num) { - timer_port *timer = timer_dev_table[timer_num].base; - - timer->CR1 |= 0x0001; // CEN -} - -/* Returns the current timer counter value. Probably very inaccurate - * if the counter is running with a low prescaler. */ -uint16 timer_get_count(timer_dev_num timer_num) { - timer_port *timer = timer_dev_table[timer_num].base; - - return timer->CNT; -} - -/* This function sets the counter value via register for the specified - * timer. Can't think of specific usecases except for resetting to - * zero but it's easy to implement and allows for "creative" - * programming */ -void timer_set_count(timer_dev_num timer_num, uint16 value) { - timer_port *timer = timer_dev_table[timer_num].base; - - timer->CNT = value; -} - -/* Get the prescaler buffer value (remember, the actual prescaler - * doesn't get set until an update event). */ -uint16 timer_get_prescaler(timer_dev_num timer_num) { - timer_port *timer = timer_dev_table[timer_num].base; - return timer->PSC; -} - -/* Sets the prescaler */ -void timer_set_prescaler(timer_dev_num timer_num, uint16 prescale) { - timer_port *timer = timer_dev_table[timer_num].base; - - timer->PSC = prescale; -} - -/* Get the reload value for the entire timer. */ -uint16 timer_get_reload(timer_dev_num timer_num) { - timer_port *timer = timer_dev_table[timer_num].base; - return timer->ARR; -} - -/* This sets the "reload" or "overflow" value for the entire timer. We - * should probably settle on either "reload" or "overflow" to prevent - * confusion? */ -void timer_set_reload(timer_dev_num timer_num, uint16 max_reload) { - timer_port *timer = timer_dev_table[timer_num].base; - - timer->ARR = max_reload; -} - -/* This quickly disables all 4 timers, presumably as part of a system shutdown - * or similar to prevent interrupts and PWM output without 16 seperate function - * calls to timer_set_mode */ -void timer_disable_all(void) { - // TODO: refactor - - /* Note: this must be very robust because it gets called from, - e.g., ASSERT */ - timer_port *timer; -#if NR_TIMERS >= 8 - timer_port *timers[6] = { (timer_port*)TIMER1_BASE, - (timer_port*)TIMER2_BASE, - (timer_port*)TIMER3_BASE, - (timer_port*)TIMER4_BASE, - (timer_port*)TIMER5_BASE, - (timer_port*)TIMER8_BASE, - }; - uint8 i; - for (i = 0; i < 6; i++) { - timer = timers[i]; - timer->CR1 = 0; - timer->CCER = 0; - } -#else - timer_port *timers[4] = { (timer_port*)TIMER1_BASE, - (timer_port*)TIMER2_BASE, - (timer_port*)TIMER3_BASE, - (timer_port*)TIMER4_BASE, - }; - uint8 i; - for (i = 0; i < 4; i++) { - timer = timers[i]; - timer->CR1 = 0; - timer->CCER = 0; - } -#endif -} - -/* Sets the mode of individual timer channels, including a DISABLE mode */ -void timer_set_mode(timer_dev_num timer_num, uint8 channel, TimerMode mode) { - timer_port *timer = timer_dev_table[timer_num].base; - ASSERT(channel >= 1); - - switch(mode) { - case TIMER_DISABLED: - /* Disable the channel - * Disable any interrupt - * Clear interrupt SR? (TODO) */ - timer->DIER &= ~(1 << channel); // 1-indexed compare nums - timer_detach_interrupt(timer_num, channel); - timer->CCER &= ~(1 << (4*(channel - 1))); // 0-indexed - break; - case TIMER_PWM: - /* Set CCMR mode - * Keep existing reload value - * Disable any interrupt - * Clear interrupt SR? (TODO) - * Enable channel */ - timer->DIER &= ~(1 << channel); // 1-indexed compare nums - switch (channel) { - case 1: - timer->CCMR1 &= ~(0xFF); - timer->CCMR1 |= 0x68; // PWM mode 1, enable preload register. - break; - case 2: - timer->CCMR1 &= ~(0xFF00); - timer->CCMR1 |= (0x68 << 8);// PWM mode 1, enable preload register. - break; - case 3: - timer->CCMR2 &= ~(0xFF); - timer->CCMR2 |= 0x68; // PWM mode 1, enable preload register. - break; - case 4: - timer->CCMR2 &= ~(0xFF00); - timer->CCMR2 |= (0x68 << 8);// PWM mode 1, enable preload register. - break; - default: - ASSERT(0); - } - timer->CCER |= (1 << (4*(channel - 1))); // Enable - break; - case TIMER_OUTPUTCOMPARE: - /* Set CCMR mode - * Keep existing reload value - * Don't modify interrupt (needs to be attached to enable) - * Clear interrupt SR? (TODO) - * Enable channel */ - switch (channel) { - case 1: - timer->CCMR1 &= ~(0xFF); - timer->CCMR1 |= 0x0010; // PWM mode 1, enable preload register. - break; - case 2: - timer->CCMR1 &= ~(0xFF00); - timer->CCMR1 |= 0x1000; // PWM mode 1, enable preload register. - break; - case 3: - timer->CCMR2 &= ~(0xFF); - timer->CCMR2 |= 0x0010; // PWM mode 1, enable preload register. - break; - case 4: - timer->CCMR2 &= ~(0xFF00); - timer->CCMR2 |= 0x1000; // PWM mode 1, enable preload register. - break; - default: - ASSERT(0); - } - timer->CCER |= (1 << (4*(channel - 1))); // Enable - break; - default: - ASSERT(0); - } -} - -uint16 timer_get_compare_value(timer_dev_num timer_num, uint8 channel_num) { - /* faster: just read TIMERx_CHy_CCR (see timers.h) */ - ASSERT(channel_num > 0 && channel_num <= 4); - timer_port *timer = timer_dev_table[timer_num].base; - switch(channel_num) { - case 1: - return timer->CCR1; - case 2: - return timer->CCR2; - case 3: - return timer->CCR3; - case 4: - return timer->CCR4; - default: /* in case ASSERT is disabled */ - return 0; - } -} - -/* This sets the compare value (aka the trigger) for a given timer - * channel */ -void timer_set_compare_value(timer_dev_num timer_num, - uint8 channel_num, - uint16 value) { - ASSERT(channel_num > 0 && channel_num <= 4); - - /* The faster version of this function is the inline - timer_pwm_write_ccr */ - timer_port *timer = timer_dev_table[timer_num].base; - - switch(channel_num) { - case 1: - timer->CCR1 = value; - break; - case 2: - timer->CCR2 = value; - break; - case 3: - timer->CCR3 = value; - break; - case 4: - timer->CCR4 = value; - break; - } -} - -/* Stores a pointer to the passed usercode interrupt function and configures - * the actual ISR so that it will actually be called */ -void timer_attach_interrupt(timer_dev_num timer_num, - uint8 compare_num, - voidFuncPtr handler) { - ASSERT(compare_num > 0 && compare_num <= 4); - - timer_port *timer = timer_dev_table[timer_num].base; - - timer_dev_table[timer_num].handlers[compare_num-1] = handler; - timer->DIER |= (1 << compare_num); // 1-indexed compare nums - nvic_irq_enable(timer_dev_table[timer_num].nvic_dev_num); -} - -void timer_detach_interrupt(timer_dev_num timer_num, uint8 compare_num) { - ASSERT(compare_num > 0 && compare_num <= 4); - - timer_port *timer = timer_dev_table[timer_num].base; - - timer_dev_table[timer_num].handlers[compare_num-1] = 0; - timer->DIER &= ~(1 << compare_num); // 1-indexed compare nums -} - -void timer_generate_update(timer_dev_num timer_num) { - /* cause update event by setting UG bit in EGR. updates prescaler - ratio etc. */ - timer_port *timer = timer_dev_table[timer_num].base; - timer->EGR |= 0x1; -} - -/* The following are the actual interrupt handlers; 1 for each timer which must - * determine which actual compare value (aka channel) was triggered. - * - * These ISRs get called when the timer interrupt is enabled, the - * timer is running, and the timer count equals any of the CCR - * registers /or/ has overflowed. - * - * This is a rather long implementation... */ -void TIM1_CC_IRQHandler(void) { - timer_port *timer = (timer_port*)TIMER1_BASE; - uint16 sr_buffer; - sr_buffer = timer->SR; - - /* Simply switch/case-ing here doesn't work because multiple - * CC flags may be high. */ - if(sr_buffer & 0x10){ // CC4 flag - timer->SR &= ~(0x10); - if(timer_dev_table[TIMER1].handlers[3]) { - timer_dev_table[TIMER1].handlers[3](); - } - } - if(sr_buffer & 0x8){ // CC3 flag - timer->SR &= ~(0x8); - if(timer_dev_table[TIMER1].handlers[2]) { - timer_dev_table[TIMER1].handlers[2](); - } - } - if(sr_buffer & 0x4){ // CC2 flag - timer->SR &= ~(0x4); - if(timer_dev_table[TIMER1].handlers[1]) { - timer_dev_table[TIMER1].handlers[1](); - } - } - if(sr_buffer & 0x2){ // CC1 flag - timer->SR &= ~(0x2); - if(timer_dev_table[TIMER1].handlers[0]) { - timer_dev_table[TIMER1].handlers[0](); - } - } - if(sr_buffer & 0x1){ // Update flag - timer->SR &= ~(0x1); - //timer->EGR = 1; - } -} -void TIM2_IRQHandler(void) { - /* This is a rather long implementation... */ - timer_port *timer = (timer_port*)TIMER2_BASE; - uint16 sr_buffer; - sr_buffer = timer->SR; - - if(sr_buffer & 0x10){ // CC4 flag - timer->SR &= ~(0x10); - if(timer_dev_table[TIMER2].handlers[3]) { - timer_dev_table[TIMER2].handlers[3](); - } - } - if(sr_buffer & 0x8){ // CC3 flag - timer->SR &= ~(0x8); - if(timer_dev_table[TIMER2].handlers[2]) { - timer_dev_table[TIMER2].handlers[2](); - } - } - if(sr_buffer & 0x4){ // CC2 flag - timer->SR &= ~(0x4); - if(timer_dev_table[TIMER2].handlers[1]) { - timer_dev_table[TIMER2].handlers[1](); - } - } - if(sr_buffer & 0x2){ // CC1 flag - timer->SR &= ~(0x2); - if(timer_dev_table[TIMER2].handlers[0]) { - timer_dev_table[TIMER2].handlers[0](); - } - } - if(sr_buffer & 0x1){ // Update flag - timer->SR &= ~(0x1); - //timer->EGR = 1; - } -} -void TIM3_IRQHandler(void) { - /* This is a rather long implementation... */ - timer_port *timer = (timer_port*)TIMER3_BASE; - uint16 sr_buffer; - sr_buffer = timer->SR; - - if(sr_buffer & 0x10){ // CC4 flag - timer->SR &= ~(0x10); - if(timer_dev_table[TIMER3].handlers[3]) { - timer_dev_table[TIMER3].handlers[3](); - } - } - if(sr_buffer & 0x8){ // CC3 flag - timer->SR &= ~(0x8); - if(timer_dev_table[TIMER3].handlers[2]) { - timer_dev_table[TIMER3].handlers[2](); - } - } - if(sr_buffer & 0x4){ // CC2 flag - timer->SR &= ~(0x4); - if(timer_dev_table[TIMER3].handlers[1]) { - timer_dev_table[TIMER3].handlers[1](); - } - } - if(sr_buffer & 0x2){ // CC1 flag - timer->SR &= ~(0x2); - if(timer_dev_table[TIMER3].handlers[0]) { - timer_dev_table[TIMER3].handlers[0](); - } - } - if(sr_buffer & 0x1){ // Update flag - timer->SR &= ~(0x1); - //timer->EGR = 1; - } -} - -void TIM4_IRQHandler(void) { - /* This is a rather long implementation... */ - timer_port*timer = (timer_port*)TIMER4_BASE; - uint16 sr_buffer; - sr_buffer = timer->SR; - - if(sr_buffer & 0x10){ // CC4 flag - timer->SR &= ~(0x10); - if(timer_dev_table[TIMER4].handlers[3]) { - timer_dev_table[TIMER4].handlers[3](); - } - } - if(sr_buffer & 0x8){ // CC3 flag - timer->SR &= ~(0x8); - if(timer_dev_table[TIMER4].handlers[2]) { - timer_dev_table[TIMER4].handlers[2](); - } - } - if(sr_buffer & 0x4){ // CC2 flag - timer->SR &= ~(0x4); - if(timer_dev_table[TIMER4].handlers[1]) { - timer_dev_table[TIMER4].handlers[1](); - } - } - if(sr_buffer & 0x2){ // CC1 flag - timer->SR &= ~(0x2); - if(timer_dev_table[TIMER4].handlers[0]) { - timer_dev_table[TIMER4].handlers[0](); - } - } - if(sr_buffer & 0x1){ // Update flag - timer->SR &= ~(0x1); - //timer->EGR = 1; - } -} diff --git a/libmaple/timers.h b/libmaple/timers.h deleted file mode 100644 index 99bcab6..0000000 --- a/libmaple/timers.h +++ /dev/null @@ -1,435 +0,0 @@ -/****************************************************************************** - * The MIT License - * - * Copyright (c) 2010 Perry Hung. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - *****************************************************************************/ - -/** - * @file timers.h - * - * @brief Timer prototypes and various definitions - */ - -/* Note to self: - * The timer clock frequencies are automatically fixed by hardware. - * There are two cases: - * 1. if the APB prescaler is 1, the timer clock frequencies are - * set to the same frequency as that of the APB domain to which - * the timers are connected. - * 2. otherwise, they are set to twice (x2) the frequency of the - * APB domain to which the timers are connected. - * See stm32 manual, 77/995 - * - * hence, 72 mhz timers - */ - -/* Maple Timer channels: - * Timer Maple Pin STM32 Pin Type - * TIM1_CH1 D6 PA8 Advanced - * TIM1_CH2 D7 PA9 Advanced - * TIM1_CH3 D8 PA10 Advanced - * - * TIM2_CH1 D2 PA0 - * TIM2_CH2 D3 PA1 - * TIM2_CH3 D1 PA2 - * TIM2_CH4 D0 PA3 - * - * TIM3_CH1 D12 PA6 - * TIM3_CH2 D11 PA7 - * TIM3_CH3 EXT7 PB0 - * TIM3_CH4 EXT8 PB1 - * - * TIM4_CH1 EXT5 PB6 - * TIM4_CH1 EXT9 PB7 - * TIM4_CH1 EXT15 PB8 - * TIM4_CH1 EXT4 PB9 - * - * Not supported: - * TIM1_CH4 USBDM, not available PA11 Advanced - * TIM1_CH1_N EXT12 PB13 - * TIM1_CH2_N EXT13 PB14 - * TIM1_CH3_N EXT14 PB15 - * */ - -/* I don't like the Arduino API for dealing with pin modes. - * How about... - * - * pinMode(digitalPin, PWM); - * pwmWrite(digitalPin) */ - -#ifndef _TIMERS_H_ -#define _TIMERS_H_ - -#ifdef __cplusplus -extern "C"{ -#endif - -typedef volatile uint16* TimerCCR; - -#define TIMER1_BASE 0x40012C00 -#define TIMER2_BASE 0x40000000 -#define TIMER3_BASE 0x40000400 -#define TIMER4_BASE 0x40000800 -#define TIMER5_BASE 0x40000C00 // High-density devices only -#define TIMER6_BASE 0x40001000 // High-density devices only -#define TIMER7_BASE 0x40001400 // High-density devices only -#define TIMER8_BASE 0x40013400 // High-density devices only - -#define ARPE BIT(7) // Auto-reload preload enable -#define NOT_A_TIMER 0 - -#define TIMER_CCR(NUM,CHAN) (TIMER ## NUM ## _CH ## CHAN ## _CRR) - -/* Timers 1-4 are present on the entire STM32 line. */ - -#define TIMER1_CH1_CCR ((TimerCCR)(TIMER1_BASE + 0x34)) -#define TIMER1_CH2_CCR ((TimerCCR)(TIMER1_BASE + 0x38)) -#define TIMER1_CH3_CCR ((TimerCCR)(TIMER1_BASE + 0x3C)) -#define TIMER1_CH4_CCR ((TimerCCR)(TIMER1_BASE + 0x40)) - -#define TIMER2_CH1_CCR ((TimerCCR)(TIMER2_BASE + 0x34)) -#define TIMER2_CH2_CCR ((TimerCCR)(TIMER2_BASE + 0x38)) -#define TIMER2_CH3_CCR ((TimerCCR)(TIMER2_BASE + 0x3C)) -#define TIMER2_CH4_CCR ((TimerCCR)(TIMER2_BASE + 0x40)) - -#define TIMER3_CH1_CCR ((TimerCCR)(TIMER3_BASE + 0x34)) -#define TIMER3_CH2_CCR ((TimerCCR)(TIMER3_BASE + 0x38)) -#define TIMER3_CH3_CCR ((TimerCCR)(TIMER3_BASE + 0x3C)) -#define TIMER3_CH4_CCR ((TimerCCR)(TIMER3_BASE + 0x40)) - -#define TIMER4_CH1_CCR ((TimerCCR)(TIMER4_BASE + 0x34)) -#define TIMER4_CH2_CCR ((TimerCCR)(TIMER4_BASE + 0x38)) -#define TIMER4_CH3_CCR ((TimerCCR)(TIMER4_BASE + 0x3C)) -#define TIMER4_CH4_CCR ((TimerCCR)(TIMER4_BASE + 0x40)) - -/* Timers 5 and 8 are in high-density devices only (such as Maple - Native). Timers 6 and 7 in these devices have no output compare - pins. */ - -#define TIMER5_CH1_CCR ((TimerCCR)(TIMER5_BASE + 0x34)) -#define TIMER5_CH2_CCR ((TimerCCR)(TIMER5_BASE + 0x38)) -#define TIMER5_CH3_CCR ((TimerCCR)(TIMER5_BASE + 0x3C)) -#define TIMER5_CH4_CCR ((TimerCCR)(TIMER5_BASE + 0x40)) - -#define TIMER8_CH1_CCR ((TimerCCR)(TIMER8_BASE + 0x34)) -#define TIMER8_CH2_CCR ((TimerCCR)(TIMER8_BASE + 0x38)) -#define TIMER8_CH3_CCR ((TimerCCR)(TIMER8_BASE + 0x3C)) -#define TIMER8_CH4_CCR ((TimerCCR)(TIMER8_BASE + 0x40)) - -/** - * Used to configure the behavior of a timer. - */ -typedef enum TimerMode { - TIMER_DISABLED, /**< In this mode, the timer stops counting, - interrupts are not called, and no state changes - are output. */ - TIMER_PWM, /**< This is the default mode for pins after - initialization. */ - TIMER_OUTPUTCOMPARE, /**< In this mode, the timer counts from 0 to - its reload value repeatedly; every time - the counter value reaches one of the - channel compare values, the corresponding - interrupt is fired. */ -} TimerMode; - -typedef struct { - /* Fields up to ARR common to general purpose (2,3,4,5), advanced - control (1,8) and basic (6, 7) timers: */ - volatile uint16 CR1; - uint16 RESERVED0; - volatile uint16 CR2; - uint16 RESERVED1; - volatile uint16 SMCR; - uint16 RESERVED2; - volatile uint16 DIER; - uint16 RESERVED3; - volatile uint16 SR; - uint16 RESERVED4; - volatile uint16 EGR; - uint16 RESERVED5; - volatile uint16 CCMR1; - uint16 RESERVED6; - volatile uint16 CCMR2; - uint16 RESERVED7; - volatile uint16 CCER; - uint16 RESERVED8; - volatile uint16 CNT; - uint16 RESERVED9; - volatile uint16 PSC; - uint16 RESERVED10; - volatile uint16 ARR; - uint16 RESERVED11; - /* Basic timers have none of the following: */ - volatile uint16 RCR; /* Advanced control timers only */ - uint16 RESERVED12; /* Advanced control timers only */ - volatile uint16 CCR1; - uint16 RESERVED13; - volatile uint16 CCR2; - uint16 RESERVED14; - volatile uint16 CCR3; - uint16 RESERVED15; - volatile uint16 CCR4; - uint16 RESERVED16; - volatile uint16 BDTR; /* Advanced control timers only */ - uint16 RESERVED17; /* Advanced control timers only */ - volatile uint16 DCR; - uint16 RESERVED18; - volatile uint16 DMAR; - uint16 RESERVED19; -} timer_port; - -/** - * Timer device numbers. See STM32 reference manual, chapters 13-15. - */ -/* several locations depend on TIMER1=0, etc.; don't change the - enumerator values to start at 1. */ -typedef enum { - TIMER1, /*< Advanced control timer TIM1 */ - TIMER2, /*< General purpose timer TIM2 */ - TIMER3, /*< General purpose timer TIM3 */ - TIMER4, /*< General purpose timer TIM4 */ -#if NR_TIMERS >= 8 - TIMER5, /*< General purpose timer TIM5; high density only */ - /* FIXME maple native: put timers 6 and 7 back in and make the - corresponding changes to timers.c */ - /* TIMER6, /\*< Basic timer TIM6; high density only *\/ */ - /* TIMER7, /\*< Basic timer TIM7; high density only *\/ */ - TIMER8, /*< Advanced control timer TIM8; high density only */ -#endif - TIMER_INVALID /* FIXME: this is starting to seem like a bad idea */ -} timer_dev_num; - -/* timer descriptor */ -struct timer_dev { - timer_port *base; - const uint8 rcc_dev_num; - const uint8 nvic_dev_num; - volatile voidFuncPtr handlers[4]; -}; - -extern struct timer_dev timer_dev_table[]; - -/** - * Initializes timer with prescale as the clock divisor. - * - * @param timer_num Timer number. - * - * @param prescale value in the range 1--65535 to use as a prescaler - * for timer counter increment frequency. - * - * @see timer_dev_num - * @see timer_set_prescaler() - * @see timer_set_mode() - */ -void timer_init(timer_dev_num timer_num, uint16 prescale); - -/** - * Quickly disable all timers. Calling this function is faster than, - * e.g., calling timer_set_mode() for all available timers/channels. - */ -void timer_disable_all(void); - -/** - * Returns the timer's counter value. Due to function call overhead, - * this value is likely to be inaccurate if the counter is running - * with a low prescaler. - * - * @param timer_num the timer whose counter to return. - * - * @pre Timer has been initialized. - */ -uint16 timer_get_count(timer_dev_num timer_num); - -/** - * Sets the counter value for the given timer. - * - * @param timer_num the timer whose counter to set. - * - * @param value the new counter value. - * - * @pre Timer has been initialized. - */ -void timer_set_count(timer_dev_num timer_num, uint16 value); - -/** - * Stops the timer's counter from incrementing. Does not modify the - * timer's mode or settings. - * - * @param timer_num the timer to pause. - * - * @see timer_resume() - * - * @pre Timer has been initialized. - */ -void timer_pause(timer_dev_num timer_num); - -/** - * Starts the counter for the given timer. Does not modify the - * timer's mode or settings. The timer will begin counting on the - * first rising clock cycle after it has been re-enabled using this - * function. - * - * @param timer_num the timer to resume. - * - * @see timer_pause() - * - * @pre Timer has been initialized. - */ -void timer_resume(timer_dev_num timer_num); - -/** - * Returns the prescaler for the given timer. - * - * @param timer_num the timer whose prescaler to return. - * - * @see timer_set_prescaler() - * - * @pre Timer has been initialized. - */ -uint16 timer_get_prescaler(timer_dev_num timer_num); - -/** - * Sets the prescaler for the given timer. This value goes into the - * PSC register, so it's 0-based (i.e., a prescale of 0 counts 1 tick - * per clock cycle). This prescale does not take effect until the - * next update event. - * - * @param timer_num the timer whose prescaler to set. - * - * @param prescale the new prescaler. - * - * @pre Timer has been initialized. - */ -void timer_set_prescaler(timer_dev_num timer_num, uint16 prescale); - -/** - * Gets the reload value for the timer. - * - * @see timer_set_reload() - * - * @pre Timer has been initialized. - */ -uint16 timer_get_reload(timer_dev_num timer_num); - -/** - * Sets the reload value for the timer. - * - * After this function returns, the timer's counter will reset to 0 - * after it has reached the value max_reload. - * - * @pre Timer has been initialized. - */ -void timer_set_reload(timer_dev_num timer_num, uint16 max_reload); - -/* TODO: timer_get_mode */ - -/** - * Set the mode of an individual timer channel. - * - * @see timer_disable_all() - * @see TimerMode - * @see timer_dev_num - * @pre Timer has been initialized. - */ -void timer_set_mode(timer_dev_num timer_num, uint8 channel, TimerMode mode); - -/** - * Get the compare value for the given timer channel. - * @see timer_set_compare_value() - * @see timer_dev_num - * @pre Timer has been initialized. - */ -uint16 timer_get_compare_value(timer_dev_num timer_num, uint8 channel); - -/** - * Sets the compare value for a given timer channel. Useful for - * scheduling when interrupt handlers will be called. - * - * @see timer_attach_interrupt() - * @see timer_detach_interrupt() - * @see timer_set_reload() - * @see timer_dev_num - * @pre Timer has been initialized. - */ -void timer_set_compare_value(timer_dev_num timer_num, uint8 channel, - uint16 value); - -/** - * Detach the interrupt handler for the given timer channel, if any. - * After this function returns, any handler attached to the given - * channel will no longer be called. - * - * @see timer_attach_interrupt() - * @pre Timer has been initialized. - * @see timer_dev_num - */ -void timer_detach_interrupt(timer_dev_num timer_num, uint8 channel); - -/** - * Attach an interrupt handler for the given timer and channel. The - * given ISR, handler, will be called whenever the timer's counter - * reaches the compare value for the given timer and channel. - * - * @see timer_set_compare_value() - * @see timer_detach_interrupt() - * @see timer_set_mode() - * @see timer_dev_num - * @see voidFuncPtr - * @pre Timer has been initialized. - * @pre The channel's mode must be set to TIMER_OUTPUTCOMPARE, or the - * interrupt handler will not get called. - */ -void timer_attach_interrupt(timer_dev_num timer_num, uint8 channel, - voidFuncPtr handler); - -/** - * Programmatically generate an update event on the given timer. This - * updates the prescaler, reloads the compare value (in upcounting - * mode, etc.). - * - * @pre Timer has been initialized. - */ -void timer_generate_update(timer_dev_num timer_num); - -/** - * Turn on PWM with duty_cycle. - * - * @param ccr TIMERx_CHn_CCR, where x goes from 1 to NR_TIMERS, - * and n goes from 1 to 4. - * - * @param duty_cycle: A number between 0 and - * timer_get_compare_value(TIMERx, y), where x and y are as above. - * - * @pre Pin has been set to alternate function output. - * - * @pre Timer has been initialized. - */ -static inline void timer_pwm_write_ccr(TimerCCR ccr, uint16 duty_cycle) { - *ccr = duty_cycle; -} - -#ifdef __cplusplus -} // extern "C" -#endif - - -#endif - diff --git a/libmaple/usart.c b/libmaple/usart.c index 44a5c92..fbd4d70 100644 --- a/libmaple/usart.c +++ b/libmaple/usart.c @@ -3,228 +3,246 @@ * * Copyright (c) 2010 Perry Hung. * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. *****************************************************************************/ /** + * @file usart.c + * @author Marti Bolivar <mbolivar@leaflabs.com, + * Perry Hung <perry@leaflabs.com> * @brief USART control routines */ -#include "libmaple.h" -#include "rcc.h" -#include "nvic.h" #include "usart.h" -#define USART1_BASE 0x40013800 -#define USART2_BASE 0x40004400 -#define USART3_BASE 0x40004800 -#define UART4_BASE 0x40004C00 // High-density devices only (Maple Native) -#define UART5_BASE 0x40005000 // High-density devices only (Maple Native) - -#define USART_UE BIT(13) -#define USART_M BIT(12) -#define USART_TE BIT(3) -#define USART_RE BIT(2) -#define USART_RXNEIE BIT(5) // read data register not empty interrupt enable -#define USART_TC BIT(6) - -/* usart descriptor table */ -struct usart_dev usart_dev_table[] = { - [USART1] = { - .base = (usart_port*)USART1_BASE, - .rcc_dev_num = RCC_USART1, - .nvic_dev_num = NVIC_USART1 - }, - [USART2] = { - .base = (usart_port*)USART2_BASE, - .rcc_dev_num = RCC_USART2, - .nvic_dev_num = NVIC_USART2 - }, - [USART3] = { - .base = (usart_port*)USART3_BASE, - .rcc_dev_num = RCC_USART3, - .nvic_dev_num = NVIC_USART3 - }, -#if NR_USART >= 5 - /* TODO test */ - [UART4] = { - .base = (usart_port*)UART4_BASE, - .rcc_dev_num = RCC_UART4, - .nvic_dev_num = NVIC_UART4 - }, - [UART5] = { - .base = (usart_port*)UART5_BASE, - .rcc_dev_num = RCC_UART5, - .nvic_dev_num = NVIC_UART5 - }, -#endif -}; - /* - * Usart interrupt handlers. + * Devices */ -static inline void usart_irq(int usart_num) { -#ifdef USART_SAFE_INSERT - /* Ignore old bytes if the user defines USART_SAFE_INSERT. */ - rb_safe_insert(&(usart_dev_table[usart_num].rb), - (uint8)((usart_dev_table[usart_num].base)->DR)); -#else - /* By default, push bytes around in the ring buffer. */ - rb_push_insert(&(usart_dev_table[usart_num].rb), - (uint8)((usart_dev_table[usart_num].base)->DR)); +static ring_buffer usart1_rb; +static usart_dev usart1 = { + .regs = USART1_BASE, + .rb = &usart1_rb, + .max_baud = 4500000UL, + .clk_id = RCC_USART1, + .irq_num = NVIC_USART1 +}; +usart_dev *USART1 = &usart1; + +static ring_buffer usart2_rb; +static usart_dev usart2 = { + .regs = USART2_BASE, + .rb = &usart2_rb, + .max_baud = 2250000UL, + .clk_id = RCC_USART2, + .irq_num = NVIC_USART2 +}; +usart_dev *USART2 = &usart2; + +static ring_buffer usart3_rb; +static usart_dev usart3 = { + .regs = USART3_BASE, + .rb = &usart3_rb, + .max_baud = 2250000UL, + .clk_id = RCC_USART3, + .irq_num = NVIC_USART3 +}; +usart_dev *USART3 = &usart3; + +#ifdef STM32_HIGH_DENSITY +static ring_buffer uart4_rb; +static usart_dev uart4 = { + .regs = UART4_BASE, + .rb = &uart4_rb, + .max_baud = 2250000UL, + .clk_id = RCC_UART4, + .irq_num = NVIC_UART4 +}; +usart_dev *UART4 = &uart4; + +static ring_buffer uart5_rb; +static usart_dev uart5 = { + .regs = UART5_BASE, + .rb = &uart5_rb, + .max_baud = 2250000UL, + .clk_id = RCC_UART5, + .irq_num = NVIC_UART5 +}; +usart_dev *UART5 = &uart5; #endif -} - -/* TODO: Check the disassembly for the following functions to make - sure GCC inlined properly. */ - -void USART1_IRQHandler(void) { - usart_irq(USART1); -} - -void USART2_IRQHandler(void) { - usart_irq(USART2); -} -void USART3_IRQHandler(void) { - usart_irq(USART3); -} - -#if NR_USART >= 5 -void UART4_IRQHandler(void) { - usart_irq(UART4); -} - -void UART5_IRQHandler(void) { - usart_irq(UART5); +/** + * @brief Initialize a serial port. + * @param dev Serial port to be initialized + */ +void usart_init(usart_dev *dev) { + rb_init(dev->rb, USART_RX_BUF_SIZE, dev->rx_buf); + rcc_clk_enable(dev->clk_id); + nvic_irq_enable(dev->irq_num); } -#endif /** - * @brief Enable a USART in single buffer transmission mode, multibuffer - * receiver mode. - * @param usart_num USART to be initialized - * @param baud Baud rate to be set at + * @brief Configure a serial port's baud rate. + * + * @param dev Serial port to be configured + * @param clock_speed Clock speed, in megahertz. + * @param baud Baud rate for transmit/receive. */ -void usart_init(uint8 usart_num, uint32 baud) { - ASSERT(usart_num <= NR_USART); - usart_port *port; - ring_buffer *ring_buf; - - uint32 clk_speed; +void usart_set_baud_rate(usart_dev *dev, uint32 clock_speed, uint32 baud) { uint32 integer_part; uint32 fractional_part; uint32 tmp; - port = usart_dev_table[usart_num].base; - rcc_clk_enable(usart_dev_table[usart_num].rcc_dev_num); - nvic_irq_enable(usart_dev_table[usart_num].nvic_dev_num); - - /* usart1 is mad fast */ - clk_speed = (usart_num == USART1) ? 72000000UL : 36000000UL; - - /* Initialize rx ring buffer */ - rb_init(&usart_dev_table[usart_num].rb, - sizeof (usart_dev_table[usart_num].rx_buf), - usart_dev_table[usart_num].rx_buf); - - /* Set baud rate */ - integer_part = ((25 * clk_speed) / (4 * baud)); + /* See ST RM0008 for the details on configuring the baud rate register */ + integer_part = (25 * clock_speed) / (4 * baud); tmp = (integer_part / 100) << 4; - fractional_part = integer_part - (100 * (tmp >> 4)); tmp |= (((fractional_part * 16) + 50) / 100) & ((uint8)0x0F); - port->BRR = (uint16)tmp; - - port->CR1 = USART_TE | // transmitter enable - USART_RE | // receiver enable - USART_RXNEIE; // receive interrupt enable - - - /* Enable the USART and set mode to 8n1 */ - port->CR1 |= USART_UE; + dev->regs->BRR = (uint16)tmp; } /** - * @brief Turn off all USARTs. + * @brief Enable a serial port. + * + * USART is enabled in single buffer transmission mode, multibuffer + * receiver mode, 8n1. + * + * Serial port must have a baud rate configured to work properly. + * + * @param dev Serial port to enable. + * @see usart_set_baud_rate() */ -void usart_disable_all() { - usart_disable(USART1); - usart_disable(USART2); - usart_disable(USART3); -#if NR_USART >= 5 - usart_disable(UART4); - usart_disable(UART5); -#endif +void usart_enable(usart_dev *dev) { + usart_reg_map *regs = dev->regs; + regs->CR1 = USART_CR1_TE | USART_CR1_RE | USART_CR1_RXNEIE; + regs->CR1 |= USART_CR1_UE; } /** - * @brief Turn off a USART. - * @param USART to be disabled + * @brief Turn off a serial port. + * @param dev Serial port to be disabled */ -void usart_disable(uint8 usart_num) { - usart_port *port = usart_dev_table[usart_num].base; +void usart_disable(usart_dev *dev) { + /* FIXME this misbehaves if you try to use PWM on TX afterwards */ + usart_reg_map *regs = dev->regs; - /* TC bit must be high before disabling the usart */ - while((port->CR1 & USART_UE) && !(port->SR & USART_TC)) + /* TC bit must be high before disabling the USART */ + while((regs->CR1 & USART_CR1_UE) && !(regs->SR & USART_SR_TC)) ; /* Disable UE */ - port->CR1 = 0; + regs->CR1 &= ~USART_CR1_UE; /* Clean up buffer */ - usart_reset_rx(usart_num); + usart_reset_rx(dev); } - /** - * @brief Print a null terminated string to the specified USART - * - * @param usart_num usart to send on - * @param str string to send + * @brief Call a function on each USART. + * @param fn Function to call. */ -void usart_putstr(uint8 usart_num, const char* str) { - char ch; +void usart_foreach(void (*fn)(usart_dev*)) { + fn(USART1); + fn(USART2); + fn(USART3); +#ifdef STM32_HIGH_DENSITY + fn(UART4); + fn(UART5); +#endif +} - while((ch = *(str++)) != '\0') { - usart_putc(usart_num, ch); +/** + * @brief Nonblocking USART transmit + * @param dev Serial port to transmit over + * @param buf Buffer to transmit + * @param len Maximum number of bytes to transmit + * @return Number of bytes transmitted + */ +uint32 usart_tx(usart_dev *dev, const uint8 *buf, uint32 len) { + usart_reg_map *regs = dev->regs; + uint32 txed = 0; + while ((regs->SR & USART_SR_TXE) && (txed < len)) { + regs->DR = buf[txed++]; } + return txed; } /** - * @brief Print an unsigned integer to the specified usart + * @brief Transmit an unsigned integer to the specified serial port in + * decimal format. * - * @param usart_num usart to send on - * @param val number to print + * This function blocks until the integer's digits have been + * completely transmitted. + * + * @param dev Serial port to send on + * @param val Number to print */ -void usart_putudec(uint8 usart_num, uint32 val) { +void usart_putudec(usart_dev *dev, uint32 val) { char digits[12]; - int i; + int i = 0; - i = 0; do { digits[i++] = val % 10 + '0'; val /= 10; } while (val > 0); + while (--i >= 0) { - usart_putc(usart_num, digits[i]); + usart_putc(dev, digits[i]); } } + +/* + * Interrupt handlers. + */ + +static inline void usart_irq(usart_dev *dev) { +#ifdef USART_SAFE_INSERT + /* If the buffer is full and the user defines USART_SAFE_INSERT, + * ignore new bytes. */ + rb_safe_insert(dev->rb, (uint8)dev->regs->DR); +#else + /* By default, push bytes around in the ring buffer. */ + rb_push_insert(dev->rb, (uint8)dev->regs->DR); +#endif +} + +void __irq_usart1(void) { + usart_irq(USART1); +} + +void __irq_usart2(void) { + usart_irq(USART2); +} + +void __irq_usart3(void) { + usart_irq(USART3); +} + +#ifdef STM32_HIGH_DENSITY +void __irq_uart4(void) { + usart_irq(UART4); +} + +void __irq_uart5(void) { + usart_irq(UART5); +} +#endif diff --git a/libmaple/usart.h b/libmaple/usart.h index 0ca3f55..0743c53 100644 --- a/libmaple/usart.h +++ b/libmaple/usart.h @@ -3,119 +3,332 @@ * * Copyright (c) 2010 Perry Hung. * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. *****************************************************************************/ /** * @file usart.h + * @author Marti Bolivar <mbolivar@leaflabs.com>, + * Perry Hung <perry@leaflabs.com> * @brief USART definitions and prototypes */ #ifndef _USART_H_ #define _USART_H_ +#include "libmaple_types.h" +#include "util.h" +#include "rcc.h" +#include "nvic.h" #include "ring_buffer.h" #ifdef __cplusplus extern "C"{ #endif -#define USART_TXE BIT(7) - -/* usart device numbers */ -enum { - USART1, - USART2, - USART3, - UART4, - UART5, -}; - -/* peripheral register struct */ -typedef struct usart_port { - volatile uint32 SR; // Status register - volatile uint32 DR; // Data register - volatile uint32 BRR; // Baud rate register - volatile uint32 CR1; // Control register 1 - volatile uint32 CR2; // Control register 2 - volatile uint32 CR3; // Control register 3 - volatile uint32 GTPR; // Guard time and prescaler register -} usart_port; - -/* usart descriptor */ -struct usart_dev { - usart_port *base; - ring_buffer rb; - uint8 rx_buf[64]; - const uint8 rcc_dev_num; - const uint8 nvic_dev_num; -}; - -extern struct usart_dev usart_dev_table[]; +/* + * Register maps and devices + */ + +/** USART register map type */ +typedef struct usart_reg_map { + __io uint32 SR; /**< Status register */ + __io uint32 DR; /**< Data register */ + __io uint32 BRR; /**< Baud rate register */ + __io uint32 CR1; /**< Control register 1 */ + __io uint32 CR2; /**< Control register 2 */ + __io uint32 CR3; /**< Control register 3 */ + __io uint32 GTPR; /**< Guard time and prescaler register */ +} usart_reg_map; + +/** USART1 register map base pointer */ +#define USART1_BASE ((struct usart_reg_map*)0x40013800) +/** USART2 register map base pointer */ +#define USART2_BASE ((struct usart_reg_map*)0x40004400) +/** USART3 register map base pointer */ +#define USART3_BASE ((struct usart_reg_map*)0x40004800) +#ifdef STM32_HIGH_DENSITY +/** UART4 register map base pointer */ +#define UART4_BASE ((struct usart_reg_map*)0x40004C00) +/** UART5 register map base pointer */ +#define UART5_BASE ((struct usart_reg_map*)0x40005000) +#endif + +/* + * Register bit definitions + */ + +/* Status register */ + +#define USART_SR_CTS_BIT 9 +#define USART_SR_LBD_BIT 8 +#define USART_SR_TXE_BIT 7 +#define USART_SR_TC_BIT 6 +#define USART_SR_RXNE_BIT 5 +#define USART_SR_IDLE_BIT 4 +#define USART_SR_ORE_BIT 3 +#define USART_SR_NE_BIT 2 +#define USART_SR_FE_BIT 1 +#define USART_SR_PE_BIT 0 + +#define USART_SR_CTS BIT(USART_SR_CTS_BIT) +#define USART_SR_LBD BIT(USART_SR_LBD_BIT) +#define USART_SR_TXE BIT(USART_SR_TXE_BIT) +#define USART_SR_TC BIT(USART_SR_TC_BIT) +#define USART_SR_RXNE BIT(USART_SR_RXNE_BIT) +#define USART_SR_IDLE BIT(USART_SR_IDLE_BIT) +#define USART_SR_ORE BIT(USART_SR_ORE_BIT) +#define USART_SR_NE BIT(USART_SR_NE_BIT) +#define USART_SR_FE BIT(USART_SR_FE_BIT) +#define USART_SR_PE BIT(USART_SR_PE_BIT) + +/* Data register */ + +#define USART_DR_DR 0xFF + +/* Baud rate register */ + +#define USART_BRR_DIV_MANTISSA (0xFFF << 4) +#define USART_BRR_DIV_FRACTION 0xF + +/* Control register 1 */ + +#define USART_CR1_UE_BIT 13 +#define USART_CR1_M_BIT 12 +#define USART_CR1_WAKE_BIT 11 +#define USART_CR1_PCE_BIT 10 +#define USART_CR1_PS_BIT 9 +#define USART_CR1_PEIE_BIT 8 +#define USART_CR1_TXEIE_BIT 7 +#define USART_CR1_TCIE_BIT 6 +#define USART_CR1_RXNEIE_BIT 5 +#define USART_CR1_IDLEIE_BIT 4 +#define USART_CR1_TE_BIT 3 +#define USART_CR1_RE_BIT 2 +#define USART_CR1_RWU_BIT 1 +#define USART_CR1_SBK_BIT 0 + +#define USART_CR1_UE BIT(USART_CR1_UE_BIT) +#define USART_CR1_M BIT(USART_CR1_M_BIT) +#define USART_CR1_WAKE BIT(USART_CR1_WAKE_BIT) +#define USART_CR1_WAKE_IDLE (0 << USART_CR1_WAKE_BIT) +#define USART_CR1_WAKE_ADDR (1 << USART_CR1_WAKE_BIT) +#define USART_CR1_PCE BIT(USART_CR1_PCE_BIT) +#define USART_CR1_PS BIT(USART_CR1_PS_BIT) +#define USART_CR1_PS_EVEN (0 << USART_CR1_PS_BIT) +#define USART_CR1_PS_ODD (1 << USART_CR1_PS_BIT) +#define USART_CR1_PEIE BIT(USART_CR1_PEIE_BIT) +#define USART_CR1_TXEIE BIT(USART_CR1_TXEIE_BIT) +#define USART_CR1_TCIE BIT(USART_CR1_TCIE_BIT) +#define USART_CR1_RXNEIE BIT(USART_CR1_RXNEIE_BIT) +#define USART_CR1_IDLEIE BIT(USART_CR1_IDLEIE_BIT) +#define USART_CR1_TE BIT(USART_CR1_TE_BIT) +#define USART_CR1_RE BIT(USART_CR1_RE_BIT) +#define USART_CR1_RWU BIT(USART_CR1_RWU_BIT) +#define USART_CR1_RWU_ACTIVE (0 << USART_CR1_RWU_BIT) +#define USART_CR1_RWU_MUTE (1 << USART_CR1_RWU_BIT) +#define USART_CR1_SBK BIT(USART_CR1_SBK_BIT) + +/* Control register 2 */ + +#define USART_CR2_LINEN_BIT 14 +#define USART_CR2_CLKEN_BIT 11 +#define USART_CR2_CPOL_BIT 10 +#define USART_CR2_CPHA_BIT 9 +#define USART_CR2_LBCL_BIT 8 +#define USART_CR2_LBDIE_BIT 6 +#define USART_CR2_LBDL_BIT 5 + +#define USART_CR2_LINEN BIT(USART_CR2_LINEN_BIT) +#define USART_CR2_STOP (0x3 << 12) +#define USART_CR2_STOP_BITS_1 (0x0 << 12) +/* Not on UART4, UART5 */ +#define USART_CR2_STOP_BITS_POINT_5 (0x1 << 12) +/* Not on UART4, UART5 */ +#define USART_CR2_STOP_BITS_1_POINT_5 (0x3 << 12) +#define USART_CR2_STOP_BITS_2 (0x2 << 12) +#define USART_CR2_CLKEN BIT(USART_CR2_CLKEN_BIT) +/* Not on UART4, UART5 */ +#define USART_CR2_CPOL BIT(USART_CR2_CPOL_BIT) +#define USART_CR2_CPOL_LOW (0x0 << USART_CR2_CLKEN_BIT) +#define USART_CR2_CPOL_HIGH (0x1 << USART_CR2_CLKEN_BIT) +/* Not on UART4, UART5 */ +#define USART_CR2_CPHA BIT(USART_CR2_CPHA_BIT) +#define USART_CR2_CPHA_FIRST (0x0 << USART_CR2_CPHA_BIT) +#define USART_CR2_CPHA_SECOND (0x1 << USART_CR2_CPHA_BIT) +/* Not on UART4, UART5 */ +#define USART_CR2_LBCL BIT(USART_CR2_LBCL_BIT) +#define USART_CR2_LBDIE BIT(USART_CR2_LBDIE_BIT) +#define USART_CR2_LBDL BIT(USART_CR2_LBDL_BIT) +#define USART_CR2_LBDL_10_BIT (0 << USART_CR2_LBDL_BIT) +#define USART_CR2_LBDL_11_BIT (1 << USART_CR2_LBDL_BIT) +#define USART_CR2_ADD 0xF + +/* Control register 3 */ + +#define USART_CR3_CTSIE_BIT 10 +#define USART_CR3_CTSE_BIT 9 +#define USART_CR3_RTSE_BIT 8 +#define USART_CR3_DMAT_BIT 7 +#define USART_CR3_DMAR_BIT 6 +#define USART_CR3_SCEN_BIT 5 +#define USART_CR3_NACK_BIT 4 +#define USART_CR3_HDSEL_BIT 3 +#define USART_CR3_IRLP_BIT 2 +#define USART_CR3_IREN_BIT 1 +#define USART_CR3_EIE_BIT 0 + +/* Not on UART4, UART5 */ +#define USART_CR3_CTSIE BIT(USART_CR3_CTSIE_BIT) +/* Not on UART4, UART5 */ +#define USART_CR3_CTSE BIT(USART_CR3_CTSE_BIT) +/* Not on UART4, UART5 */ +#define USART_CR3_RTSE BIT(USART_CR3_RTSE_BIT) +/* Not on UART5 */ +#define USART_CR3_DMAT BIT(USART_CR3_DMAT_BIT) +/* Not on UART5 */ +#define USART_CR3_DMAR BIT(USART_CR3_DMAR_BIT) +/* Not on UART4, UART5 */ +#define USART_CR3_SCEN BIT(USART_CR3_SCEN_BIT) +/* Not on UART4, UART5 */ +#define USART_CR3_NACK BIT(USART_CR3_NACK_BIT) +#define USART_CR3_HDSEL BIT(USART_CR3_HDSEL_BIT) +#define USART_CR3_IRLP BIT(USART_CR3_IRLP_BIT) +#define USART_CR3_IRLP_NORMAL (0 << USART_CR3_IRLP_BIT) +#define USART_CR3_IRLP_LOW_POWER (1 << USART_CR3_IRLP_BIT) +#define USART_CR3_IREN BIT(USART_CR3_IREN_BIT) +#define USART_CR3_EIE BIT(USART_CR3_EIE_BIT) + +/* Guard time and prescaler register */ + +/* Not on UART4, UART5 */ +#define USART_GTPR_GT (0xFF << 8) +/* Not on UART4, UART5 */ +#define USART_GTPR_PSC 0xFF + +/* + * Devices + */ + +#define USART_RX_BUF_SIZE 64 + +/** USART device type */ +typedef struct usart_dev { + usart_reg_map *regs; + ring_buffer *rb; + uint32 max_baud; + uint8 rx_buf[USART_RX_BUF_SIZE]; + rcc_clk_id clk_id; + nvic_irq_num irq_num; +} usart_dev; + +/** USART1 device */ +extern usart_dev *USART1; +/** USART2 device */ +extern usart_dev *USART2; +/** USART3 device */ +extern usart_dev *USART3; +#ifdef STM32_HIGH_DENSITY +/** UART4 device */ +extern usart_dev *UART4; +/** UART5 device */ +extern usart_dev *UART5; +#endif + +void usart_init(usart_dev *dev); +void usart_set_baud_rate(usart_dev *dev, uint32 clock_speed, uint32 baud); +void usart_enable(usart_dev *dev); +void usart_disable(usart_dev *dev); +void usart_foreach(void (*fn)(usart_dev *dev)); +uint32 usart_tx(usart_dev *dev, const uint8 *buf, uint32 len); +void usart_putudec(usart_dev *dev, uint32 val); /** - * @brief send one character on a usart - * @param usart_num usart to send on - * @param byte byte to send + * @brief Disable all serial ports. */ -static inline void usart_putc(uint8 usart_num, uint8 byte) { - usart_port *port = usart_dev_table[usart_num].base; +static inline void usart_disable_all(void) { + usart_foreach(usart_disable); +} - /* Wait for the buffer to empty */ - while ((port->SR & USART_TXE) == 0) +/** + * @brief Transmit one character on a serial port. + * + * This function blocks until the character has been successfully + * transmitted. + * + * @param dev Serial port to send on. + * @param byte Byte to transmit. + */ +static inline void usart_putc(usart_dev* dev, uint8 byte) { + uint8 buf[] = {byte}; + while (!usart_tx(dev, buf, 1)) ; +} - port->DR = byte; +/** + * @brief Transmit a character string on a serial port. + * + * This function blocks until str is completely transmitted. + * + * @param dev Serial port to send on + * @param str String to send + */ +static inline void usart_putstr(usart_dev *dev, const char* str) { + uint32 i = 0; + while (str[i] != '\0') { + usart_putc(dev, str[i++]); + } } /** - * @brief read one character from a usart - * @param usart_num usart to read from + * @brief Read one character from a serial port. + * + * It's not safe to call this function if the serial port has no data + * available. + * + * @param dev Serial port to read from * @return byte read + * @see usart_data_available() */ -static inline uint8 usart_getc(uint8 usart_num) { - return rb_remove(&usart_dev_table[usart_num].rb); +static inline uint8 usart_getc(usart_dev *dev) { + return rb_remove(dev->rb); } /** - * @brief return the amount of data available in the rx buffer - * @param usart_num which usart to check - * @return number of bytes in the rx buffer + * @brief Return the amount of data available in a serial port's RX buffer. + * @param dev Serial port to check + * @return Number of bytes in dev's RX buffer. */ -static inline uint32 usart_data_available(uint8 usart_num) { - return rb_full_count(&usart_dev_table[usart_num].rb); +static inline uint32 usart_data_available(usart_dev *dev) { + return rb_full_count(dev->rb); } /** - * @brief removes the contents of the rx fifo - * @param usart_num which usart to reset + * @brief Discard the contents of a serial port's RX buffer. + * @param dev Serial port whose buffer to empty. */ -static inline void usart_reset_rx(uint8 usart_num) { - rb_reset(&usart_dev_table[usart_num].rb); +static inline void usart_reset_rx(usart_dev *dev) { + rb_reset(dev->rb); } -void usart_init(uint8 usart_num, uint32 baud); -void usart_disable(uint8 usart_num); -void usart_disable_all(); -void usart_putstr(uint8 usart_num, const char*); -void usart_putudec(uint8 usart_num, uint32 val); - #ifdef __cplusplus } // extern "C" #endif diff --git a/libmaple/usb/descriptors.c b/libmaple/usb/descriptors.c index 360e6dd..8dd9521 100644 --- a/libmaple/usb/descriptors.c +++ b/libmaple/usb/descriptors.c @@ -150,43 +150,50 @@ const USB_Descriptor_Config usbVcomDescriptor_Config = { // } }; -/* - String Identifiers: +/***************************************************************************** + ***************************************************************************** + *** + *** FIXME FIXME FIXME NOT THE RIGHT THING! MOVE ALL THIS INTO TO WIRISH! + *** + ***************************************************************************** + *****************************************************************************/ - we may choose to specify any or none of the following string - identifiers: +const uint8 usbVcomDescriptor_LangID[USB_DESCRIPTOR_STRING_LEN(1)] = { + USB_DESCRIPTOR_STRING_LEN(1), + USB_DESCRIPTOR_TYPE_STRING, + 0x09, + 0x04 +}; - iManufacturer: LeafLabs - iProduct: Maple R3 - iSerialNumber: NONE - iConfiguration: NONE - iInterface(CCI): NONE - iInterface(DCI): NONE +const uint8 usbVcomDescriptor_iManufacturer[USB_DESCRIPTOR_STRING_LEN(8)] = { + USB_DESCRIPTOR_STRING_LEN(8), + USB_DESCRIPTOR_TYPE_STRING, + 'L', 0, 'e', 0, 'a', 0, 'f', 0, + 'L', 0, 'a', 0, 'b', 0, 's', 0 +}; - additionally we must provide the unicode language identifier, - which is 0x0409 for US English -*/ +/* + String Identifiers: -const uint8 usbVcomDescriptor_LangID[USB_DESCRIPTOR_STRING_LEN(1)] = -{ - USB_DESCRIPTOR_STRING_LEN(1), - USB_DESCRIPTOR_TYPE_STRING, - 0x09, - 0x04 -}; + we may choose to specify any or none of the following string + identifiers: -const uint8 usbVcomDescriptor_iManufacturer[USB_DESCRIPTOR_STRING_LEN(8)] = -{ - USB_DESCRIPTOR_STRING_LEN(8), - USB_DESCRIPTOR_TYPE_STRING, - 'L', 0, 'e', 0, 'a', 0, 'f', 0, - 'L', 0, 'a', 0, 'b', 0, 's', 0 -}; + iManufacturer: LeafLabs + iProduct: Maple R3 + iSerialNumber: NONE + iConfiguration: NONE + iInterface(CCI): NONE + iInterface(DCI): NONE -const uint8 usbVcomDescriptor_iProduct[USB_DESCRIPTOR_STRING_LEN(8)] = -{ - USB_DESCRIPTOR_STRING_LEN(8), - USB_DESCRIPTOR_TYPE_STRING, - 'M', 0, 'a', 0, 'p', 0, 'l', 0, - 'e', 0, ' ', 0, 'R', 0, '3', 0 + additionally we must provide the unicode language identifier, + which is 0x0409 for US English +*/ +const uint8 usbVcomDescriptor_iProduct[USB_DESCRIPTOR_STRING_LEN(8)] = { + USB_DESCRIPTOR_STRING_LEN(8), + USB_DESCRIPTOR_TYPE_STRING, + 'M', 0, 'a', 0, 'p', 0, 'l', 0, + 'e', 0, ' ', 0, ' ', 0, ' ', 0 }; + +/***************************************************************************** + *****************************************************************************/ diff --git a/libmaple/usb/descriptors.h b/libmaple/usb/descriptors.h index 6f7d08b..460a88c 100644 --- a/libmaple/usb/descriptors.h +++ b/libmaple/usb/descriptors.h @@ -61,7 +61,7 @@ extern "C" { uint16 bString[len]; \ } -#define CDC_FUCNTIONAL_DESCRIPTOR(DataSize) \ +#define CDC_FUNCTIONAL_DESCRIPTOR(DataSize) \ struct \ { \ uint8 bLength; \ diff --git a/libmaple/usb/usb.c b/libmaple/usb/usb.c index d875785..b34c4b6 100644 --- a/libmaple/usb/usb.c +++ b/libmaple/usb/usb.c @@ -35,6 +35,7 @@ #include "usb_lib.h" #include "gpio.h" #include "usb_hardware.h" +#include "delay.h" #include "usb_config.h" #include "usb_callbacks.h" @@ -44,7 +45,7 @@ volatile uint32 bDeviceState = UNCONNECTED; volatile uint16 wIstr = 0; -volatile bIntPackSOF = 0; +volatile uint32 bIntPackSOF = 0; DEVICE Device_Table = {NUM_ENDPTS, @@ -99,15 +100,13 @@ struct { } ResumeS; void setupUSB (void) { - gpio_set_mode(USB_DISC_BANK, - USB_DISC_PIN, - GPIO_MODE_OUTPUT_PP); + gpio_set_mode(USB_DISC_DEV, USB_DISC_PIN, GPIO_OUTPUT_PP); /* setup the apb1 clock for USB */ pRCC->APB1ENR |= 0x00800000; /* initialize the usb application */ - gpio_write_bit(USB_DISC_BANK, USB_DISC_PIN, 0); // presents us to the host + gpio_write_bit(USB_DISC_DEV, USB_DISC_PIN, 0); // presents us to the host USB_Init(); // low level init routine provided by the ST library } @@ -115,7 +114,7 @@ void disableUSB (void) { // These are just guesses about how to do this // TODO: real disable function usbDsbISR(); - gpio_write_bit(USB_DISC_BANK,USB_DISC_PIN,1); + gpio_write_bit(USB_DISC_DEV, USB_DISC_PIN, 1); } void usbSuspend(void) { @@ -241,7 +240,7 @@ void usbDsbISR(void) { } /* overloaded ISR routine, this is the main usb ISR */ -void usb_lpIRQHandler(void) { +void __irq_usb_lp_can_rx0(void) { wIstr = _GetISTR(); /* go nuts with the preproc switches since this is an ISTR and must be FAST */ @@ -321,7 +320,7 @@ if (wIstr & ISTR_CTR & wInterrupt_Mask) { } void usbWaitReset(void) { - delay(RESET_DELAY); + delay_us(RESET_DELAY); systemHardReset(); } @@ -344,23 +343,21 @@ void usbBlockingSendByte(char ch) { countTx = 1; while (countTx); } -uint32 usbSendBytes(uint8* sendBuf, uint32 len) { - /* any checks on connection (via dtr/rts) done upstream in wirish or - by user */ - /* last xmit hasnt finished, abort */ +uint32 usbSendBytes(const uint8* sendBuf, uint32 len) { + /* Last transmission hasn't finished, abort */ if (countTx) { return 0; } // We can only put VCOM_TX_EPSIZE bytes in the buffer - if(len > VCOM_TX_EPSIZE/2) { - len = VCOM_TX_EPSIZE/2; + if (len > VCOM_TX_EPSIZE / 2) { + len = VCOM_TX_EPSIZE / 2; } // Try to load some bytes if we can if (len) { - UserToPMABufferCopy(sendBuf,VCOM_TX_ADDR, len); + UserToPMABufferCopy(sendBuf, VCOM_TX_ADDR, len); _SetEPTxCount(VCOM_TX_ENDP, len); countTx += len; _SetEPTxValid(VCOM_TX_ENDP); diff --git a/libmaple/usb/usb.h b/libmaple/usb/usb.h index 0ed02e5..c724c54 100644 --- a/libmaple/usb/usb.h +++ b/libmaple/usb/usb.h @@ -69,12 +69,12 @@ void usbDsbISR(void); void usbEnbISR(void); /* overloaded ISR routine, this is the main usb ISR */ -void usb_lpIRQHandler(void); +void __irq_usb_lp_can_rx0(void); void usbWaitReset(void); /* blocking functions for send/receive */ void usbBlockingSendByte(char ch); -uint32 usbSendBytes(uint8* sendBuf,uint32 len); +uint32 usbSendBytes(const uint8* sendBuf,uint32 len); uint32 usbBytesAvailable(void); uint32 usbReceiveBytes(uint8* recvBuf, uint32 len); uint8 usbGetDTR(void); diff --git a/libmaple/usb/usb_callbacks.c b/libmaple/usb/usb_callbacks.c index ccb0fdd..8184537 100644 --- a/libmaple/usb/usb_callbacks.c +++ b/libmaple/usb/usb_callbacks.c @@ -129,7 +129,7 @@ u8* vcomGetSetLineCoding(uint16 length) { return (uint8*)&line_coding; } -vcomSetLineState(void) { +void vcomSetLineState(void) { } void usbInit(void) { diff --git a/libmaple/usb/usb_config.h b/libmaple/usb/usb_config.h index e5f3979..23b49ee 100644 --- a/libmaple/usb/usb_config.h +++ b/libmaple/usb/usb_config.h @@ -4,6 +4,68 @@ #define __USB_CONFIG_H #include "usb_lib.h" +#include "gpio.h" + +/****************************************************************************** + ****************************************************************************** + *** + *** HACK ALERT + *** + *** FIXME FIXME FIXME FIXME + *** + *** A bunch of board-specific #defines that are only used by the + *** USB routines got put into libmaple.h for what appear to be + *** historical reasons. I'm [mbolivar] putting them in here for + *** now, so that we can treat the usb/ directory as a black box, + *** freeing the rest of libmaple/ to be implemented as a + *** general-purpose STM32 library. All of this REALLY needs to get + *** moved into wirish when we get a chance to redo the USB stack. + *** + ****************************************************************************** + *****************************************************************************/ + +#define VCOM_ID_VENDOR 0x1EAF +#define RESET_DELAY (100000) +#define USB_CONFIG_MAX_POWER (100 >> 1) + +#if defined(BOARD_maple) || defined(BOARD_maple_RET6) + + /* USB Identifier numbers */ + #define VCOM_ID_PRODUCT 0x0004 + #define USB_DISC_DEV GPIOC + #define USB_DISC_PIN 12 + +#elif defined(BOARD_maple_mini) + + #define VCOM_ID_PRODUCT 0x0004 + #define USB_DISC_DEV GPIOB + #define USB_DISC_PIN 9 + +#elif defined(BOARD_maple_native) + + #define VCOM_ID_PRODUCT 0x0004 + #define USB_DISC_DEV GPIOB + #define USB_DISC_PIN 8 + +#else + +#error ("Sorry! the USB stack relies on LeafLabs board-specific " \ + "configuration right now. If you want, you can pretend you're one " \ + "of our boards; i.e., #define BOARD_maple, BOARD_maple_mini, or " \ + "BOARD_maple_native according to what matches your MCU best. " \ + "You should also take a look at libmaple/usb/descriptors.c; we make " \ + "some assumptions there that you probably won't like.") + +#endif + +/****************************************************************************** + ****************************************************************************** + *** + *** END HACK + *** + ****************************************************************************** + *****************************************************************************/ + /* choose addresses to give endpoints the max 64 byte buffers */ #define USB_BTABLE_ADDRESS 0x00 @@ -33,14 +95,15 @@ #define NUM_ENDPTS 0x04 /* handle all usb interrupts */ -#define ISR_MSK ( CNTR_CTRM | \ - CNTR_WKUPM | \ - CNTR_SUSPM | \ - CNTR_ERRM | \ - CNTR_SOFM | \ - CNTR_ESOFM | \ - CNTR_RESETM ) +#define ISR_MSK (CNTR_CTRM | \ + CNTR_WKUPM | \ + CNTR_SUSPM | \ + CNTR_ERRM | \ + CNTR_SOFM | \ + CNTR_ESOFM | \ + CNTR_RESETM) #define F_SUSPEND_ENABLED 1 + #endif diff --git a/libmaple/usb/usb_hardware.c b/libmaple/usb/usb_hardware.c index 505dcf1..9a7d12c 100644 --- a/libmaple/usb/usb_hardware.c +++ b/libmaple/usb/usb_hardware.c @@ -33,47 +33,6 @@ #include "usb_hardware.h" -void setPin(u32 bank, u8 pin) { - u32 pinMask = 0x1 << (pin); - SET_REG(GPIO_BSRR(bank),pinMask); -} - -void resetPin(u32 bank, u8 pin) { - u32 pinMask = 0x1 << (16+pin); - SET_REG(GPIO_BSRR(bank),pinMask); -} - -void systemReset(void) { - SET_REG(RCC_CR, GET_REG(RCC_CR) | 0x00000001); - SET_REG(RCC_CFGR, GET_REG(RCC_CFGR) & 0xF8FF0000); - SET_REG(RCC_CR, GET_REG(RCC_CR) & 0xFEF6FFFF); - SET_REG(RCC_CR, GET_REG(RCC_CR) & 0xFFFBFFFF); - SET_REG(RCC_CFGR, GET_REG(RCC_CFGR) & 0xFF80FFFF); - - SET_REG(RCC_CIR, 0x00000000); // disable all RCC interrupts -} - -void setupCLK (void) { - /* enable HSE */ - SET_REG(RCC_CR,GET_REG(RCC_CR) | 0x00010001); - /* for it to come on */ - while ((GET_REG(RCC_CR) & 0x00020000) == 0); - - /* Configure PLL */ - /* pll=72Mhz,APB1=36Mhz,AHB=72Mhz */ - SET_REG(RCC_CFGR,GET_REG(RCC_CFGR) | 0x001D0400); - /* enable the pll */ - SET_REG(RCC_CR,GET_REG(RCC_CR) | 0x01000000); - /* wait for it to come on */ - while ((GET_REG(RCC_CR) & 0x03000000) == 0); - - /* Set SYSCLK as PLL */ - SET_REG(RCC_CFGR,GET_REG(RCC_CFGR) | 0x00000002); - /* wait for it to come on */ - while ((GET_REG(RCC_CFGR) & 0x00000008) == 0); -} - - void nvicInit(NVIC_InitTypeDef* NVIC_InitStruct) { u32 tmppriority = 0x00; u32 tmpreg = 0x00; diff --git a/libmaple/usb/usb_hardware.h b/libmaple/usb/usb_hardware.h index e4a26b4..cfead1a 100644 --- a/libmaple/usb/usb_hardware.h +++ b/libmaple/usb/usb_hardware.h @@ -22,34 +22,20 @@ * THE SOFTWARE. * ****************************************************************************/ -#ifndef __HARDWARE_H -#define __HARDWARE_H - +#include "rcc.h" #include "usb_type.h" +#ifndef _USB_HARDWARE_H_ +#define _USB_HARDWARE_H_ + /* macro'd register and peripheral definitions */ #define EXC_RETURN 0xFFFFFFF9 #define DEFAULT_CPSR 0x61000000 -#define RCC ((u32)0x40021000) #define FLASH ((u32)0x40022000) -#define GPIOA ((u32)0x40010800) -#define GPIOC ((u32)0x40011000) #define USB_PACKET_BUFFER ((u32)0x40006000) -#define RCC_CR RCC -#define RCC_CFGR RCC + 0x04 -#define RCC_CIR RCC + 0x08 -#define RCC_AHBENR RCC + 0x14 -#define RCC_APB2ENR RCC + 0x18 -#define RCC_APB1ENR RCC + 0x16 - -#define GPIO_CRL(port) port -#define GPIO_CRH(port) port+0x04 -#define GPIO_ODR(port) port+0x0c -#define GPIO_BSRR(port) port+0x10 - #define SCS_BASE ((u32)0xE000E000) #define NVIC_BASE (SCS_BASE + 0x0100) #define SCB_BASE (SCS_BASE + 0x0D00) @@ -74,9 +60,8 @@ #define __PSC(__TIMCLK, __PERIOD) (((__VAL(__TIMCLK, __PERIOD)+49999UL)/50000UL) - 1) #define __ARR(__TIMCLK, __PERIOD) ((__VAL(__TIMCLK, __PERIOD)/(__PSC(__TIMCLK, __PERIOD)+1)) - 1) -/* todo, wrap in do whiles for the natural ; */ -#define SET_REG(addr,val) *(vu32*)(addr)=val -#define GET_REG(addr) *(vu32*)(addr) +#define SET_REG(addr,val) do { *(vu32*)(addr)=val; } while (0) +#define GET_REG(addr) do { *(vu32*)(addr); } while (0) #if defined(__cplusplus) extern "C" { @@ -85,7 +70,7 @@ extern "C" { /* todo: there must be some major misunderstanding in how we access regs. The direct access approach (GET_REG) causes the usb init to fail upon trying to activate RCC_APB1 |= 0x00800000. However, using the struct approach from ST, it works fine...temporarily switching to that approach */ -typedef struct +typedef struct { vu32 CR; vu32 CFGR; @@ -98,7 +83,7 @@ typedef struct vu32 BDCR; vu32 CSR; } RCC_RegStruct; -#define pRCC ((RCC_RegStruct *) RCC) +#define pRCC ((RCC_RegStruct *) RCC_BASE) typedef struct { vu32 ISER[2]; @@ -139,12 +124,7 @@ typedef struct { } SCB_TypeDef; -void setPin (u32 bank, u8 pin); -void resetPin (u32 bank, u8 pin); - void systemHardReset(void); -void systemReset (void); -void setupCLK (void); void nvicInit (NVIC_InitTypeDef*); void nvicDisableInterrupts(void); diff --git a/libmaple/usb/usb_lib/usb_mem.c b/libmaple/usb/usb_lib/usb_mem.c index ee698c5..90ffc62 100644 --- a/libmaple/usb/usb_lib/usb_mem.c +++ b/libmaple/usb/usb_lib/usb_mem.c @@ -30,9 +30,9 @@ * - wPMABufAddr: address into PMA.
* - wNBytes: no. of bytes to be copied.
* Output : None.
-* Return : None .
+* Return : None .
*******************************************************************************/
-void UserToPMABufferCopy(u8 *pbUsrBuf, u16 wPMABufAddr, u16 wNBytes)
+void UserToPMABufferCopy(const u8 *pbUsrBuf, u16 wPMABufAddr, u16 wNBytes)
{
u32 n = (wNBytes + 1) >> 1; /* n = (wNBytes + 1) / 2 */
u32 i, temp1, temp2;
diff --git a/libmaple/usb/usb_lib/usb_mem.h b/libmaple/usb/usb_lib/usb_mem.h index a3d7927..60ff9f1 100644 --- a/libmaple/usb/usb_lib/usb_mem.h +++ b/libmaple/usb/usb_lib/usb_mem.h @@ -26,7 +26,7 @@ extern "C" {
#endif
-void UserToPMABufferCopy(u8 *pbUsrBuf, u16 wPMABufAddr, u16 wNBytes);
+void UserToPMABufferCopy(const u8 *pbUsrBuf, u16 wPMABufAddr, u16 wNBytes);
void PMAToUserBufferCopy(u8 *pbUsrBuf, u16 wPMABufAddr, u16 wNBytes);
#if defined(__cplusplus)
diff --git a/libmaple/util.c b/libmaple/util.c index 135f005..4a234ae 100644 --- a/libmaple/util.c +++ b/libmaple/util.c @@ -24,7 +24,7 @@ /** * @brief Utility procedures for debugging, mostly an error LED fade - * and messages dumped over a uart for failed asserts. + * and messages dumped over a UART for failed asserts. */ #include "libmaple.h" @@ -32,60 +32,92 @@ #include "gpio.h" #include "nvic.h" #include "adc.h" -#include "timers.h" - -/* Error assert + fade */ -void _fail(const char* file, int line, const char* exp) { - int32 slope = 1; - uint32 CC = 0x0000; - uint32 TOP_CNT = 0x02FF; - uint32 i = 0; +#include "timer.h" + +/* Failed ASSERT()s send out a message using this USART config. */ +#ifndef ERROR_USART +#define ERROR_USART USART2 +#define ERROR_USART_CLK_SPEED 72000000UL +#define ERROR_USART_BAUD 9600 +#define ERROR_TX_PORT GPIOA +#define ERROR_TX_PIN 2 +#endif + +/* If you define ERROR_LED_PORT and ERROR_LED_PIN, then a failed + * ASSERT() will also throb() an LED connected to that port and pin. + */ +#if defined(ERROR_LED_PORT) && defined(ERROR_LED_PIN) +#define HAVE_ERROR_LED +#endif - /* Turn off interrupts */ +/** + * @brief Disables all peripheral interrupts except USB and fades the + * error LED. + */ +/* (Called from exc.S with global interrupts disabled.) */ +void __error(void) { + /* Turn off peripheral interrupts */ nvic_irq_disable_all(); - /* Turn off timers */ + /* Turn off timers */ timer_disable_all(); /* Turn off ADC */ - adc_disable(); + adc_disable_all(); - /* Turn off all usarts */ + /* Turn off all USARTs */ usart_disable_all(); - /* Initialize the error usart */ - gpio_set_mode(ERROR_TX_PORT, ERROR_TX_PIN, GPIO_MODE_AF_OUTPUT_PP); - usart_init(ERROR_USART_NUM, ERROR_USART_BAUD); + /* Turn the USB interrupt back on so the bootloader keeps on functioning */ + nvic_irq_enable(NVIC_USB_HP_CAN_TX); + nvic_irq_enable(NVIC_USB_LP_CAN_RX0); - /* Print failed assert message */ - usart_putstr(ERROR_USART_NUM, "ERROR: FAILED ASSERT("); - usart_putstr(ERROR_USART_NUM, exp); - usart_putstr(ERROR_USART_NUM, "): "); - usart_putstr(ERROR_USART_NUM, file); - usart_putstr(ERROR_USART_NUM, ": "); - usart_putudec(ERROR_USART_NUM, line); - usart_putc(ERROR_USART_NUM, '\n'); - usart_putc(ERROR_USART_NUM, '\r'); - - /* Turn on the error LED */ - gpio_set_mode(ERROR_LED_PORT, ERROR_LED_PIN, GPIO_MODE_OUTPUT_PP); + /* Reenable global interrupts */ + nvic_globalirq_enable(); + throb(); +} - /* Turn the USB interrupt back on so the bootloader keeps on functioning */ - nvic_irq_enable(NVIC_INT_USBHP); - nvic_irq_enable(NVIC_INT_USBLP); +/** + * @brief Print an error message on a UART upon a failed assertion + * and throb the error LED, if there is one defined. + * @param file Source file of failed assertion + * @param line Source line of failed assertion + * @param exp String representation of failed assertion + * @sideeffect Turns of all peripheral interrupts except USB. + */ +void _fail(const char* file, int line, const char* exp) { + /* Initialize the error USART */ + gpio_set_mode(ERROR_TX_PORT, ERROR_TX_PIN, GPIO_AF_OUTPUT_PP); + usart_init(ERROR_USART); + usart_set_baud_rate(ERROR_USART, ERROR_USART_CLK_SPEED, ERROR_USART_BAUD); + + /* Print failed assert message */ + usart_putstr(ERROR_USART, "ERROR: FAILED ASSERT("); + usart_putstr(ERROR_USART, exp); + usart_putstr(ERROR_USART, "): "); + usart_putstr(ERROR_USART, file); + usart_putstr(ERROR_USART, ": "); + usart_putudec(ERROR_USART, line); + usart_putc(ERROR_USART, '\n'); + usart_putc(ERROR_USART, '\r'); /* Error fade */ - throb(); + __error(); } +/** + * @brief Fades the error LED on and off + * @sideeffect Sets output push-pull on ERROR_LED_PIN. + */ void throb(void) { +#ifdef HAVE_ERROR_LED int32 slope = 1; uint32 CC = 0x0000; uint32 TOP_CNT = 0x0200; uint32 i = 0; - gpio_set_mode(ERROR_LED_PORT, ERROR_LED_PIN, GPIO_MODE_OUTPUT_PP); - /* Error fade */ + gpio_set_mode(ERROR_LED_PORT, ERROR_LED_PIN, GPIO_OUTPUT_PP); + /* Error fade. */ while (1) { if (CC == TOP_CNT) { slope = -1; @@ -105,5 +137,9 @@ void throb(void) { } i++; } +#else + /* No error LED is defined; do nothing. */ + while (1) + ; +#endif } - diff --git a/libmaple/util.h b/libmaple/util.h index 64782d9..f54f3fd 100644 --- a/libmaple/util.h +++ b/libmaple/util.h @@ -23,59 +23,48 @@ *****************************************************************************/ /** - * @file util.h - * - * @brief Various macros and utility procedures. + * @file util.h + * @brief Miscellaneous utility macros and procedures. */ -/* Generally "useful" utility procedures */ +#include "libmaple_types.h" + #ifndef _UTIL_H_ #define _UTIL_H_ -#include "libmaple.h" +#ifdef __cplusplus +extern "C"{ +#endif + +/* + * Bit manipulation + */ #define BIT(shift) (1UL << (shift)) #define BIT_MASK_SHIFT(mask, shift) ((mask) << (shift)) - -/* Return bits m to n of x */ +/** Bits m to n of x */ #define GET_BITS(x, m, n) ((((uint32)x) << (31 - (n))) >> ((31 - (n)) + (m))) - -/* Bit-banding macros */ -/* Convert SRAM address */ -#define BITBAND_SRAM(a,b) ((BITBAND_SRAM_BASE+(a-BITBAND_SRAM_REF)*32+(b*4))) -/* Convert PERI address */ -#define BITBAND_PERI(a,b) ((BITBAND_PERI_BASE+(a-BITBAND_PERI_REF)*32+(b*4))) - -#define REG_SET(reg, val) (*(volatile uint32*)(reg) = (val)) -#define REG_SET_BIT(reg, bit) (*(volatile uint32*)(reg) |= BIT(bit)) -#define REG_CLEAR_BIT(reg, bit) (*(volatile uint32*)(reg) &= ~BIT(bit)) -#define REG_SET_MASK(reg, mask) (*(volatile uint32*)(reg) |= (uint32)(mask)) -#define REG_CLEAR_MASK(reg, mask) (*(volatile uint32*)(reg) &= (uint32)~(mask)) - -#define REG_GET(reg) (*(volatile uint32*)(reg)) - -#define __set_bits(addr, mask) (*(volatile uint32*)(addr) |= (uint32)(mask)) -#define __clear_bits(addr, mask) (*(volatile uint32*)(addr) &= (uint32)~(mask)) -#define __get_bits(addr, mask) (*(volatile uint32*)(addr) & (uint32)(mask)) - -#define __read(reg) (*(volatile uint32*)(reg)) -#define __write(reg, value) (*(volatile uint32*)(reg) = (value)) - #define IS_POWER_OF_TWO(v) (v && !(v & (v - 1))) -#ifdef __cplusplus -extern "C"{ -#endif +/* + * Failure routines + */ +void __error(void); void _fail(const char*, int, const char*); void throb(void); -#ifdef __cplusplus -} // extern "C" -#endif +/* + * Asserts and debug levels + */ -/* Asserts for sanity checks, redefine DEBUG_LEVEL in libmaple.h to - * compile out these checks */ +#define DEBUG_NONE 0 +#define DEBUG_FAULT 1 +#define DEBUG_ALL 2 + +#ifndef DEBUG_LEVEL +#define DEBUG_LEVEL DEBUG_ALL +#endif #if DEBUG_LEVEL >= DEBUG_ALL #define ASSERT(exp) \ @@ -83,7 +72,6 @@ void throb(void); } else { \ _fail(__FILE__, __LINE__, #exp); \ } - #else #define ASSERT(exp) (void)((0)) #endif @@ -94,10 +82,12 @@ void throb(void); } else { \ _fail(__FILE__, __LINE__, #exp); \ } - #else #define ASSERT_FAULT(exp) (void)((0)) #endif +#ifdef __cplusplus +} // extern "C" #endif +#endif diff --git a/libraries/Servo/Servo.cpp b/libraries/Servo/Servo.cpp index ae87b63..5122391 100644 --- a/libraries/Servo/Servo.cpp +++ b/libraries/Servo/Servo.cpp @@ -29,110 +29,120 @@ #include "pwm.h" #include "wirish_math.h" -// Configure prescaler and overflow for a 20msec period (could just -// use HardwareTimer::setPeriod(), but this lets conversions below -// happen more statically, in combination with an inlined map() -- a -// premature optimization? TODO profile speed/size tradeoff) -#define CYC_20MSEC (20000 * CYCLES_PER_MICROSECOND) -#define SERVO_PRE ((uint16)((CYC_20MSEC >> 16) + 1)) -#define SERVO_OVF ((uint16)((CYC_20MSEC / SERVO_PRE) - 1)) -#define SERVO_TAU_USEC \ - ((uint32)(((double)SERVO_OVF) * SERVO_PRE / CYCLES_PER_MICROSECOND + 0.5)) - -#define US_TO_COMPARE(us) ((uint16)map(us, 0, SERVO_TAU_USEC, 0, SERVO_OVF)) -#define COMPARE_TO_US(c) ((uint32)map(c, 0, SERVO_OVF, 0, SERVO_TAU_USEC)) - -#define ANGLE_TO_US(a) ((uint16)(map(a, 0, 180, this->min, this->max))) -#define US_TO_ANGLE(us) ((uint8)(map(us, this->min, this->max, 0, 180))) +// 20 millisecond period config. For a 1-based prescaler, +// +// (prescaler * overflow / CYC_MSEC) msec = 1 timer cycle = 20 msec +// => prescaler * overflow = 20 * CYC_MSEC +// +// This picks the smallest prescaler that allows an overflow < 2^16. +#define MAX_OVERFLOW ((1 << 16) - 1) +#define CYC_MSEC (1000 * CYCLES_PER_MICROSECOND) +#define TAU_MSEC 20 +#define TAU_USEC (TAU_MSEC * 1000) +#define TAU_CYC (TAU_MSEC * CYC_MSEC) +#define SERVO_PRESCALER (TAU_CYC / MAX_OVERFLOW + 1) +#define SERVO_OVERFLOW ((uint16)round((double)TAU_CYC / SERVO_PRESCALER)) + +// Unit conversions +#define US_TO_COMPARE(us) ((uint16)map((us), 0, TAU_USEC, 0, SERVO_OVERFLOW)) +#define COMPARE_TO_US(c) ((uint32)map((c), 0, SERVO_OVERFLOW, 0, TAU_USEC)) +#define ANGLE_TO_US(a) ((uint16)(map((a), this->minAngle, this->maxAngle, \ + this->minPW, this->maxPW))) +#define US_TO_ANGLE(us) ((int16)(map((us), this->minPW, this->maxPW, \ + this->minAngle, this->maxAngle))) Servo::Servo() { - this->pin = NOT_ATTACHED; - this->timer = 0; - this->channel = TIMER_INVALID; - this->min = SERVO_DEFAULT_MIN_PW; - this->max = SERVO_DEFAULT_MAX_PW; + this->resetFields(); } -bool Servo::attach(uint8_t pin) { - return this->attach(pin, SERVO_DEFAULT_MIN_PW, SERVO_DEFAULT_MAX_PW); -} +bool Servo::attach(uint8 pin, + uint16 minPW, + uint16 maxPW, + int16 minAngle, + int16 maxAngle) { + timer_dev *tdev = PIN_MAP[pin].timer_device; -bool Servo::attach(uint8_t pin, uint16_t min, uint16_t max) { - timer_dev_num timer_num = PIN_MAP[pin].timer_num; - uint32_t channel = PIN_MAP[pin].timer_chan; - if (timer_num == TIMER_INVALID) { - // don't reset any members or ASSERT(0), to keep driving any + if (tdev == NULL) { + // don't reset any fields or ASSERT(0), to keep driving any // previously attach()ed servo. return false; } + + if (this->attached()) { + this->detach(); + } + this->pin = pin; - this->timer = getTimer(timer_num); - this->channel = channel; - this->min = min; - this->max = max; + this->minPW = minPW; + this->maxPW = maxPW; + this->minAngle = minAngle; + this->maxAngle = maxAngle; pinMode(pin, PWM); - this->timer->pause(); - this->timer->setPrescaleFactor(SERVO_PRE); - this->timer->setOverflow(SERVO_OVF); - this->timer->generateUpdate(); - this->timer->resume(); + timer_pause(tdev); + timer_set_prescaler(tdev, SERVO_PRESCALER - 1); // prescaler is 1-based + timer_set_reload(tdev, SERVO_OVERFLOW); + timer_generate_update(tdev); + timer_resume(tdev); + return true; } bool Servo::detach() { - if (this->pin == NOT_ATTACHED) return false; + if (!this->attached()) { + return false; + } - this->timer->setChannelMode(this->channel, TIMER_DISABLED); + timer_dev *tdev = PIN_MAP[this->pin].timer_device; + uint8 tchan = PIN_MAP[this->pin].timer_channel; + timer_set_mode(tdev, tchan, TIMER_DISABLED); - this->pin = NOT_ATTACHED; - this->timer = 0; - this->channel = TIMER_INVALID; - this->min = SERVO_DEFAULT_MIN_PW; - this->max = SERVO_DEFAULT_MAX_PW; + this->resetFields(); return true; } -void Servo::write(unsigned int value) { - if (value < SERVO_MAX_WRITE_ANGLE) { - this->writeMicroseconds(ANGLE_TO_US(value)); - } else { - this->writeMicroseconds(value); - } +void Servo::write(int degrees) { + degrees = constrain(degrees, this->minAngle, this->maxAngle); + this->writeMicroseconds(ANGLE_TO_US(degrees)); } -void Servo::writeMicroseconds(uint16_t pulseWidth) { - if (this->pin == NOT_ATTACHED) { +int Servo::read() const { + int a = US_TO_ANGLE(this->readMicroseconds()); + // map() round-trips in a weird way we mostly correct for here; + // the round-trip is still sometimes off-by-one for write(1) and + // write(179). + return a == this->minAngle || a == this->maxAngle ? a : a + 1; +} + +void Servo::writeMicroseconds(uint16 pulseWidth) { + if (!this->attached()) { ASSERT(0); return; } - pulseWidth = constrain(pulseWidth, this->min, this->max); + pulseWidth = constrain(pulseWidth, this->minPW, this->maxPW); pwmWrite(this->pin, US_TO_COMPARE(pulseWidth)); } -int Servo::read() const { - if (this->pin == NOT_ATTACHED) { +uint16 Servo::readMicroseconds() const { + if (!this->attached()) { ASSERT(0); return 0; } - unsigned int pw = this->readMicroseconds(); - int a = US_TO_ANGLE(pw); - // map() round-trips in a weird way we correct for here - return a == 0 || a == 180 ? a : a + 1; -} + stm32_pin_info pin_info = PIN_MAP[this->pin]; + uint16 compare = timer_get_compare(pin_info.timer_device, + pin_info.timer_channel); -uint16_t Servo::readMicroseconds() const { - if (this->pin == NOT_ATTACHED) { - ASSERT(0); - return 0; - } + return COMPARE_TO_US(compare); +} - unsigned int compare = this->timer->getCompare(this->channel); - uint16_t c = COMPARE_TO_US(compare); - // map() round-trips in a weird way we correct for here - return c == 0 || c == 180 ? c : c + 1; +void Servo::resetFields(void) { + this->pin = NOT_ATTACHED; + this->minAngle = SERVO_DEFAULT_MIN_ANGLE; + this->maxAngle = SERVO_DEFAULT_MAX_ANGLE; + this->minPW = SERVO_DEFAULT_MIN_PW; + this->maxPW = SERVO_DEFAULT_MAX_PW; } diff --git a/libraries/Servo/Servo.h b/libraries/Servo/Servo.h index 1c75618..f80339e 100644 --- a/libraries/Servo/Servo.h +++ b/libraries/Servo/Servo.h @@ -25,92 +25,178 @@ #ifndef _SERVO_H_ #define _SERVO_H_ -#include <stdint.h> +#include "libmaple_types.h" +#include "timer.h" -#include "wirish.h" /* hack for IDE compile */ - -/* Note on Arduino compatibility: - - In the Arduino implementation, PWM is done "by hand" in the sense - that timer channels are hijacked in groups and an ISR is set which - toggles Servo::attach()ed pins using digitalWrite(). +#include "wirish_types.h" - While this scheme allows any pin to drive a servo, it chews up - cycles and complicates the programmer's notion of when a particular - timer channel will be in use. - - This implementation only allows Servo instances to Servo::attach() - to pins that already have a timer channel associated with them, and - just uses pwmWrite() to drive the wave. - - This introduces an incompatibility: while the Arduino - implementation of attach() returns the affected channel on success - and 0 on failure, this one returns true on success and false on - failure. +#ifdef MAPLE_IDE +#include "wirish.h" /* hack for IDE compile */ +#endif - RC Servos expect a pulse every 20ms. Since periods are set for - entire timers, rather than individual channels, attach()ing a Servo - to a pin can interfere with other pins associated with the same - timer. As always, the pin mapping mega table is your friend. +/* + * Note on Arduino compatibility: + * + * In the Arduino implementation, PWM is done "by hand" in the sense + * that timer channels are hijacked in groups and an ISR is set which + * toggles Servo::attach()ed pins using digitalWrite(). + * + * While this scheme allows any pin to drive a servo, it chews up + * cycles and complicates the programmer's notion of when a particular + * timer channel will be in use. + * + * This implementation only allows Servo instances to attach() to pins + * that already have a timer channel associated with them, and just + * uses pwmWrite() to drive the wave. + * + * This introduces an incompatibility: while the Arduino + * implementation of attach() returns the affected channel on success + * and 0 on failure, this one returns true on success and false on + * failure. + * + * RC Servos expect a pulse every 20ms. Since periods are set for + * entire timers, rather than individual channels, attach()ing a Servo + * to a pin can interfere with other pins associated with the same + * timer. As always, your board's pin map is your friend. */ // Pin number of unattached pins -#define NOT_ATTACHED (-1) +#define NOT_ATTACHED (-1) // Maximum angle in degrees you can write(), exclusive. Value chosen -// for Arduino compatibility. -#define SERVO_MAX_WRITE_ANGLE (200) - -// Default min (0 deg)/max(180 deg) pulse widths, in microseconds. -// Value chosen for Arduino compatibility. -#define SERVO_DEFAULT_MIN_PW (544) -#define SERVO_DEFAULT_MAX_PW (2400) +// for Arduino compatibility. This value is part of the public API; +// DO NOT CHANGE IT. +#define SERVO_MAX_WRITE_ANGLE 200 + +// Default min/max pulse widths (in microseconds) and angles (in +// degrees). Values chosen for Arduino compatibility. These values +// are part of the public API; DO NOT CHANGE THEM. +#define SERVO_DEFAULT_MIN_PW 544 +#define SERVO_DEFAULT_MAX_PW 2400 +#define SERVO_DEFAULT_MIN_ANGLE 0 +#define SERVO_DEFAULT_MAX_ANGLE 180 class Servo { public: + /** + * @brief Construct a new Servo instance. + * + * The new instance will not be attached to any pin. + */ Servo(); - /* Pin has to have a timer channel associated with it already; - * sets pinMode to PWM and returns true iff successful (failure - * when pin doesn't support PWM). doesn't detach any ISRs - * associated with timer channel. */ - bool attach(uint8_t pin); - - /* Like attach(int), but with (inclusive) min (0 degree) and max - * (180 degree) pulse widths, in microseconds. + /** + * @brief Associate this instance with a servomotor whose input is + * connected to pin. + * + * If this instance is already attached to a pin, it will be + * detached before being attached to the new pin. This function + * doesn't detach any interrupt attached with the pin's timer + * channel. + * + * @param pin Pin connected to the servo pulse wave input. This + * pin must be capable of PWM output. + * + * @param minPulseWidth Minimum pulse width to write to pin, in + * microseconds. This will be associated + * with a minAngle degree angle. Defaults to + * SERVO_DEFAULT_MIN_PW = 544. + * + * @param maxPulseWidth Maximum pulse width to write to pin, in + * microseconds. This will be associated + * with a maxAngle degree angle. Defaults to + * SERVO_DEFAULT_MAX_PW = 2400. + * + * @param minAngle Target angle (in degrees) associated with + * minPulseWidth. Defaults to + * SERVO_DEFAULT_MIN_ANGLE = 0. + * + * @param maxAngle Target angle (in degrees) associated with + * maxPulseWidth. Defaults to + * SERVO_DEFAULT_MAX_ANGLE = 180. + * + * @sideeffect May set pinMode(pin, PWM). + * + * @return true if successful, false when pin doesn't support PWM. */ - bool attach(uint8_t pin, uint16_t min, uint16_t max); - - /* Return pin number if currently attach()ed to a pin, - NOT_ATTACHED otherwise. */ - int attached() const { return pin; } + bool attach(uint8 pin, + uint16 minPulseWidth=SERVO_DEFAULT_MIN_PW, + uint16 maxPulseWidth=SERVO_DEFAULT_MAX_PW, + int16 minAngle=SERVO_DEFAULT_MIN_ANGLE, + int16 maxAngle=SERVO_DEFAULT_MAX_ANGLE); + + /** + * @brief Check if this instance is attached to a servo. + * @return true if this instance is attached to a servo, false otherwise. + * @see Servo::attachedPin() + */ + bool attached() const { return this->pin != NOT_ATTACHED; } - /* Stop driving the wave by disabling the output compare - interrupt. Returns true if this call did anything. */ + /** + * @brief Get the pin this instance is attached to. + * @return Pin number if currently attached to a pin, NOT_ATTACHED + * otherwise. + * @see Servo::attach() + */ + int attachedPin() const { return this->pin; } + + /** + * @brief Stop driving the servo pulse train. + * + * If not currently attached to a motor, this function has no effect. + * + * @return true if this call did anything, false otherwise. + */ bool detach(); - /* If value < MAX_WRITE_ANGLE, treated as an angle in degrees. - Otherwise, it's treated as a pulse width. */ - void write(unsigned int value); + /** + * @brief Set the servomotor target angle. + * + * @param angle Target angle, in degrees. If the target angle is + * outside the range specified at attach() time, it + * will be clamped to lie in that range. + * + * @see Servo::attach() + */ + void write(int angle); - /* If outside of [min, max] determined by attach(), it is clamped - to lie in that range. */ - void writeMicroseconds(uint16_t pulseWidth); - /* Return servo target angle, in degrees. This will lie between 0 - and 180. */ + /** + * Get the servomotor's target angle, in degrees. This will + * lie inside the range specified at attach() time. + * + * @see Servo::attach() + */ int read() const; - /* Returns the current pulse width, in microseconds. This will - lie within the [min, max] range. */ - uint16_t readMicroseconds() const; + /** + * @brief Set the pulse width, in microseconds. + * + * @param pulseWidth Pulse width to send to the servomotor, in + * microseconds. If outside of the range + * specified at attach() time, it is clamped to + * lie in that range. + * + * @see Servo::attach() + */ + void writeMicroseconds(uint16 pulseWidth); + + /** + * Get the current pulse width, in microseconds. This will + * lie within the range specified at attach() time. + * + * @see Servo::attach() + */ + uint16 readMicroseconds() const; private: - int8_t pin; - HardwareTimer *timer; - int channel; - uint16_t min; - uint16_t max; + int16 pin; + uint16 minPW; + uint16 maxPW; + int16 minAngle; + int16 maxAngle; + + void resetFields(void); }; #endif /* _SERVO_H_ */ diff --git a/libraries/Servo/rules.mk b/libraries/Servo/rules.mk index 13cd364..e013754 100644 --- a/libraries/Servo/rules.mk +++ b/libraries/Servo/rules.mk @@ -5,27 +5,21 @@ d := $(dir) BUILDDIRS += $(BUILD_PATH)/$(d) # Local flags -CFLAGS_$(d) := $(WIRISH_INCLUDES) $(LIBMAPLE_INCLUDES) +CXXFLAGS_$(d) := $(WIRISH_INCLUDES) $(LIBMAPLE_INCLUDES) # Local rules and targets cSRCS_$(d) := -# examples/UDPApp/udpapp.c \ -# examples/SocketApp/socketapp.c \ -# examples/WebClient/webclient.c \ -# examples/WebServer/webserver.c \ -# examples/Flash/webserver.c \ - cppSRCS_$(d) := Servo.cpp cFILES_$(d) := $(cSRCS_$(d):%=$(d)/%) cppFILES_$(d) := $(cppSRCS_$(d):%=$(d)/%) OBJS_$(d) := $(cFILES_$(d):%.c=$(BUILD_PATH)/%.o) \ - $(cppFILES_$(d):%.cpp=$(BUILD_PATH)/%.o) + $(cppFILES_$(d):%.cpp=$(BUILD_PATH)/%.o) DEPS_$(d) := $(OBJS_$(d):%.o=%.d) -$(OBJS_$(d)): TGT_CFLAGS := $(CFLAGS_$(d)) +$(OBJS_$(d)): TGT_CXXFLAGS := $(CXXFLAGS_$(d)) TGT_BIN += $(OBJS_$(d)) diff --git a/main.cpp.example b/main.cpp.example index 8fc522a..23d5540 100644 --- a/main.cpp.example +++ b/main.cpp.example @@ -27,16 +27,17 @@ void loop() { } // Force init to be called *first*, i.e. before static object allocation. -// Otherwise, statically allocated object that need libmaple may fail. -__attribute__(( constructor )) void premain() { +// Otherwise, statically allocated objects that need libmaple may fail. +__attribute__((constructor)) void premain() { init(); } int main(void) { setup(); - while (1) { + while (true) { loop(); } + return 0; } diff --git a/notes/coding_standard.rst b/notes/coding_standard.rst new file mode 100644 index 0000000..f761db7 --- /dev/null +++ b/notes/coding_standard.rst @@ -0,0 +1,427 @@ +libmaple Coding Standards +========================= + +Author: Marti Bolivar (mbolivar@leaflabs.com) + +LeafLabs team members are required to follow these when producing new +code. Community contributors to libmaple are strongly encouraged to +do so; following these rules will greatly increase the probability +that your patches will be folded in. + +In general, do it like this unless there's a really good reason why +not. You being lazy doesn't count as a good reason. Most, if not +all, of these decisions are entirely arbitrary, but it's important for +readability that we be consistent. + +The file ``.dir-locals.el`` in the libmaple root directory already +ensures that many of these standards are followed by default in Emacs +(but not on Windows, where it would need to be named +``_dir_locals.el``, and no way, man). There's also some elisp +scattered about this file which will provide you additional help. + +Vim customizations to do the same thing would be nice (hint, hint)! + +.. contents:: Contents + :local: + +License +------- + +.. highlight:: scheme + +- Put an MIT license at the beginning of the file (look at any of our + source files for an example). Copyright should go either to you or + to LeafLabs, LLC. + + Emacs: if you don't like seeing the license, you should use + elide-head (which will hide it for you). You can use the following:: + + (require 'elide-head) + (setq programming-mode-hooks '(c-mode-hook c++-mode-hook)) + (add-to-list 'elide-head-headers-to-hide + '("The MIT License" . "DEALINGS IN\n [*] THE SOFTWARE")) + (add-to-list 'elide-head-headers-to-hide + '("The MIT License" . "DEALINGS IN THE\n...SOFTWARE")) + (dolist (hook programming-mode-hooks) + (add-hook hook (lambda () (elide-head)))) + +Whitespace/Indentation +---------------------- + +- 4 space indents. [Set in ``.dir-locals.el``] + +- Unix newlines. [Some exceptions are currently grandfathered in; + these will go away in time.] + +- No tab characters. [Set in ``.dir-locals.el``] + +- No trailing whitespace. For help getting this (and no tab + characters) done automatically in Emacs, you can use this: + + http://github.com/mbolivar/code-fascism + + I hear tell you can get something similar in Vim; ask around, I + guess. + +- Files end in exactly one newline. [The presence of a newline at EOF + is already done by ``c-require-final-newline`` in recent versions of + Emacs.] + +- Exactly two newlines separate source paragraphs (you do separate + your code into paragraphs, don't you?). + +- The first line in a function is non-blank. + +.. highlight:: cpp + +- Exactly one space after ``if``, ``else``, ``for``, and ``while``, + before the following ``{`` or ``(``. One space before ``else``, + after the preceding ``}``. For example:: + + // This is good; we like this: + if (foo) { + while (quux) { + bar(); + } + } else { + baz(); + } + + // THIS IS BAD! DON'T DO THIS: + if(foo){ + while(quux){ + bar(); + } + }else{ + baz(); + } + +- Exactly one space in between binary arithmetic, logical, and + comparison operators and their operands. This doesn't apply to the + . and -> operators. Examples:: + + // This is good: + int x = a + b * (c - d); + if (x != 0 && a > 7) { + SerialUSB.println(x); + } + + // THIS IS BAD! + int x = a+b*(c-d); + if (x!=0 && a>7) { + SerialUSB.println(x); + } + + // This is good: + uint32 adc_data = ADC1_BASE->DR; + SerialUSB.println(adc_data); + + // THIS IS BAD! + uint32 adc_data = ADC1_BASE -> DR; + SerialUSB . println(adc_data); + +- No space between a unary operator and its operand. Examples:: + + // Good: + x++; + + // BAD! + x ++; + + // Good: + y = -x; + + // BAD! + y = - x; + +- If you need to break up a long line: + + * Prefer to break up long expressions after a binary operator. Example:: + + // Good: + if (some_really_long_conditional_wow_this_really_goes_on_forever || + maybe_something_else_could_happen_too) { + ... + } + + // BAD! + if (some_really_long_conditional_wow_this_really_goes_on_forever + || maybe_something_else_could_happen_too) { + ... + } + + * When breaking up a function's arguments over multiple lines, align + the arguments on subsequent lines with the first argument. + Example:: + + // Good: + return_type value_i_got = function_with_a_really_long_name(argument1, + argument2, + argument3); + + // BAD! + return_type value_i_got = function_with_a_really_long_name(argument1, + argument2, + argument3); + + // BAD! + return_type value_i_got = function_with_a_really_long_name(argument1, + argument2, + argument3); + +- In function invocations, no space in between the function name and + the opening parenthesis. Example:: + + // Good: + SerialUSB.println("Hello, world!"); + + // BAD! + SerialUSB.println ("Hello, world!"); + +- Don't indent C code within a conditionally-compiled ``extern "C"`` + block. Example:: + + // Good: + #ifdef __cplusplus + extern "C"{ + #endif + + void some_c_function(void); + + #ifdef __cplusplus + } // extern "C" + #endif + + // BAD! + #ifdef __cplusplus + extern "C"{ + #endif + + void some_c_function(void); + + #ifdef __cplusplus + } // extern "C" + #endif + +.. highlight:: scheme + + Emacs does the "bad" behavior by default, which can be very + annoying. You can turn this off with:: + + (defun c-mode-inextern-lang-hook () + (setcdr (assq 'inextern-lang c-offsets-alist) '-)) + (add-hook 'c-mode-hook c-mode-inextern-lang-hook) + +Comments +-------- + +.. highlight:: c++ + +- Multi-line comments are pretty flexible. Any of these is fine:: + + /* Comment starts here. + * Continued lines have a '*' before them. + * The comment can end after the last line. + */ + + /* Comment starts here. + * The comment can end on the same line. */ + + /* + * You can also place a newline after the opening "/*". + */ + +- Doxygen comments are multi-line comments that begin with ``/**`` + instead. + +- Single-line comments on the same line are ``//`` in C++. (That's OK + in C as well). + +- Single-line comments on their own source line should be ``/* */`` in + C, but can also be ``//`` in C++. (This isn't of great importance). + In Emacs, you can use M-; (comment-dwim), and it'll Do What You + Mean. + +Braces +------ + +- Mostly 1TBS: + + http://en.wikipedia.org/wiki/Indent_style#Variant:_1TBS + + The only difference is that the opening brace of a function's + definition occurs exactly one space character after the closing + parenthesis in that function's parameter list. Example:: + + void func(void) { + ... + } + +Naming conventions +------------------ + +There's always a fight about upper and lower case vs. underscores. +We'll handle this as follows. + +- First, ``Dont_Mix_Like_This``, because ``It_Looks_Really_Ugly``, ok? + [There's been some debate about this, and some exceptions are + already grandfathered in, so in order to settle it, let's call this + a "recommendation" instead of "requirement".] + +- Variables: Use underscores to separate words in C identifiers:: + + int some_example_name; + + User-facing C++ variables should be camel cased + (``thisIsAnExample``, ``boardPWMPins``, etc.), for consistency with + the Arduino style. It's probably a good idea for you to case + non-user facing C++ variables in the C style; this will help + disambiguate what's part of the Wirish API and what's not. + +- Classes: Pascal case. So ``ThisIsAClassName``, but ``thisIsNot``, + ``this_is_not``, and like I said, + ``Dont_You_DareTryANYTHING_STUPID``. + +- Functions: C functions are all lowercase, and words are separated by + underscores. C++ method names are camel cased. + +- Structs: Usually like variables (``adc_dev``, ``adc_reg_map``, + etc.), but it's not crucial. Don't feel obliged to put ``_t`` at + the end of the type name; we don't. + +- Macros and constants: all caps, separated by underscores. C++ + variables with the ``const`` qualifier generally aren't considered + "constants" for the purposes of this rule; i.e., they are cased + according to the rules for variables. We make an exception for + ``PIN_MAP``, because it's the central Wirish data structure. + +- foo.h gets ``#ifdef``\ 'ed to ``_FOO_H_``. + +- Acronyms: The case of letters in an acronym is determined by the + case of the first letter in the acronym, which is determined by + following the above rules. Examples:: + + // Good: + void usb_func() { ... } + void frob_usb_disc() { ... } + class SomethingUSB { + void usbInit(); + void initUSB(); + }; + + // BAD: + class BadUsb { ... }; // say "GoodUSB" instead + void swizzle_USB_disc() { ... } // say "swizzle_usb_disc" instead + +Documentation +------------- + +- You **must** document your code. At a bare minimum, this means + Doxygen comments on every user-facing function and type. + Additionally, you need to individually document the fields and + enumerator values of ``struct``\ s and ``enum``\ s. See any + register map type's definition for an example. + +- For libmaple proper, you don't need comments for each register bit + definition (for now). + +- Doxygen comments generally just belong on types, functions, + etc. that are part of the public user-facing API. This generally + means that if there's ReST documentation for it under docs/source/, + it needs Doxygen comments, and that ReST should use Breathe to pull + that Doxygen comment out. (For more info on this, see docs/README). + + There are some exceptions to this rule since Breathe isn't totally + mature yet and Sphinx's C++ domain is still in flux. In these + cases, document the code "manually" in ReST. + + This should be avoided if at all possible, since it creates a + maintenance burden of documenting things in two places at once, and + makes it easier for documentation to go stale. + + If you do have to document something manually, put a comment in the + source file informing future maintainers about it, so they'll pay + extra attention when making changes. + +- When adding peripheral support, it would be nice if you put + longer-form comments into the /notes/ directory, with a comment in + the corresponding .h file referring to it. See /libmaple/dac.h for + an example. + + This lets us keep the source files relatively free of "introductory" + material, while allowing new readers a convenient starting point. + These longer-form notes also have a habit of turning into + user-facing documentation. + +- For libmaple proper (the pure C library under libmaple/); the + convention is to document any user-facing function at the point where + it is defined. In particular, this means you should document an + externally-linked function defined in a .c file in that .c file, not + in the header file where it is declared to the user. + +General Formatting +------------------ + +.. highlight:: scheme + +- Keep it 80-column clean. That means Emacs says the largest column + number=79. You should turn on column number mode to help you out: + + (column-number-mode 1) + + You can get more help from lineker-mode: + + http://www.helsinki.fi/~sjpaavol/programs/lineker.el + + Just put lineker.el somewhere in your load-path, and: + + (require 'lineker) + (dolist (hook '(c-mode-hook c++-mode-hook)) + (add-hook hook (lambda () (lineker-mode 1)))) + +.. highlight:: cpp + +Language Features and Compiler Extensions +----------------------------------------- + +- In libmaple proper, aim for C99 compatibility. Some GCC extensions + are OK, but let's not go crazy. + +- If you'd like to get code into libmaple which uses a GCC extension + not already in use elsewhere, ask a LeafLabs developer (or another + one, if you are one) what they think about it first. + +- Explicitly approved GCC extensions: + + * ``asm volatile``: + http://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html + + * ``Nested functions``: + http://gcc.gnu.org/onlinedocs/gcc/Nested-Functions.html + +- In wirish, generally be very conservative when using C++ features + that aren't part of C. We are forced to use C++ for Arduino + compatibility (and the general Arduino style of pretending that an + object is a library), but it's an angry beast, and we don't want to + provoke it. The mantra is "C with classes". + +- Explicitly approved C++ features: + + * Initializers that aren't constant; e.g. the ``gpio_dev*`` values + in a ``PIN_MAP``. + + * Default arguments: e.g., the timeout argument defaulting to 0 + (meaning to wait forever) in ``waitForButtonPress()``. + +- Explicitly forbidden C++ features: + + * Templates + +- C++ features that are conditionally allowed, but require explicit + approval from at least two libmaple developers (one of which may be + yourself): + + * Operator overloading: Never allowed when it's just for style. + Potentially allowed when you're implementing a class that models a + mathematical structure, and you'd like to implement + e.g. ``operator+()``. diff --git a/notes/coding_standard.txt b/notes/coding_standard.txt deleted file mode 100644 index 5cb96c3..0000000 --- a/notes/coding_standard.txt +++ /dev/null @@ -1,223 +0,0 @@ -libmaple Coding Standards -========================= - -Author: Marti Bolivar (mbolivar@leaflabs.com) - -LeafLabs team members are required to follow these when producing new -code. Community contributors to libmaple are strongly encouraged to -do so; following these rules will greatly increase the probability -that your patches will be folded in. - -In general, do it like this unless there's a really good reason why -not. You being lazy doesn't count as a good reason. Most, if not -all, of these decisions are entirely arbitrary, but it's important for -readability that we be consistent. - -The file .dir-locals.el in the libmaple root directory already ensures -that many of these standards are followed by default in Emacs (but not -on Windows, where it would need to be named _dir_locals.el, and no -way, man). There's also some elisp scattered about this file which -will provide you additional help. - -Vim customizations to do the same thing would be nice (hint, hint)! - -License -------- - -- Put an MIT license at the beginning of the file (look at any of our - source files for an example). Copyright should go to either you or - LeafLabs LLC. - - Emacs: if you don't like seeing the license, you should use - elide-head (which will hide it for you). Here is some elisp you can - modify to make this pleasant: - - (require 'elide-head) - (setq programming-mode-hooks '(c-mode-hook c++-mode-hook)) - (add-to-list 'elide-head-headers-to-hide - '("The MIT License" . "DEALINGS IN\n [*] THE SOFTWARE")) - (add-to-list 'elide-head-headers-to-hide - '("The MIT License" . "DEALINGS IN THE\n...SOFTWARE")) - (dolist (hook mbolivar-programming-mode-hooks) - (add-hook hook (lambda () (elide-head)))) - -Whitespace/Indentation ----------------------- - -- 4 space indents. [Set in .dir-locals.el] - -- Unix newlines. - -- No tab characters. [Set in .dir-locals.el] - -- No trailing whitespace. For help getting this (and no tab - characters) done automatically in Emacs, you can use this: - - http://github.com/mbolivar/code-fascism - - I hear tell you can get something similar in vim; ask around, I - guess. - -- Files end in exactly one newline. [The presence of a newline at EOF - is already done by `c-require-final-newline' in recent versions of - Emacs.] - -- Exactly two newlines separate source paragraphs (you do separate - your code into paragraphs, don't you?). - -- The first line in a function is non-blank. - -- Don't indent C code within a conditionally-compiled extern "C" - block. Emacs does this by default, which can be very annoying; you - can turn this behavior off with - - (defun c-mode-inextern-lang-hook () - (setcdr (assq 'inextern-lang c-offsets-alist) '-)) - - (add-hook 'c-mode-hook c-mode-inextern-lang-hook) - - -Comments --------- - -- Multi-line comments look like this: - - /* text starts here - * continued lines have a '*' before them - * the comment can end after the last line - */ - - or this: - - /* comment starts here - * the comment can end on the same line */ - -- Doxygen comments are newline comments that begin with /** instead. - It is not required that the "/**" appear on a line by itself. - -- Single-line comments on the same line are // in c or c++. - -- Single-line comments on their own source line are /* */ in c, but - can also be // in c++. If you think that typing out /* */ is too - slow in emacs, use M-; (comment-dwim) when you're on an empty line, - and it'll ... well... - - You should be using the (super awesome) comment-dwim; it pretty - much does exactly what you want to the comment on the current - line, including "create one and put it in the right place". - -Braces ------- - -- 1TBS. Nothing more need be said. - - http://en.wikipedia.org/wiki/Indent_style#Variant:_1TBS - -Naming conventions ------------------- - -- There's always a fight about upper and lower case vs. underscores. - We'll handle this as follows. - - First, Dammit_Dont_Mix_Like_This, because It_Looks_Really_Ugly, ok? - [There's been some debate about this, and some exceptions are - already grandfathered in, so in order to settle it, let's call this - a "recommendation" instead of "requirement".] - -- Variables: Use underscores to separate words in C identifiers: - - int some_example_name; - - It is strongly advised to do it this way in C++ too, but it's not - [yet] mandatory. - -- Classes: Pascal case. So ThisIsAClassName, but thisIsNot, - this_is_not, and like I said, Dont_You_DareTryANYTHING_STUPID. - -- Functions: C functions are all lowercase, and words are separated by - underscores. C++ method names are camel cased (thisIsAnExample). - -- Structs: pick a style from "Variables" or "Classes" depending on how - you mean it (since it might be either a simple record type, in which - case do like c variables, or you might be faking an object in c, in - which case do like classes). If it's in a typedef, don't feel - obliged to put "_t" at the end of the name; we don't. - -- Macros and constants: all caps, separated by underscores. Variables - with the "const" qualifier aren't considered "constants" for the - purposes of this rule; i.e., case them according to the rules for - variables. - -- foo.h gets #ifdef'ed to _FOO_H_. - -- Acronyms: The case of letters in an acronym is determined by the - case of the first letter in the acronym, which is determined by - following the above rules. Examples: - - void usb_func() { ... } - - class SomethingUSB { - void usbInit(); - void initUSB(); - }; - - Never do this: - - class BadUsb { ... }; // say "GoodUSB" instead - -Documentation -------------- - -- Document your code. This should go without saying. - -- For complicated peripherals, it would be nice if you put longer-form - comments into this directory (notes/), with a comment in the - corresponding .h file referring to it. See libmaple/dac.h for an - example. That lets us keep the source files relatively clean while - still allowing new readers to have a starting point. - -- At least put a doxygen comment with a nonempty @brief for every .h - file you add. See the existing ones for examples. For now, it'd be - better if you didn't put a @brief into any .c[pp] files, since it - (currently) interferes with our documentation generator in a way - that I won't explain here (though you can look into the LeafLabs or - michaeljones breathe repos on github and potentially figure out - why). - -- Doxygen comments generally just belong on types, functions, - etc. that are part of the public user-facing API. This generally - means that if there's ReST documentation for it under docs/source/, - it needs Doxygen comments, and that ReST should use Breathe to pull - that Doxygen comment out. (For more info on this, see docs/README). - - There are some exceptions to this rule since Breathe isn't totally - mature yet and Sphinx's C++ domain is still in flux. In these - cases, document the code "manually" in ReST. - - This should be avoided if at all possible, since it creates a - maintenance burden of documenting things in two places at once, and - provides an opportunity for bad documentation to slip in, when the - code comments fall out of sync with the ReST docs. - -General Formatting ------------------- - -- Keep it 80-column clean. That means Emacs says the largest column - number=79. If you haven't already, you should turn on column - numbers to help you out: - - (column-number-mode 1) - - You can get more help from lineker-mode. Download it here: - - http://www.helsinki.fi/~sjpaavol/programs/lineker.el - - Then put the file somewhere in your load-path, and: - - (require 'lineker) - (dolist (hook '(c-mode-hook c++-mode-hook)) - (add-hook hook (lambda () (lineker-mode 1)))) - - There are only a few exceptional situations. The most important one - is when specifying a lookup table like PIN_MAP where it'd be ugly to - split each entry over multiple lines. diff --git a/notes/dma.txt b/notes/dma.txt new file mode 100644 index 0000000..97b23a0 --- /dev/null +++ b/notes/dma.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..1ad49ee --- /dev/null +++ b/notes/exti.txt @@ -0,0 +1,67 @@ +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." + +---------------------------------------------------------------------------- +Pin to EXTI Line Mappings: +EXTI0 EXTI1 EXTI2 EXTI3 EXTI4 +-------------------------------------------------------------------------- +D2/PA0 D3/PA1 D1/PA2 D0/A6/PA3 D10/A10/PA4 +D26/EXT7/PB0 D27/EXT8/PB1 D16/A2/PC2 D17/A3/PC3 D18/A4/PC4 +D14/A0/PC0 D15/PC1 D25/EXT5/PD2 + +EXTI5 EXTI6 EXTI7 EXTI8 EXTI9 +---------------------------------------------------------------------------- +D13/A13/PA5 D12/A12/PA6 D11/A11/PA7 D6/PA8 D7/PA9 +D4/PB5 D5/PB6 D9/PB7 D38/PB8 D23/EXT4/PB9 +D19/A5/PC5 D34/EXTI15/PC6 D35/EXT16/PC7 D36/PC8 D37/EXT18/PC9 + +EXTI10 EXTI11 EXTI12 EXTI13 EXTI14 +---------------------------------------------------------------------------- +D8/PA10 D29/EXT10/PB11 D30/EXTI1/PB12 D31/EXTI12/PB13 D32/EXT13/PB14 +D28/PB10 D20/EXTI1/PC13 D21/EXT2/PC14 +D25/PC10 + +EXTI15 +---------------------------------------------------------------------------- +D33/EXTI14/PB15 +D22/EXT3/PC15 + + +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/native-pin-definitions.txt b/notes/pin-definitions.txt index b871f89..30f5056 100644 --- a/notes/native-pin-definitions.txt +++ b/notes/pin-definitions.txt @@ -1,21 +1,18 @@ -Maple Native (STM32F103ZE) pin definitions, by GPIO bank. +Pin definitions by GPIO bank. Source: ST DOC ID 14611, Datasheet for STM32F103xC, STM32F103xD, STM32F103xE, Table 5, pp. 30--35. -Some peripherals and extra functionality with less/no libmaple -relevance (at time of writing) are given in "Other" following each -bank's main table. Non-default alternate functions are not listed. If -wirish will/does remap the pin's main function after reset, the main -function is listed under "Other". +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 complete -and correct, but the final arbiter of truth is the ST datasheet. +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). --------------------------------------------------------------------------- -STM32 ADC Timer FSMC I2S I2C USART SPI DAC 5v? +GPIO ADC Timer FSMC I2S I2C USART SPI DAC 5v? --------------------------------------------------------------------------- PA0 123in0 2ch1etr - - - 2cts - - - 5ch1 @@ -52,7 +49,7 @@ PA14: JTCK-SWCLK (default) PA15: JTDI (default) ------------------------------------------------------------------------------- -STM32 ADC Timer FSMC I2S I2C USART SPI DAC 5v? SDIO +GPIO ADC Timer FSMC I2S I2C USART SPI DAC 5v? SDIO ------------------------------------------------------------------------------- PB0 12in8 3ch3 - - - - - - - - 8ch2n @@ -80,7 +77,7 @@ PB3: JTDO (default) PB4: NJTRST (default) ------------------------------------------------------------------------------- -STM32 ADC Timer FSMC I2S I2C UART SPI DAC 5v? SDIO +GPIO ADC Timer FSMC I2S I2C UART SPI DAC 5v? SDIO ------------------------------------------------------------------------------- PC0 123in10 - - - - - - - - - PC1 123in11 - - - - - - - - - @@ -106,7 +103,7 @@ PC14: OSC32_IN PC15: OSC32_OUT ------------------------------------------------------------------------------- -STM32 ADC Timer FSMC I2S I2C UART SPI DAC 5v? SDIO +GPIO ADC Timer FSMC I2S I2C UART SPI DAC 5v? SDIO ------------------------------------------------------------------------------- PD0 - - D2 - - - - - Y - PD1 - - D3 - - - - - Y - @@ -132,7 +129,7 @@ PD0: OSC_IN (default) PD1: OSC_OUT (default) --------------------------------------------------------------------------- -STM32 ADC Timer FSMC I2S I2C USART SPI DAC 5v? +GPIO ADC Timer FSMC I2S I2C USART SPI DAC 5v? --------------------------------------------------------------------------- PE0 - 4etr NBL0 - - - - - Y PE1 - - NBL1 - - - - - Y @@ -159,7 +156,7 @@ PE5: TRACED2 PE6: TRACED3 --------------------------------------------------------------------------- -STM32 ADC Timer FSMC I2S I2C USART SPI DAC 5v? +GPIO ADC Timer FSMC I2S I2C USART SPI DAC 5v? --------------------------------------------------------------------------- PF0 - - A0 - - - - - Y PF1 - - A1 - - - - - Y @@ -179,7 +176,7 @@ PF14 - - A8 - - - - - Y PF15 - - A9 - - - - - Y --------------------------------------------------------------------------- -STM32 ADC Timer FSMC I2S I2C USART SPI DAC 5v? +GPIO ADC Timer FSMC I2S I2C USART SPI DAC 5v? --------------------------------------------------------------------------- PG0 - - A10 - - - - - Y PG1 - - A11 - - - - - Y diff --git a/notes/portable.txt b/notes/portable.txt index 69952d7..cc1f2ac 100644 --- a/notes/portable.txt +++ b/notes/portable.txt @@ -1,98 +1,28 @@ +Board portability is implemented in boards.h, libmaple.h, and stm32.h. -Disclaimer text: // High-density devices only (Maple Native) +At compile time, we currently expect one of STM32_MEDIUM_DENSITY or +STM32_HIGH_DENSITY to be defined. There's no support for low-density +chips. XL-density isn't in the near horizon; patches welcome. You'll +also need to define some BOARD_foo if you expect to use Wirish; this +comes along with some additional assumptions about your board's layout. +The code in usb/ is not very portable at all right now; expect this to +change in the future, but for now, we're focusing on rolling out a +more complete backend. -Board portability is implemented by adding a header file to ./libmaple with the -name of the BOARD target, and then editing libmaple.h to add this file as an -option. - -A pin maple file should be added to ./notes describing the pin numbering - -Files to check by hand: -# adc.c -# adc.h -# exc.c -# exti.c -# exti.h -# flash.c -# flash.h -# gpio.c -# gpio.h -# libmaple_types.h -# nvic.c -# nvic.h -# rcc.c -# rcc.h -# ring_buffer.h -# rules.mk -# spi.c -- spi.h -# syscalls.c -# systick.c -# systick.h -# timers.c -# timers.h -# usart.c -# usart.h -# util.c -# util.h -# libmaple.h -# usb/* - -wirish/: -# bits.h -# boards.h -# cxxabi-compat.cpp -# ext_interrupts.c -# ext_interrupts.h -# HardwareTimer.cpp -# HardwareTimer.h -# io.h -# main.cxx -# Print.cpp -# Print.h -# pwm.c -# pwm.h -# rules.mk -# time.c -# time.h -# usb_serial.cpp -# usb_serial.h -# wirish_analog.c -# wirish.c -# wirish_digital.c -# wirish.h -# wirish_math.cpp -# wirish_math.h -# wirish_shift.c -# WProgram.h -- comm/ - - - -ADC Notes: - only using ADC1? - untested - -EXTI Notes: - need to update huge table in comments? - untested +A file should be added to ./notes describing the pin numbering of any +new board you add. NVIC Notes: - I don't think NVIC_ISER3 and NVIC_ICER3 actually exist? Only CANBUS and USB OTG use interrupts above #63, but I updated the nvic code anyways RCC Notes: Added some clock stuff to all boards even though they aren't usable... blah. SPI Notes: - SPI3 is only in XL chips so didn't really handle that + SPI3 is only in XL chips, so we don't handle that. TIMER Notes: - High-density devices add an advanced timer (TIMER8) and another normal one (TIMER5). - TIMER6 and TIMER7 are much less useful. - There is some partial progress towards adding timer5/timer8 functionality, - but not much. This should probably all be rewritten. The wirish timer implementation should be refactored to use pin numbers. USART Notes: diff --git a/notes/timers.txt b/notes/timers.txt index 3f5b9f4..647e92e 100644 --- a/notes/timers.txt +++ b/notes/timers.txt @@ -1,82 +1,87 @@ +Timers +====== -Each timer (1-4) has 4 capture/compare channels (1-4). These are directly used -by PWM but have a ton of other possible functionality. The STM32 implementation -is particularly complicated with, eg, the ability to chain together timers - -Timer1 is an "advanced timer" with many more features. I think if we use just -the "Capture and compare interrupt", and enable MOE during initialization -everything will be ok. There are seperate Break, Update, and Trigger interrupts -as well that we will ignore for now. - -Timer2,Ch 3+4 are D0 and D1, which conflict with Serial2. USART should work -fine as long as pins aren't in output mode? and timers should work fine if -Serial2 isn't in use? - -Caveats ------------------------------------------------------------------------------- -There are probably subtle bugs with the way register settings get read in; eg, -it is often required to have the counter fully overflow before new settings -come into effect? -Getting really good timing is still an art, not a science here... usually you -need to fiddle with an oscilloscope and the exact overflow/compare numbers to -get just the right time values. -Any other interrupts (SysTick, USB, Serial, etc) can blow away all the nice -timing stuff. - -Misc Notes ------------------------------------------------------------------------------- -Implementation with case/switch in the interrupt handlers doesn't work; a lot -of the time multiple interrupt flags are active at the same time (or become -active?) +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 carefully (eg, determine clock-wise and overflow-wise behavior for - each function) -- track down and handle all pin conflicts -- implement the update interrupt as a "5th channel" -- "pulse in" stuff, both c and c++ -- function to read out CCR registers -- allow comparison output to the pin (a la PWM) -- additional modes and configuration (up, down, up/down, etc) - -Possible Wirish implementation ------------------------------------------------------------------------------- -Inspired by Timer1 Library for arduino -http://arduino.cc/pipermail/developers_arduino.cc/2010-June/002845.html +---- + +- Document more carefully (e.g., determine clock-wise and + overflow-wise behavior for each function). - class HardwareTimer { - - public: - - void pause(); - void resume(); - void setPrescaleFactor(uint8 factor); - void setOverflow(uint16 val); // truncates to overflow - void setCount(uint16 val); // truncates to overflow - uint16 getCount(); - uint16 setPeriod(uint32 microseconds); // tries to set prescaler and overflow wisely; returns overflow - void setMode(uint8 mode); - void setCompare1(uint16 val); // truncates to overflow - void setCompare2(uint16 val); // truncates to overflow - void setCompare3(uint16 val); // truncates to overflow - void setCompare4(uint16 val); // truncates to overflow - void attachCompare1Interrupt(void (*f)(void)); - void attachCompare2Interrupt(void (*f)(void)); - void attachCompare3Interrupt(void (*f)(void)); - void attachCompare4Interrupt(void (*f)(void)); - void detachCompare1Interrupt(); - void detachCompare2Interrupt(); - void detachCompare3Interrupt(); - void detachCompare4Interrupt(); - }; - - HardwareTimer Timer1 = HardwareTimer(1); - HardwareTimer Timer2 = HardwareTimer(2); - HardwareTimer Timer3 = HardwareTimer(3); - HardwareTimer Timer4 = HardwareTimer(4); +- 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); @@ -89,4 +94,3 @@ http://www.arduino.cc/playground/Code/Timer1 void disablePwm(char pin); void attachInterrupt(void (*isr)(), long microseconds=-1); void detachInterrupt(); - diff --git a/notes/usb.txt b/notes/usb.txt index 5e00354..9552b9f 100644 --- a/notes/usb.txt +++ b/notes/usb.txt @@ -1,3 +1,7 @@ +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] diff --git a/notes/vga.txt b/notes/vga.txt index d75281a..43b6830 100644 --- a/notes/vga.txt +++ b/notes/vga.txt @@ -7,8 +7,7 @@ gpio_write_bit() is about 360ns (2.78MHz) Writing to GPIO?_BASE is about 60ns (16.6MHz -> 18MHz) -pwm write 0x0001 is about 30ns (33MHz) with prescaler as 1 (default) -pwm write 0x0001 is about 14ns (72MHz) with prescaler as 0 (!) +PWM write 0x0001 is about 14ns (72MHz) with prescaler as 0 (!) VGA Timing ------------------------------------------------------------------------------ @@ -34,9 +33,3 @@ Crude 640x480 directions: 11 lines front porch 2 lines Vsync (low) 31 lines back porch - -Currently, setting vs. clearing GPIO registers seems to take a different amount -of time? Or perhaps i'm not analyzing branching correctly. Regardless, if you -SET 100x times then UNSET on one line, then UNSET 100x then SET the next line, -the two changes in color will generally not line up. - diff --git a/support/codeblocks/libmaple.cbp b/support/codeblocks/libmaple.cbp deleted file mode 100644 index 4766275..0000000 --- a/support/codeblocks/libmaple.cbp +++ /dev/null @@ -1,300 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" standalone="yes" ?> -<CodeBlocks_project_file> - <FileVersion major="1" minor="6" /> - <Project> - <Option title="libmaple" /> - <Option makefile_is_custom="1" /> - <Option pch_mode="2" /> - <Option compiler="gcc" /> - <Build> - <Target title="flash"> - <Option output="support/codeblocks/program_flash.sh" prefix_auto="0" extension_auto="0" /> - <Option working_dir="support/codeblocks" /> - <Option object_output="/home/bnewbold/leaf/libmaple/.objs" /> - <Option type="1" /> - <Option compiler="gcc" /> - <Option use_console_runner="0" /> - <MakeCommands> - <Build command="$make -f $makefile MAPLE_TARGET=$target" /> - <CompileFile command="$make -f $makefile MAPLE_TARGET=$target $file" /> - <Clean command="$make -f $makefile clean" /> - <DistClean command="$make -f $makefile distclean$target" /> - </MakeCommands> - </Target> - <Target title="ram"> - <Option output="support/codeblocks/program_ram.sh" prefix_auto="0" extension_auto="0" /> - <Option working_dir="support/codeblocks" /> - <Option type="1" /> - <Option compiler="gcc" /> - <Option use_console_runner="0" /> - <MakeCommands> - <Build command="$make -f $makefile MAPLE_TARGET=$target" /> - <CompileFile command="$make -f $makefile MAPLE_TARGET=$target $file" /> - <Clean command="$make -f $makefile clean" /> - <DistClean command="$make -f $makefile distclean" /> - </MakeCommands> - </Target> - </Build> - <Compiler> - <Add option="-Wall" /> - </Compiler> - <Unit filename="libmaple/adc.c"> - <Option compilerVar="CC" /> - <Option target="<{~None~}>" /> - </Unit> - <Unit filename="libmaple/adc.h"> - <Option target="<{~None~}>" /> - </Unit> - <Unit filename="libmaple/exc.c"> - <Option compilerVar="CC" /> - <Option target="<{~None~}>" /> - </Unit> - <Unit filename="libmaple/exti.c"> - <Option compilerVar="CC" /> - <Option target="<{~None~}>" /> - </Unit> - <Unit filename="libmaple/exti.h"> - <Option target="<{~None~}>" /> - </Unit> - <Unit filename="libmaple/flash.c"> - <Option compilerVar="CC" /> - <Option target="<{~None~}>" /> - </Unit> - <Unit filename="libmaple/flash.h"> - <Option target="<{~None~}>" /> - </Unit> - <Unit filename="libmaple/gpio.c"> - <Option compilerVar="CC" /> - <Option target="<{~None~}>" /> - </Unit> - <Unit filename="libmaple/gpio.h"> - <Option target="<{~None~}>" /> - </Unit> - <Unit filename="libmaple/libmaple.h"> - <Option target="<{~None~}>" /> - </Unit> - <Unit filename="libmaple/libmaple_types.h"> - <Option target="<{~None~}>" /> - </Unit> - <Unit filename="libmaple/nvic.c"> - <Option compilerVar="CC" /> - <Option target="<{~None~}>" /> - </Unit> - <Unit filename="libmaple/nvic.h"> - <Option target="<{~None~}>" /> - </Unit> - <Unit filename="libmaple/rcc.c"> - <Option compilerVar="CC" /> - <Option target="<{~None~}>" /> - </Unit> - <Unit filename="libmaple/rcc.h"> - <Option target="<{~None~}>" /> - </Unit> - <Unit filename="libmaple/spi.c"> - <Option compilerVar="CC" /> - <Option target="<{~None~}>" /> - </Unit> - <Unit filename="libmaple/spi.h"> - <Option target="<{~None~}>" /> - </Unit> - <Unit filename="libmaple/syscalls.c"> - <Option compilerVar="CC" /> - <Option target="<{~None~}>" /> - </Unit> - <Unit filename="libmaple/systick.c"> - <Option compilerVar="CC" /> - <Option target="<{~None~}>" /> - </Unit> - <Unit filename="libmaple/systick.h"> - <Option target="<{~None~}>" /> - </Unit> - <Unit filename="libmaple/timers.c"> - <Option compilerVar="CC" /> - <Option target="<{~None~}>" /> - </Unit> - <Unit filename="libmaple/timers.h"> - <Option target="<{~None~}>" /> - </Unit> - <Unit filename="libmaple/usart.c"> - <Option compilerVar="CC" /> - <Option target="<{~None~}>" /> - </Unit> - <Unit filename="libmaple/usart.h"> - <Option target="<{~None~}>" /> - </Unit> - <Unit filename="libmaple/usb/descriptors.c"> - <Option compilerVar="CC" /> - <Option target="<{~None~}>" /> - </Unit> - <Unit filename="libmaple/usb/descriptors.h"> - <Option target="<{~None~}>" /> - </Unit> - <Unit filename="libmaple/usb/usb.c"> - <Option compilerVar="CC" /> - <Option target="<{~None~}>" /> - </Unit> - <Unit filename="libmaple/usb/usb.h"> - <Option target="<{~None~}>" /> - </Unit> - <Unit filename="libmaple/usb/usb_callbacks.c"> - <Option compilerVar="CC" /> - <Option target="<{~None~}>" /> - </Unit> - <Unit filename="libmaple/usb/usb_callbacks.h"> - <Option target="<{~None~}>" /> - </Unit> - <Unit filename="libmaple/usb/usb_config.h"> - <Option target="<{~None~}>" /> - </Unit> - <Unit filename="libmaple/usb/usb_hardware.c"> - <Option compilerVar="CC" /> - <Option target="<{~None~}>" /> - </Unit> - <Unit filename="libmaple/usb/usb_hardware.h"> - <Option target="<{~None~}>" /> - </Unit> - <Unit filename="libmaple/usb/usb_lib/usb_core.c"> - <Option compilerVar="CC" /> - <Option target="<{~None~}>" /> - </Unit> - <Unit filename="libmaple/usb/usb_lib/usb_core.h"> - <Option target="<{~None~}>" /> - </Unit> - <Unit filename="libmaple/usb/usb_lib/usb_def.h"> - <Option target="<{~None~}>" /> - </Unit> - <Unit filename="libmaple/usb/usb_lib/usb_init.c"> - <Option compilerVar="CC" /> - <Option target="<{~None~}>" /> - </Unit> - <Unit filename="libmaple/usb/usb_lib/usb_init.h"> - <Option target="<{~None~}>" /> - </Unit> - <Unit filename="libmaple/usb/usb_lib/usb_int.c"> - <Option compilerVar="CC" /> - <Option target="<{~None~}>" /> - </Unit> - <Unit filename="libmaple/usb/usb_lib/usb_int.h"> - <Option target="<{~None~}>" /> - </Unit> - <Unit filename="libmaple/usb/usb_lib/usb_lib.h"> - <Option target="<{~None~}>" /> - </Unit> - <Unit filename="libmaple/usb/usb_lib/usb_mem.c"> - <Option compilerVar="CC" /> - <Option target="<{~None~}>" /> - </Unit> - <Unit filename="libmaple/usb/usb_lib/usb_mem.h"> - <Option target="<{~None~}>" /> - </Unit> - <Unit filename="libmaple/usb/usb_lib/usb_regs.c"> - <Option compilerVar="CC" /> - <Option target="<{~None~}>" /> - </Unit> - <Unit filename="libmaple/usb/usb_lib/usb_regs.h"> - <Option target="<{~None~}>" /> - </Unit> - <Unit filename="libmaple/usb/usb_lib/usb_type.h"> - <Option target="<{~None~}>" /> - </Unit> - <Unit filename="libmaple/util.c"> - <Option compilerVar="CC" /> - <Option target="<{~None~}>" /> - </Unit> - <Unit filename="libmaple/util.h"> - <Option target="<{~None~}>" /> - </Unit> - <Unit filename="main.cpp"> - <Option target="<{~None~}>" /> - </Unit> - <Unit filename="wirish/Print.cpp"> - <Option target="<{~None~}>" /> - </Unit> - <Unit filename="wirish/Print.h"> - <Option target="<{~None~}>" /> - </Unit> - <Unit filename="wirish/WProgram.h"> - <Option target="<{~None~}>" /> - </Unit> - <Unit filename="wirish/bits.h"> - <Option target="<{~None~}>" /> - </Unit> - <Unit filename="wirish/comm/HardwareSPI.cpp"> - <Option target="<{~None~}>" /> - </Unit> - <Unit filename="wirish/comm/HardwareSPI.h"> - <Option target="<{~None~}>" /> - </Unit> - <Unit filename="wirish/comm/HardwareSerial.cpp"> - <Option target="<{~None~}>" /> - </Unit> - <Unit filename="wirish/comm/HardwareSerial.h"> - <Option target="<{~None~}>" /> - </Unit> - <Unit filename="wirish/cxxabi-compat.cpp"> - <Option target="<{~None~}>" /> - </Unit> - <Unit filename="wirish/ext_interrupts.c"> - <Option compilerVar="CC" /> - <Option target="<{~None~}>" /> - </Unit> - <Unit filename="wirish/ext_interrupts.h"> - <Option target="<{~None~}>" /> - </Unit> - <Unit filename="wirish/io.h"> - <Option target="<{~None~}>" /> - </Unit> - <Unit filename="wirish/main.cxx"> - <Option target="<{~None~}>" /> - </Unit> - <Unit filename="wirish/pwm.c"> - <Option compilerVar="CC" /> - <Option target="<{~None~}>" /> - </Unit> - <Unit filename="wirish/pwm.h"> - <Option target="<{~None~}>" /> - </Unit> - <Unit filename="wirish/time.c"> - <Option compilerVar="CC" /> - <Option target="<{~None~}>" /> - </Unit> - <Unit filename="wirish/time.h"> - <Option target="<{~None~}>" /> - </Unit> - <Unit filename="wirish/usb_serial.cpp"> - <Option target="<{~None~}>" /> - </Unit> - <Unit filename="wirish/usb_serial.h"> - <Option target="<{~None~}>" /> - </Unit> - <Unit filename="wirish/wirish.c"> - <Option compilerVar="CC" /> - <Option target="<{~None~}>" /> - </Unit> - <Unit filename="wirish/wirish.h"> - <Option target="<{~None~}>" /> - </Unit> - <Unit filename="wirish/wirish_analog.c"> - <Option compilerVar="CC" /> - <Option target="<{~None~}>" /> - </Unit> - <Unit filename="wirish/wirish_digital.c"> - <Option compilerVar="CC" /> - <Option target="<{~None~}>" /> - </Unit> - <Unit filename="wirish/wirish_math.cpp"> - <Option target="<{~None~}>" /> - </Unit> - <Unit filename="wirish/wirish_math.h"> - <Option target="<{~None~}>" /> - </Unit> - <Unit filename="wirish/wirish_shift.c"> - <Option compilerVar="CC" /> - <Option target="<{~None~}>" /> - </Unit> - <Extensions> - <code_completion /> - <debugger /> - </Extensions> - </Project> -</CodeBlocks_project_file> diff --git a/support/codeblocks/program_flash.sh b/support/codeblocks/program_flash.sh deleted file mode 100755 index 9dfe9f1..0000000 --- a/support/codeblocks/program_flash.sh +++ /dev/null @@ -1,4 +0,0 @@ -#/usr/bin/env sh - -cd ../.. -make MAPLE_TARGET=flash install diff --git a/support/codeblocks/program_ram.sh b/support/codeblocks/program_ram.sh deleted file mode 100755 index 55fe73d..0000000 --- a/support/codeblocks/program_ram.sh +++ /dev/null @@ -1,4 +0,0 @@ -#/usr/bin/env sh - -cd ../.. -make MAPLE_TARGET=ram install diff --git a/support/gdb/gpio/gpio.gdb b/support/gdb/gpio/gpio.gdb new file mode 100644 index 0000000..4376cfd --- /dev/null +++ b/support/gdb/gpio/gpio.gdb @@ -0,0 +1,12 @@ +set print pretty on + +print "GPIOA registers:" +p/x *GPIOA->regs +print "GPIOB registers:" +p/x *GPIOB->regs +print "GPIOC registers:" +p/x *GPIOC->regs +print "GPIOD registers:" +p/x *GPIOD->regs +print "AFIO registers:" +p/x *(struct afio_reg_map*)0x40010000 diff --git a/support/gdb/i2c/test.gdb b/support/gdb/i2c/test.gdb new file mode 100644 index 0000000..8b71320 --- /dev/null +++ b/support/gdb/i2c/test.gdb @@ -0,0 +1,112 @@ +define i2c_sr1_flags +set $s = $arg0 +printf "SR1: " + +if (($s & (1 << 15))) + printf "SMBALERT " +end + +if (($s & (1 << 14))) + printf "TIMEOUT " +end + +if (($s & (1 << 12))) + printf "PECERR " +end + +if (($s & (1 << 11))) + printf "OVR " +end + +if (($s & (1 << 10))) + printf "AF " +end + +if (($s & (1 << 9))) + printf "ARLO " +end + +if (($s & (1 << 8))) + printf "BERR " +end + +if (($s & (1 << 7))) + printf "TXE " +end + +if (($s & (1 << 6))) + printf "RXNE " +end + +if (($s & (1 << 4))) + printf "STOPF " +end + +if (($s & (1 << 3))) + printf "ADD10 " +end + +if (($s & (1 << 2))) + printf "BTF " +end + +if (($s & (1 << 1))) + printf "ADDR " +end + +if (($s & (1 << 0))) + printf "SB " +end +end + +define i2c_sr2_flags +set $s = $arg0 +printf "SR2: " + +if (($s & (1 << 7))) + printf "DUALF " +end + +if (($s & (1 << 6))) + printf "SMBHOST " +end + +if (($s & (1 << 5))) + printf "SMBDEFAULT " +end + +if (($s & (1 << 4))) + printf "GENCALL " +end + + +if (($s & (1 << 2))) + printf "TRA " +end + +if (($s & (1 << 1))) + printf "BUSY " +end + +if (($s & (1 << 0))) + printf "MSL " +end + +end + +define pbc +set $c = crumbs +while ($c->event) + if ($c->event != 0) + printf "Event: %d ", $c->event + if ($c->event == 1) + i2c_sr1_flags $c->sr1 + printf "\t" + i2c_sr2_flags $c->sr2 + end + printf "\n" + end + set $c = $c + 1 +end + + diff --git a/support/ld/common_ram.inc b/support/ld/common_ram.inc new file mode 100644 index 0000000..be83e84 --- /dev/null +++ b/support/ld/common_ram.inc @@ -0,0 +1,221 @@ +/* Linker script for STM32 (by Lanchon with Mods by LeafLabs) */ + +OUTPUT_FORMAT ("elf32-littlearm", "elf32-bigarm", "elf32-littlearm") +SEARCH_DIR(.) +/* + * Link against libgcc, libc, and libm + */ +GROUP(libgcc.a libc.a libm.a) + +/* These force the linker to search for particular symbols from + * the start of the link process and thus ensure the user's + * overrides are picked up + */ +INCLUDE names.inc + +/* STM32 vector table. See stm32_vector_table.S */ +EXTERN(__cs3_stm32_vector_table) + +/* libcs3 C start function. See cs3.h */ +EXTERN(__cs3_start_c) + +/* main entry point */ +EXTERN(main) + +/* Initial stack pointer value. */ +EXTERN(__cs3_stack) +PROVIDE(__cs3_stack = __cs3_region_start_ram + __cs3_region_size_ram); + +/* Reset vector and chip reset entry point. See start.S */ +EXTERN(_start) +ENTRY(_start) +PROVIDE(__cs3_reset = _start); + +/* Beginning of the heap */ +PROVIDE(__cs3_heap_start = _end); + +/* End of the heap */ +PROVIDE(__cs3_heap_end = __cs3_region_start_ram + LENGTH(ram)); + + +SECTIONS +{ + .text : + { + CREATE_OBJECT_SYMBOLS + __cs3_region_start_ram = .; + *(.cs3.region-head.ram) + + /* + * STM32 vector table + */ + __cs3_interrupt_vector = __cs3_stm32_vector_table; + *(.cs3.interrupt_vector) + /* Make sure we pulled in an interrupt vector. */ + ASSERT (. != __cs3_stm32_vector_table, "No interrupt vector"); + + /* + * Program code and vague linking + */ + *(.text .text.* .gnu.linkonce.t.*) + *(.plt) + *(.gnu.warning) + *(.glue_7t) *(.glue_7) *(.vfp11_veneer) + + *(.rodata .rodata.* .gnu.linkonce.r.*) + + *(.ARM.extab* .gnu.linkonce.armextab.*) + *(.gcc_except_table) + *(.eh_frame_hdr) + *(.eh_frame) + + . = ALIGN(4); + KEEP(*(.init)) + + . = ALIGN(4); + __preinit_array_start = .; + KEEP (*(.preinit_array)) + __preinit_array_end = .; + + . = ALIGN(4); + __init_array_start = .; + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array)) + __init_array_end = .; + + . = ALIGN(0x4); + KEEP (*crtbegin.o(.ctors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*crtend.o(.ctors)) + + . = ALIGN(4); + KEEP(*(.fini)) + + . = ALIGN(4); + __fini_array_start = .; + KEEP (*(.fini_array)) + KEEP (*(SORT(.fini_array.*))) + __fini_array_end = .; + + KEEP (*crtbegin.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*crtend.o(.dtors)) + + . = ALIGN(4); + __cs3_regions = .; + LONG (0) + LONG (__cs3_region_init_ram) + LONG (__cs3_region_start_ram) + LONG (__cs3_region_init_size_ram) + LONG (__cs3_region_zero_size_ram) + } > ram + + /* + * .ARM.exidx exception unwinding + */ + __exidx_start = .; + .ARM.exidx : + { + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + } > ram + __exidx_end = .; + + /* + * End of text + */ + .text.align : + { + . = ALIGN(8); + _etext = .; + } > ram + + .cs3.rom : + { + __cs3_region_start_rom = .; + *(.cs3.region-head.rom) + *(.rom) + . = ALIGN (8); + } >ram + + .cs3.rom.bss : + { + *(.rom.b) + . = ALIGN (8); + } >ram + /* __cs3_region_end_rom is deprecated */ + __cs3_region_end_rom = __cs3_region_start_rom + LENGTH(ram); + __cs3_region_size_rom = LENGTH(ram); + __cs3_region_init_rom = LOADADDR (.cs3.rom); + __cs3_region_init_size_rom = SIZEOF(.cs3.rom); + __cs3_region_zero_size_rom = SIZEOF(.cs3.rom.bss); + + /* + * Start of data + */ + .data : + { + KEEP(*(.jcr)) + *(.got.plt) *(.got) + *(.shdata) + *(.data .data.* .gnu.linkonce.d.*) + *(.ram) + . = ALIGN (8); + _edata = .; + } > ram + + .bss : + { + *(.shbss) + *(.bss .bss.* .gnu.linkonce.b.*) + *(COMMON) + *(.ram.b) + . = ALIGN (8); + _end = .; + __end = .; + } > ram + + /* __cs3_region_end_ram is deprecated */ + __cs3_region_end_ram = __cs3_region_start_ram + LENGTH(ram); + __cs3_region_size_ram = LENGTH(ram); + __cs3_region_init_ram = LOADADDR (.text); + __cs3_region_init_size_ram = _edata - ADDR (.text); + __cs3_region_zero_size_ram = _end - _edata; + __cs3_region_num = 1; + + /* + * Debugging sections + */ + .stab 0 (NOLOAD) : { *(.stab) } + .stabstr 0 (NOLOAD) : { *(.stabstr) } + /* DWARF debug sections. + * Symbols in the DWARF debugging sections are relative to the beginning + * of the section so we begin them at 0. */ + /* DWARF 1 */ + .debug 0 : { *(.debug) } + .line 0 : { *(.line) } + /* GNU DWARF 1 extensions */ + .debug_srcinfo 0 : { *(.debug_srcinfo) } + .debug_sfnames 0 : { *(.debug_sfnames) } + /* DWARF 1.1 and DWARF 2 */ + .debug_aranges 0 : { *(.debug_aranges) } + .debug_pubnames 0 : { *(.debug_pubnames) } + /* DWARF 2 */ + .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_line 0 : { *(.debug_line) } + .debug_frame 0 : { *(.debug_frame) } + .debug_str 0 : { *(.debug_str) } + .debug_loc 0 : { *(.debug_loc) } + .debug_macinfo 0 : { *(.debug_macinfo) } + /* SGI/MIPS DWARF 2 extensions */ + .debug_weaknames 0 : { *(.debug_weaknames) } + .debug_funcnames 0 : { *(.debug_funcnames) } + .debug_typenames 0 : { *(.debug_typenames) } + .debug_varnames 0 : { *(.debug_varnames) } + + .note.gnu.arm.ident 0 : { KEEP (*(.note.gnu.arm.ident)) } + .ARM.attributes 0 : { KEEP (*(.ARM.attributes)) } + /DISCARD/ : { *(.note.GNU-stack) } +} diff --git a/support/ld/common_rom.inc b/support/ld/common_rom.inc new file mode 100644 index 0000000..e0c295f --- /dev/null +++ b/support/ld/common_rom.inc @@ -0,0 +1,223 @@ +/* Linker script for STM32 (by Lanchon with Mods by LeafLabs) */ + +OUTPUT_FORMAT ("elf32-littlearm", "elf32-bigarm", "elf32-littlearm") +ENTRY(_start) +SEARCH_DIR(.) +/* + * Link against libgcc, libc, and libm + */ +GROUP(libgcc.a libc.a libm.a) + +/* These force the linker to search for particular symbols from + * the start of the link process and thus ensure the user's + * overrides are picked up + */ +INCLUDE names.inc + +/* STM32 vector table. See stm32_vector_table.S */ +EXTERN(__cs3_stm32_vector_table) + +/* libcs3 C start function. See cs3.h */ +EXTERN(__cs3_start_c) + +/* main entry point */ +EXTERN(main) + +/* Initial stack pointer value. */ +EXTERN(__cs3_stack) +PROVIDE(__cs3_stack = __cs3_region_start_ram + __cs3_region_size_ram); + +/* Reset vector and chip reset entry point. See start.S */ +EXTERN(_start) +PROVIDE(__cs3_reset = _start); + +/* Beginning of the heap */ +PROVIDE(__cs3_heap_start = _end); + +/* End of the heap */ +PROVIDE(__cs3_heap_end = __cs3_region_start_ram + LENGTH(ram)); + + +SECTIONS +{ + .text : + { + CREATE_OBJECT_SYMBOLS + __cs3_region_start_rom = .; + *(.cs3.region-head.rom) + + /* + * STM32 vector table + */ + __cs3_interrupt_vector = __cs3_stm32_vector_table; + *(.cs3.interrupt_vector) + /* Make sure we pulled in an interrupt vector. */ + ASSERT (. != __cs3_stm32_vector_table, "No interrupt vector"); + + *(.rom) + *(.rom.b) + + /* + * Program code and vague linking + */ + *(.rom) + *(.rom.b) + *(.text .text.* .gnu.linkonce.t.*) + *(.plt) + *(.gnu.warning) + *(.glue_7t) *(.glue_7) *(.vfp11_veneer) + + *(.rodata .rodata.* .gnu.linkonce.r.*) + + *(.ARM.extab* .gnu.linkonce.armextab.*) + *(.gcc_except_table) + *(.eh_frame_hdr) + *(.eh_frame) + + . = ALIGN(4); + KEEP(*(.init)) + + . = ALIGN(4); + __preinit_array_start = .; + KEEP (*(.preinit_array)) + __preinit_array_end = .; + + . = ALIGN(4); + __init_array_start = .; + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array)) + __init_array_end = .; + + . = ALIGN(0x4); + KEEP (*crtbegin.o(.ctors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*crtend.o(.ctors)) + + . = ALIGN(4); + KEEP(*(.fini)) + + . = ALIGN(4); + __fini_array_start = .; + KEEP (*(.fini_array)) + KEEP (*(SORT(.fini_array.*))) + __fini_array_end = .; + + KEEP (*crtbegin.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*crtend.o(.dtors)) + + . = ALIGN(4); + __cs3_regions = .; + LONG (0) + LONG (__cs3_region_init_ram) + LONG (__cs3_region_start_ram) + LONG (__cs3_region_init_size_ram) + LONG (__cs3_region_zero_size_ram) + } > REGION_TEXT + + /* + * .ARM.exidx exception unwinding + */ + __exidx_start = .; + .ARM.exidx : + { + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + } > REGION_TEXT + __exidx_end = .; + + /* + * End of text + */ + .text.align : + { + . = ALIGN(8); + _etext = .; + } > REGION_TEXT + + /* expose a custom rom only section */ + .USER_FLASH : + { + *(.USER_FLASH) + } >rom + + /* __cs3_region_end_rom is deprecated */ + __cs3_region_end_rom = __cs3_region_start_rom + LENGTH(rom); + __cs3_region_size_rom = LENGTH(rom); + __cs3_region_num = 1; + + /* + * Start of data + */ + .data : + { + ram_begin = DEFINED(RAM_BUILD) ? . : . ; + *(.cs3.region-head.ram_begin) + + __cs3_region_start_ram = .; + *(.cs3.region-head.ram) + + KEEP(*(.jcr)) + *(.got.plt) *(.got) + *(.shdata) + *(.data .data.* .gnu.linkonce.d.*) + *(.ram) + . = ALIGN (8); + _edata = .; + } > REGION_DATA AT> REGION_TEXT + + .bss : + { + *(.shbss) + *(.bss .bss.* .gnu.linkonce.b.*) + *(COMMON) + *(.ram.b) + . = ALIGN (8); + _end = .; + __end = .; + } > REGION_BSS AT> REGION_TEXT + + /* __cs3_region_end_ram is deprecated */ + __cs3_region_end_ram = __cs3_region_start_ram + LENGTH(ram); + __cs3_region_size_ram = LENGTH(ram); + __cs3_region_init_ram = LOADADDR (.data); + __cs3_region_init_size_ram = _edata - ADDR (.data); + __cs3_region_zero_size_ram = _end - _edata; + __cs3_region_num = 1; + + /* + * Debugging sections + */ + .stab 0 (NOLOAD) : { *(.stab) } + .stabstr 0 (NOLOAD) : { *(.stabstr) } + /* DWARF debug sections. + * Symbols in the DWARF debugging sections are relative to the beginning + * of the section so we begin them at 0. */ + /* DWARF 1 */ + .debug 0 : { *(.debug) } + .line 0 : { *(.line) } + /* GNU DWARF 1 extensions */ + .debug_srcinfo 0 : { *(.debug_srcinfo) } + .debug_sfnames 0 : { *(.debug_sfnames) } + /* DWARF 1.1 and DWARF 2 */ + .debug_aranges 0 : { *(.debug_aranges) } + .debug_pubnames 0 : { *(.debug_pubnames) } + /* DWARF 2 */ + .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_line 0 : { *(.debug_line) } + .debug_frame 0 : { *(.debug_frame) } + .debug_str 0 : { *(.debug_str) } + .debug_loc 0 : { *(.debug_loc) } + .debug_macinfo 0 : { *(.debug_macinfo) } + /* SGI/MIPS DWARF 2 extensions */ + .debug_weaknames 0 : { *(.debug_weaknames) } + .debug_funcnames 0 : { *(.debug_funcnames) } + .debug_typenames 0 : { *(.debug_typenames) } + .debug_varnames 0 : { *(.debug_varnames) } + + .note.gnu.arm.ident 0 : { KEEP (*(.note.gnu.arm.ident)) } + .ARM.attributes 0 : { KEEP (*(.ARM.attributes)) } + /DISCARD/ : { *(.note.GNU-stack) } +} diff --git a/support/ld/libcs3-lanchon-stm32.a b/support/ld/libcs3-lanchon-stm32.a Binary files differdeleted file mode 100644 index 4ed858f..0000000 --- a/support/ld/libcs3-lanchon-stm32.a +++ /dev/null diff --git a/support/ld/libcs3-lanchon-stm32.tar.gz b/support/ld/libcs3-lanchon-stm32.tar.gz Binary files differdeleted file mode 100644 index 5cbcf7d..0000000 --- a/support/ld/libcs3-lanchon-stm32.tar.gz +++ /dev/null diff --git a/support/ld/libcs3_stm32_high_density.a b/support/ld/libcs3_stm32_high_density.a Binary files differnew file mode 100644 index 0000000..a09d7f0 --- /dev/null +++ b/support/ld/libcs3_stm32_high_density.a diff --git a/support/ld/libcs3_stm32_med_density.a b/support/ld/libcs3_stm32_med_density.a Binary files differnew file mode 100644 index 0000000..07a991d --- /dev/null +++ b/support/ld/libcs3_stm32_med_density.a diff --git a/support/ld/libcs3_stm32_src/Makefile b/support/ld/libcs3_stm32_src/Makefile new file mode 100644 index 0000000..d5275b9 --- /dev/null +++ b/support/ld/libcs3_stm32_src/Makefile @@ -0,0 +1,35 @@ +# setup environment
+
+TARGET_ARCH = -mcpu=cortex-m3 -mthumb
+
+CC = arm-none-eabi-gcc
+CFLAGS =
+
+AS = $(CC) -x assembler-with-cpp -c $(TARGET_ARCH)
+ASFLAGS =
+
+AR = arm-none-eabi-ar
+ARFLAGS = cr
+
+LIB_OBJS = stm32_vector_table.o stm32_isrs.o start.o start_c.o
+
+help:
+ @echo "Targets:"
+ @echo "\t medium-density: Target medium density chips (e.g. Maple)"
+ @echo "\t high-density: Target high density chips (e.g. Maple-native)"
+
+.PHONY: help medium high
+
+medium-density: $(LIB_OBJS)
+ $(AR) $(ARFLAGS) libcs3_stm32_med_density.a $(LIB_OBJS)
+ rm -f $(LIB_OBJS)
+
+high-density: CFLAGS := -DSTM32_HIGH_DENSITY
+high-density: $(LIB_OBJS)
+ $(AR) $(ARFLAGS) libcs3_stm32_high_density.a $(LIB_OBJS)
+ rm -f $(LIB_OBJS)
+
+# clean
+.PHONY: clean
+clean:
+ -rm -f $(LIB_OBJS) *.a
diff --git a/support/ld/libcs3_stm32_src/start.S b/support/ld/libcs3_stm32_src/start.S new file mode 100644 index 0000000..ae75747 --- /dev/null +++ b/support/ld/libcs3_stm32_src/start.S @@ -0,0 +1,27 @@ +/*
+ * The authors hereby grant permission to use, copy, modify, distribute,
+ * and license this software and its documentation for any purpose, provided
+ * that existing copyright notices are retained in all copies and that this
+ * notice is included verbatim in any distributions. No written agreement,
+ * license, or royalty fee is required for any of the authorized uses.
+ * Modifications to this software may be copyrighted by their authors
+ * and need not follow the licensing terms described here, provided that
+ * the new terms are clearly indicated on the first page of each file where
+ * they apply.
+ */
+
+ .text
+ .code 16
+ .thumb_func
+
+ .globl _start
+ .type _start, %function
+_start:
+ .fnstart
+ ldr r1,=__cs3_stack
+ mov sp,r1
+ ldr r1,=__cs3_start_c
+ bx r1
+ .pool
+ .cantunwind
+ .fnend
diff --git a/support/ld/libcs3_stm32_src/start_c.c b/support/ld/libcs3_stm32_src/start_c.c new file mode 100644 index 0000000..dff9fa3 --- /dev/null +++ b/support/ld/libcs3_stm32_src/start_c.c @@ -0,0 +1,58 @@ +/* CS3 start_c routine.
+ *
+ * Copyright (c) 2006, 2007 CodeSourcery Inc
+ *
+ * The authors hereby grant permission to use, copy, modify, distribute,
+ * and license this software and its documentation for any purpose, provided
+ * that existing copyright notices are retained in all copies and that this
+ * notice is included verbatim in any distributions. No written agreement,
+ * license, or royalty fee is required for any of the authorized uses.
+ * Modifications to this software may be copyrighted by their authors
+ * and need not follow the licensing terms described here, provided that
+ * the new terms are clearly indicated on the first page of each file where
+ * they apply.
+ */
+
+#include "cs3.h"
+
+extern void __libc_init_array (void);
+
+extern int main (int, char **, char **);
+
+extern void exit (int) __attribute__ ((noreturn, weak));
+
+void __attribute ((noreturn))
+__cs3_start_c (void)
+{
+ unsigned regions = __cs3_region_num;
+ const struct __cs3_region *rptr = __cs3_regions;
+ int exit_code;
+
+ /* Initialize memory */
+ for (regions = __cs3_region_num, rptr = __cs3_regions; regions--; rptr++)
+ {
+ long long *src = (long long *)rptr->init;
+ long long *dst = (long long *)rptr->data;
+ unsigned limit = rptr->init_size;
+ unsigned count;
+
+ if (src != dst)
+ for (count = 0; count != limit; count += sizeof (long long))
+ *dst++ = *src++;
+ else
+ dst = (long long *)((char *)dst + limit);
+ limit = rptr->zero_size;
+ for (count = 0; count != limit; count += sizeof (long long))
+ *dst++ = 0;
+ }
+
+ /* Run initializers. */
+ __libc_init_array ();
+
+ exit_code = main (0, NULL, NULL);
+ if (exit)
+ exit (exit_code);
+ /* If exit is NULL, make sure we don't return. */
+ for (;;)
+ continue;
+}
diff --git a/support/ld/libcs3_stm32_src/stm32_isrs.S b/support/ld/libcs3_stm32_src/stm32_isrs.S new file mode 100644 index 0000000..f95468c --- /dev/null +++ b/support/ld/libcs3_stm32_src/stm32_isrs.S @@ -0,0 +1,235 @@ +/* STM32 ISR weak declarations */
+
+ .thumb
+
+/* Default handler for all non-overridden interrupts and exceptions */
+ .globl __default_handler
+ .type __default_handler, %function
+
+__default_handler:
+ b .
+
+ .weak __exc_nmi
+ .globl __exc_nmi
+ .set __exc_nmi, __default_handler
+ .weak __exc_hardfault
+ .globl __exc_hardfault
+ .set __exc_hardfault, __default_handler
+ .weak __exc_memmanage
+ .globl __exc_memmanage
+ .set __exc_memmanage, __default_handler
+ .weak __exc_busfault
+ .globl __exc_busfault
+ .set __exc_busfault, __default_handler
+ .weak __exc_usagefault
+ .globl __exc_usagefault
+ .set __exc_usagefault, __default_handler
+ .weak __stm32reservedexception7
+ .globl __stm32reservedexception7
+ .set __stm32reservedexception7, __default_handler
+ .weak __stm32reservedexception8
+ .globl __stm32reservedexception8
+ .set __stm32reservedexception8, __default_handler
+ .weak __stm32reservedexception9
+ .globl __stm32reservedexception9
+ .set __stm32reservedexception9, __default_handler
+ .weak __stm32reservedexception10
+ .globl __stm32reservedexception10
+ .set __stm32reservedexception10, __default_handler
+ .weak __exc_svc
+ .globl __exc_svc
+ .set __exc_svc, __default_handler
+ .weak __exc_debug_monitor
+ .globl __exc_debug_monitor
+ .set __exc_debug_monitor, __default_handler
+ .weak __stm32reservedexception13
+ .globl __stm32reservedexception13
+ .set __stm32reservedexception13, __default_handler
+ .weak __exc_pendsv
+ .globl __exc_pendsv
+ .set __exc_pendsv, __default_handler
+ .weak __exc_systick
+ .globl __exc_systick
+ .set __exc_systick, __default_handler
+ .weak __irq_wwdg
+ .globl __irq_wwdg
+ .set __irq_wwdg, __default_handler
+ .weak __irq_pvd
+ .globl __irq_pvd
+ .set __irq_pvd, __default_handler
+ .weak __irq_tamper
+ .globl __irq_tamper
+ .set __irq_tamper, __default_handler
+ .weak __irq_rtc
+ .globl __irq_rtc
+ .set __irq_rtc, __default_handler
+ .weak __irq_flash
+ .globl __irq_flash
+ .set __irq_flash, __default_handler
+ .weak __irq_rcc
+ .globl __irq_rcc
+ .set __irq_rcc, __default_handler
+ .weak __irq_exti0
+ .globl __irq_exti0
+ .set __irq_exti0, __default_handler
+ .weak __irq_exti1
+ .globl __irq_exti1
+ .set __irq_exti1, __default_handler
+ .weak __irq_exti2
+ .globl __irq_exti2
+ .set __irq_exti2, __default_handler
+ .weak __irq_exti3
+ .globl __irq_exti3
+ .set __irq_exti3, __default_handler
+ .weak __irq_exti4
+ .globl __irq_exti4
+ .set __irq_exti4, __default_handler
+ .weak __irq_dma1_channel1
+ .globl __irq_dma1_channel1
+ .set __irq_dma1_channel1, __default_handler
+ .weak __irq_dma1_channel2
+ .globl __irq_dma1_channel2
+ .set __irq_dma1_channel2, __default_handler
+ .weak __irq_dma1_channel3
+ .globl __irq_dma1_channel3
+ .set __irq_dma1_channel3, __default_handler
+ .weak __irq_dma1_channel4
+ .globl __irq_dma1_channel4
+ .set __irq_dma1_channel4, __default_handler
+ .weak __irq_dma1_channel5
+ .globl __irq_dma1_channel5
+ .set __irq_dma1_channel5, __default_handler
+ .weak __irq_dma1_channel6
+ .globl __irq_dma1_channel6
+ .set __irq_dma1_channel6, __default_handler
+ .weak __irq_dma1_channel7
+ .globl __irq_dma1_channel7
+ .set __irq_dma1_channel7, __default_handler
+ .weak __irq_adc
+ .globl __irq_adc
+ .set __irq_adc, __default_handler
+ .weak __irq_usb_hp_can_tx
+ .globl __irq_usb_hp_can_tx
+ .set __irq_usb_hp_can_tx, __default_handler
+ .weak __irq_usb_lp_can_rx0
+ .globl __irq_usb_lp_can_rx0
+ .set __irq_usb_lp_can_rx0, __default_handler
+ .weak __irq_can_rx1
+ .globl __irq_can_rx1
+ .set __irq_can_rx1, __default_handler
+ .weak __irq_can_sce
+ .globl __irq_can_sce
+ .set __irq_can_sce, __default_handler
+ .weak __irq_exti9_5
+ .globl __irq_exti9_5
+ .set __irq_exti9_5, __default_handler
+ .weak __irq_tim1_brk
+ .globl __irq_tim1_brk
+ .set __irq_tim1_brk, __default_handler
+ .weak __irq_tim1_up
+ .globl __irq_tim1_up
+ .set __irq_tim1_up, __default_handler
+ .weak __irq_tim1_trg_com
+ .globl __irq_tim1_trg_com
+ .set __irq_tim1_trg_com, __default_handler
+ .weak __irq_tim1_cc
+ .globl __irq_tim1_cc
+ .set __irq_tim1_cc, __default_handler
+ .weak __irq_tim2
+ .globl __irq_tim2
+ .set __irq_tim2, __default_handler
+ .weak __irq_tim3
+ .globl __irq_tim3
+ .set __irq_tim3, __default_handler
+ .weak __irq_tim4
+ .globl __irq_tim4
+ .set __irq_tim4, __default_handler
+ .weak __irq_i2c1_ev
+ .globl __irq_i2c1_ev
+ .set __irq_i2c1_ev, __default_handler
+ .weak __irq_i2c1_er
+ .globl __irq_i2c1_er
+ .set __irq_i2c1_er, __default_handler
+ .weak __irq_i2c2_ev
+ .globl __irq_i2c2_ev
+ .set __irq_i2c2_ev, __default_handler
+ .weak __irq_i2c2_er
+ .globl __irq_i2c2_er
+ .set __irq_i2c2_er, __default_handler
+ .weak __irq_spi1
+ .globl __irq_spi1
+ .set __irq_spi1, __default_handler
+ .weak __irq_spi2
+ .globl __irq_spi2
+ .set __irq_spi2, __default_handler
+ .weak __irq_usart1
+ .globl __irq_usart1
+ .set __irq_usart1, __default_handler
+ .weak __irq_usart2
+ .globl __irq_usart2
+ .set __irq_usart2, __default_handler
+ .weak __irq_usart3
+ .globl __irq_usart3
+ .set __irq_usart3, __default_handler
+ .weak __irq_exti15_10
+ .globl __irq_exti15_10
+ .set __irq_exti15_10, __default_handler
+ .weak __irq_rtcalarm
+ .globl __irq_rtcalarm
+ .set __irq_rtcalarm, __default_handler
+ .weak __irq_usbwakeup
+ .globl __irq_usbwakeup
+ .set __irq_usbwakeup, __default_handler
+#if defined (STM32_HIGH_DENSITY)
+ .weak __irq_tim8_brk
+ .globl __irq_tim8_brk
+ .set __irq_tim8_brk, __default_handler
+ .weak __irq_tim8_up
+ .globl __irq_tim8_up
+ .set __irq_tim8_up, __default_handler
+ .weak __irq_tim8_trg_com
+ .globl __irq_tim8_trg_com
+ .set __irq_tim8_trg_com, __default_handler
+ .weak __irq_tim8_cc
+ .globl __irq_tim8_cc
+ .set __irq_tim8_cc, __default_handler
+ .weak __irq_adc3
+ .globl __irq_adc3
+ .set __irq_adc3, __default_handler
+ .weak __irq_fsmc
+ .globl __irq_fsmc
+ .set __irq_fsmc, __default_handler
+ .weak __irq_sdio
+ .globl __irq_sdio
+ .set __irq_sdio, __default_handler
+ .weak __irq_tim5
+ .globl __irq_tim5
+ .set __irq_tim5, __default_handler
+ .weak __irq_spi3
+ .globl __irq_spi3
+ .set __irq_spi3, __default_handler
+ .weak __irq_uart4
+ .globl __irq_uart4
+ .set __irq_uart4, __default_handler
+ .weak __irq_uart5
+ .globl __irq_uart5
+ .set __irq_uart5, __default_handler
+ .weak __irq_tim6
+ .globl __irq_tim6
+ .set __irq_tim6, __default_handler
+ .weak __irq_tim7
+ .globl __irq_tim7
+ .set __irq_tim7, __default_handler
+ .weak __irq_dma2_channel1
+ .globl __irq_dma2_channel1
+ .set __irq_dma2_channel1, __default_handler
+ .weak __irq_dma2_channel2
+ .globl __irq_dma2_channel2
+ .set __irq_dma2_channel2, __default_handler
+ .weak __irq_dma2_channel3
+ .globl __irq_dma2_channel3
+ .set __irq_dma2_channel3, __default_handler
+ .weak __irq_dma2_channel4_5
+ .globl __irq_dma2_channel4_5
+ .set __irq_dma2_channel4_5, __default_handler
+#endif /* STM32_HIGH_DENSITY */
diff --git a/support/ld/libcs3_stm32_src/stm32_vector_table.S b/support/ld/libcs3_stm32_src/stm32_vector_table.S new file mode 100644 index 0000000..c3f0fc7 --- /dev/null +++ b/support/ld/libcs3_stm32_src/stm32_vector_table.S @@ -0,0 +1,90 @@ +/* STM32 vector table */
+
+ .section ".cs3.interrupt_vector"
+
+ .globl __cs3_stm32_vector_table
+ .type __cs3_stm32_vector_table, %object
+
+__cs3_stm32_vector_table:
+/* CM3 core interrupts */
+ .long __cs3_stack
+ .long __cs3_reset
+ .long __exc_nmi
+ .long __exc_hardfault
+ .long __exc_memmanage
+ .long __exc_busfault
+ .long __exc_usagefault
+ .long __stm32reservedexception7
+ .long __stm32reservedexception8
+ .long __stm32reservedexception9
+ .long __stm32reservedexception10
+ .long __exc_svc
+ .long __exc_debug_monitor
+ .long __stm32reservedexception13
+ .long __exc_pendsv
+ .long __exc_systick
+/* Peripheral interrupts */
+ .long __irq_wwdg
+ .long __irq_pvd
+ .long __irq_tamper
+ .long __irq_rtc
+ .long __irq_flash
+ .long __irq_rcc
+ .long __irq_exti0
+ .long __irq_exti1
+ .long __irq_exti2
+ .long __irq_exti3
+ .long __irq_exti4
+ .long __irq_dma1_channel1
+ .long __irq_dma1_channel2
+ .long __irq_dma1_channel3
+ .long __irq_dma1_channel4
+ .long __irq_dma1_channel5
+ .long __irq_dma1_channel6
+ .long __irq_dma1_channel7
+ .long __irq_adc
+ .long __irq_usb_hp_can_tx
+ .long __irq_usb_lp_can_rx0
+ .long __irq_can_rx1
+ .long __irq_can_sce
+ .long __irq_exti9_5
+ .long __irq_tim1_brk
+ .long __irq_tim1_up
+ .long __irq_tim1_trg_com
+ .long __irq_tim1_cc
+ .long __irq_tim2
+ .long __irq_tim3
+ .long __irq_tim4
+ .long __irq_i2c1_ev
+ .long __irq_i2c1_er
+ .long __irq_i2c2_ev
+ .long __irq_i2c2_er
+ .long __irq_spi1
+ .long __irq_spi2
+ .long __irq_usart1
+ .long __irq_usart2
+ .long __irq_usart3
+ .long __irq_exti15_10
+ .long __irq_rtcalarm
+ .long __irq_usbwakeup
+#if defined (STM32_HIGH_DENSITY)
+ .long __irq_tim8_brk
+ .long __irq_tim8_up
+ .long __irq_tim8_trg_com
+ .long __irq_tim8_cc
+ .long __irq_adc3
+ .long __irq_fsmc
+ .long __irq_sdio
+ .long __irq_tim5
+ .long __irq_spi3
+ .long __irq_uart4
+ .long __irq_uart5
+ .long __irq_tim6
+ .long __irq_tim7
+ .long __irq_dma2_channel1
+ .long __irq_dma2_channel2
+ .long __irq_dma2_channel3
+ .long __irq_dma2_channel4_5
+#endif /* STM32_HIGH_DENSITY */
+
+ .size __cs3_stm32_vector_table, . - __cs3_stm32_vector_table
diff --git a/support/ld/maple/flash.ld b/support/ld/maple/flash.ld index 2d40100..9c3efcb 100644 --- a/support/ld/maple/flash.ld +++ b/support/ld/maple/flash.ld @@ -1,211 +1,28 @@ -/* Linker script for STM32 (by Lanchon with Mods by LeafLabs)
- *
- * Version:Sourcery G++ 4.2-84
- * BugURL:https://support.codesourcery.com/GNUToolchain/
- *
- * Copyright 2007 CodeSourcery.
- *
- * The authors hereby grant permission to use, copy, modify, distribute,
- * and license this software and its documentation for any purpose, provided
- * that existing copyright notices are retained in all copies and that this
- * notice is included verbatim in any distributions. No written agreement,
- * license, or royalty fee is required for any of the authorized uses.
- * Modifications to this software may be copyrighted by their authors
- * and need not follow the licensing terms described here, provided that
- * the new terms are clearly indicated on the first page of each file where
- * they apply. */
-
-/* Linker script for STM32 (by Lanchon),
- * ROM and RAM relocated to their positions
- * as placed by Maple bootloader
- *
- * Configure target memory and included script
- * according to your application requirements. */
+/*
+ * Linker script for STM32.
+ * STM32F103RBT6 medium density chip linker script for use with
+ * maple bootloader. Loads to flash.
+ */
-/* Define memory spaces. */
+/*
+ * Define memory spaces.
+ */
MEMORY
{
ram (rwx) : ORIGIN = 0x20000C00, LENGTH = 17K
rom (rx) : ORIGIN = 0x08005000, LENGTH = 108K
}
-OUTPUT_FORMAT ("elf32-littlearm", "elf32-bigarm", "elf32-littlearm")
-ENTRY(_start)
-SEARCH_DIR(.)
-/* GROUP(-lgcc -lc -lcs3 -lcs3unhosted -lcs3-lanchon-stm32) */
-GROUP(libgcc.a libc.a libm.a libcs3-lanchon-stm32.a)
-
-/* These force the linker to search for particular symbols from
- * the start of the link process and thus ensure the user's
- * overrides are picked up
+/*
+ * Use medium density device vector table
*/
-EXTERN(__cs3_reset_lanchon_stm32)
-INCLUDE names.inc
-EXTERN(__cs3_interrupt_vector_lanchon_stm32)
-EXTERN(__cs3_start_c main __cs3_stack __cs3_heap_end)
-EXTERN(_start)
-
-PROVIDE(__cs3_stack = __cs3_region_start_ram + __cs3_region_size_ram);
-PROVIDE(__cs3_heap_start = _end);
-PROVIDE(__cs3_heap_end = __cs3_region_start_ram + __cs3_region_size_ram);
-
-SECTIONS
-{
- .text :
- {
- CREATE_OBJECT_SYMBOLS
- __cs3_region_start_rom = .;
- *(.cs3.region-head.rom)
- __cs3_interrupt_vector = __cs3_interrupt_vector_lanchon_stm32;
- *(.cs3.interrupt_vector)
- /* Make sure we pulled in an interrupt vector. */
- ASSERT (. != __cs3_interrupt_vector_lanchon_stm32, "No interrupt vector");
- *(.rom)
- *(.rom.b)
-
- PROVIDE(__cs3_reset_lanchon_stm32 = _start);
- __cs3_reset = __cs3_reset_lanchon_stm32;
- *(.cs3.reset)
-
- *(.text .text.* .gnu.linkonce.t.*)
- *(.plt)
- *(.gnu.warning)
- *(.glue_7t) *(.glue_7) *(.vfp11_veneer)
-
- *(.rodata .rodata.* .gnu.linkonce.r.*)
-
- *(.ARM.extab* .gnu.linkonce.armextab.*)
- *(.gcc_except_table)
- *(.eh_frame_hdr)
- *(.eh_frame)
-
- . = ALIGN(4);
- KEEP(*(.init))
-
- . = ALIGN(4);
- __preinit_array_start = .;
- KEEP (*(.preinit_array))
- __preinit_array_end = .;
+GROUP(libcs3_stm32_med_density.a)
- . = ALIGN(4);
- __init_array_start = .;
- KEEP (*(SORT(.init_array.*)))
- KEEP (*(.init_array))
- __init_array_end = .;
+REGION_ALIAS("REGION_TEXT", rom);
+REGION_ALIAS("REGION_DATA", ram);
+REGION_ALIAS("REGION_BSS", ram);
- . = ALIGN(0x4);
- KEEP (*crtbegin.o(.ctors))
- KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors))
- KEEP (*(SORT(.ctors.*)))
- KEEP (*crtend.o(.ctors))
-
- . = ALIGN(4);
- KEEP(*(.fini))
-
- . = ALIGN(4);
- __fini_array_start = .;
- KEEP (*(.fini_array))
- KEEP (*(SORT(.fini_array.*)))
- __fini_array_end = .;
-
- KEEP (*crtbegin.o(.dtors))
- KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors))
- KEEP (*(SORT(.dtors.*)))
- KEEP (*crtend.o(.dtors))
-
- . = ALIGN(4);
- __cs3_regions = .;
- LONG (0)
- LONG (__cs3_region_init_ram)
- LONG (__cs3_region_start_ram)
- LONG (__cs3_region_init_size_ram)
- LONG (__cs3_region_zero_size_ram)
- } >rom
-
- /* .ARM.exidx is sorted, so has to go in its own output section. */
- __exidx_start = .;
- .ARM.exidx :
- {
- *(.ARM.exidx* .gnu.linkonce.armexidx.*)
- } >rom
- __exidx_end = .;
- .text.align :
- {
- . = ALIGN(8);
- _etext = .;
- } >rom
-
-/* expose a custom rom only section */
- .USER_FLASH :
- {
- *(.USER_FLASH)
- } >rom
-
-
- /* __cs3_region_end_rom is deprecated */
- __cs3_region_end_rom = __cs3_region_start_rom + LENGTH(rom);
- __cs3_region_size_rom = LENGTH(rom);
- __cs3_region_num = 1;
-
- .data :
- {
- __cs3_region_start_ram = .;
- *(.cs3.region-head.ram)
- KEEP(*(.jcr))
- *(.got.plt) *(.got)
- *(.shdata)
- *(.data .data.* .gnu.linkonce.d.*)
- *(.ram)
- . = ALIGN (8);
- _edata = .;
- } >ram AT>rom
- .bss :
- {
- *(.shbss)
- *(.bss .bss.* .gnu.linkonce.b.*)
- *(COMMON)
- *(.ram.b)
- . = ALIGN (8);
- _end = .;
- __end = .;
- } >ram AT>rom
- /* __cs3_region_end_ram is deprecated */
- __cs3_region_end_ram = __cs3_region_start_ram + LENGTH(ram);
- __cs3_region_size_ram = LENGTH(ram);
- __cs3_region_init_ram = LOADADDR (.data);
- __cs3_region_init_size_ram = _edata - ADDR (.data);
- __cs3_region_zero_size_ram = _end - _edata;
- __cs3_region_num = 1;
-
- .stab 0 (NOLOAD) : { *(.stab) }
- .stabstr 0 (NOLOAD) : { *(.stabstr) }
- /* DWARF debug sections.
- * Symbols in the DWARF debugging sections are relative to the beginning
- * of the section so we begin them at 0. */
- /* DWARF 1 */
- .debug 0 : { *(.debug) }
- .line 0 : { *(.line) }
- /* GNU DWARF 1 extensions */
- .debug_srcinfo 0 : { *(.debug_srcinfo) }
- .debug_sfnames 0 : { *(.debug_sfnames) }
- /* DWARF 1.1 and DWARF 2 */
- .debug_aranges 0 : { *(.debug_aranges) }
- .debug_pubnames 0 : { *(.debug_pubnames) }
- /* DWARF 2 */
- .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) }
- .debug_abbrev 0 : { *(.debug_abbrev) }
- .debug_line 0 : { *(.debug_line) }
- .debug_frame 0 : { *(.debug_frame) }
- .debug_str 0 : { *(.debug_str) }
- .debug_loc 0 : { *(.debug_loc) }
- .debug_macinfo 0 : { *(.debug_macinfo) }
- /* SGI/MIPS DWARF 2 extensions */
- .debug_weaknames 0 : { *(.debug_weaknames) }
- .debug_funcnames 0 : { *(.debug_funcnames) }
- .debug_typenames 0 : { *(.debug_typenames) }
- .debug_varnames 0 : { *(.debug_varnames) }
-
- .note.gnu.arm.ident 0 : { KEEP (*(.note.gnu.arm.ident)) }
- .ARM.attributes 0 : { KEEP (*(.ARM.attributes)) }
- /DISCARD/ : { *(.note.GNU-stack) }
-}
+/*
+ * Define the rest of the sections
+ */
+INCLUDE common_rom.inc
diff --git a/support/ld/maple/jtag.ld b/support/ld/maple/jtag.ld index 435e3f0..caf90ee 100644 --- a/support/ld/maple/jtag.ld +++ b/support/ld/maple/jtag.ld @@ -1,186 +1,28 @@ -/* Linker script for STM32 (by Lanchon),
- * ROM and RAM relocated to their positions
- * as placed by Maple bootloader
- *
- * Configure target memory and included script
- * according to your application requirements. */
+/*
+ * Linker script for STM32.
+ * STM32F103RBT6 medium density chip linker script.
+ */
-/* Define memory spaces. */
+/*
+ * Define memory spaces.
+ */
MEMORY
{
ram (rwx) : ORIGIN = 0x20000000, LENGTH = 20K
rom (rx) : ORIGIN = 0x08000000, LENGTH = 128K
}
-OUTPUT_FORMAT ("elf32-littlearm", "elf32-bigarm", "elf32-littlearm")
-ENTRY(_start)
-SEARCH_DIR(.)
-/* GROUP(-lgcc -lc -lcs3 -lcs3unhosted -lcs3-lanchon-stm32) */
-GROUP(libgcc.a libc.a libm.a libcs3-lanchon-stm32.a)
-
-/* These force the linker to search for particular symbols from
- * the start of the link process and thus ensure the user's
- * overrides are picked up
+/*
+ * Use medium density device vector table
*/
-EXTERN(__cs3_reset_lanchon_stm32)
-INCLUDE names.inc
-EXTERN(__cs3_interrupt_vector_lanchon_stm32)
-EXTERN(__cs3_start_c main __cs3_stack __cs3_heap_end)
-EXTERN(_start)
-
-PROVIDE(__cs3_stack = __cs3_region_start_ram + __cs3_region_size_ram);
-PROVIDE(__cs3_heap_start = _end);
-PROVIDE(__cs3_heap_end = __cs3_region_start_ram + __cs3_region_size_ram);
-
-SECTIONS
-{
- .text :
- {
- CREATE_OBJECT_SYMBOLS
- __cs3_region_start_rom = .;
- *(.cs3.region-head.rom)
- __cs3_interrupt_vector = __cs3_interrupt_vector_lanchon_stm32;
- *(.cs3.interrupt_vector)
- /* Make sure we pulled in an interrupt vector. */
- ASSERT (. != __cs3_interrupt_vector_lanchon_stm32, "No interrupt vector");
- *(.rom)
- *(.rom.b)
-
- PROVIDE(__cs3_reset_lanchon_stm32 = _start);
- __cs3_reset = __cs3_reset_lanchon_stm32;
- *(.cs3.reset)
-
- *(.text .text.* .gnu.linkonce.t.*)
- *(.plt)
- *(.gnu.warning)
- *(.glue_7t) *(.glue_7) *(.vfp11_veneer)
-
- *(.rodata .rodata.* .gnu.linkonce.r.*)
-
- *(.ARM.extab* .gnu.linkonce.armextab.*)
- *(.gcc_except_table)
- *(.eh_frame_hdr)
- *(.eh_frame)
-
- . = ALIGN(4);
- KEEP(*(.init))
+GROUP(libcs3_stm32_med_density.a)
- . = ALIGN(4);
- __preinit_array_start = .;
- KEEP (*(.preinit_array))
- __preinit_array_end = .;
+REGION_ALIAS("REGION_TEXT", rom);
+REGION_ALIAS("REGION_DATA", ram);
+REGION_ALIAS("REGION_BSS", ram);
- . = ALIGN(4);
- __init_array_start = .;
- KEEP (*(SORT(.init_array.*)))
- KEEP (*(.init_array))
- __init_array_end = .;
-
- . = ALIGN(0x4);
- KEEP (*crtbegin.o(.ctors))
- KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors))
- KEEP (*(SORT(.ctors.*)))
- KEEP (*crtend.o(.ctors))
-
- . = ALIGN(4);
- KEEP(*(.fini))
-
- . = ALIGN(4);
- __fini_array_start = .;
- KEEP (*(.fini_array))
- KEEP (*(SORT(.fini_array.*)))
- __fini_array_end = .;
-
- KEEP (*crtbegin.o(.dtors))
- KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors))
- KEEP (*(SORT(.dtors.*)))
- KEEP (*crtend.o(.dtors))
-
- . = ALIGN(4);
- __cs3_regions = .;
- LONG (0)
- LONG (__cs3_region_init_ram)
- LONG (__cs3_region_start_ram)
- LONG (__cs3_region_init_size_ram)
- LONG (__cs3_region_zero_size_ram)
- } >rom
-
- /* .ARM.exidx is sorted, so has to go in its own output section. */
- __exidx_start = .;
- .ARM.exidx :
- {
- *(.ARM.exidx* .gnu.linkonce.armexidx.*)
- } >rom
- __exidx_end = .;
- .text.align :
- {
- . = ALIGN(8);
- _etext = .;
- } >rom
- /* __cs3_region_end_rom is deprecated */
- __cs3_region_end_rom = __cs3_region_start_rom + LENGTH(rom);
- __cs3_region_size_rom = LENGTH(rom);
- __cs3_region_num = 1;
-
- .data :
- {
- __cs3_region_start_ram = .;
- *(.cs3.region-head.ram)
- KEEP(*(.jcr))
- *(.got.plt) *(.got)
- *(.shdata)
- *(.data .data.* .gnu.linkonce.d.*)
- *(.ram)
- . = ALIGN (8);
- _edata = .;
- } >ram AT>rom
- .bss :
- {
- *(.shbss)
- *(.bss .bss.* .gnu.linkonce.b.*)
- *(COMMON)
- *(.ram.b)
- . = ALIGN (8);
- _end = .;
- __end = .;
- } >ram AT>rom
- /* __cs3_region_end_ram is deprecated */
- __cs3_region_end_ram = __cs3_region_start_ram + LENGTH(ram);
- __cs3_region_size_ram = LENGTH(ram);
- __cs3_region_init_ram = LOADADDR (.data);
- __cs3_region_init_size_ram = _edata - ADDR (.data);
- __cs3_region_zero_size_ram = _end - _edata;
- __cs3_region_num = 1;
-
- .stab 0 (NOLOAD) : { *(.stab) }
- .stabstr 0 (NOLOAD) : { *(.stabstr) }
- /* DWARF debug sections.
- * Symbols in the DWARF debugging sections are relative to the beginning
- * of the section so we begin them at 0. */
- /* DWARF 1 */
- .debug 0 : { *(.debug) }
- .line 0 : { *(.line) }
- /* GNU DWARF 1 extensions */
- .debug_srcinfo 0 : { *(.debug_srcinfo) }
- .debug_sfnames 0 : { *(.debug_sfnames) }
- /* DWARF 1.1 and DWARF 2 */
- .debug_aranges 0 : { *(.debug_aranges) }
- .debug_pubnames 0 : { *(.debug_pubnames) }
- /* DWARF 2 */
- .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) }
- .debug_abbrev 0 : { *(.debug_abbrev) }
- .debug_line 0 : { *(.debug_line) }
- .debug_frame 0 : { *(.debug_frame) }
- .debug_str 0 : { *(.debug_str) }
- .debug_loc 0 : { *(.debug_loc) }
- .debug_macinfo 0 : { *(.debug_macinfo) }
- /* SGI/MIPS DWARF 2 extensions */
- .debug_weaknames 0 : { *(.debug_weaknames) }
- .debug_funcnames 0 : { *(.debug_funcnames) }
- .debug_typenames 0 : { *(.debug_typenames) }
- .debug_varnames 0 : { *(.debug_varnames) }
+/*
+ * Define the rest of the sections
+ */
+INCLUDE common_rom.inc
- .note.gnu.arm.ident 0 : { KEEP (*(.note.gnu.arm.ident)) }
- .ARM.attributes 0 : { KEEP (*(.ARM.attributes)) }
- /DISCARD/ : { *(.note.GNU-stack) }
-}
diff --git a/support/ld/maple/ram.ld b/support/ld/maple/ram.ld index 1fbecc5..b1e285e 100644 --- a/support/ld/maple/ram.ld +++ b/support/ld/maple/ram.ld @@ -1,220 +1,27 @@ -/* Linker script for STM32 (by Lanchon with Mods by LeafLabs)
- *
- * Version:Sourcery G++ 4.2-84
- * BugURL:https://support.codesourcery.com/GNUToolchain/
- *
- * Copyright 2007 CodeSourcery.
- *
- * The authors hereby grant permission to use, copy, modify, distribute,
- * and license this software and its documentation for any purpose, provided
- * that existing copyright notices are retained in all copies and that this
- * notice is included verbatim in any distributions. No written agreement,
- * license, or royalty fee is required for any of the authorized uses.
- * Modifications to this software may be copyrighted by their authors
- * and need not follow the licensing terms described here, provided that
- * the new terms are clearly indicated on the first page of each file where
- * they apply. */
-
-/* Linker script for STM32 (by Lanchon),
- * ROM and RAM relocated to their positions
- * as placed by Maple bootloader
- *
- * Configure target memory and included script
- * according to your application requirements. */
+/*
+ * Linker script for STM32.
+ * STM32F103RBT6 medium density chip linker script. Loads to ram.
+ */
-/* Define memory spaces. */
+/*
+ * Define memory spaces.
+ */
MEMORY
{
ram (rwx) : ORIGIN = 0x20000C00, LENGTH = 17K
rom (rx) : ORIGIN = 0x08005000, LENGTH = 0K
}
-
-OUTPUT_FORMAT ("elf32-littlearm", "elf32-bigarm", "elf32-littlearm")
-ENTRY(_start)
-SEARCH_DIR(.)
-/* GROUP(-lgcc -lc -lcs3 -lcs3unhosted -lcs3-lanchon-stm32) */
-GROUP(libgcc.a libc.a libm.a libcs3-lanchon-stm32.a)
-
-/* These force the linker to search for particular symbols from
- * the start of the link process and thus ensure the user's
- * overrides are picked up
+/*
+ * Use medium density device vector table
*/
-EXTERN(__cs3_reset_lanchon_stm32)
-INCLUDE names.inc
-EXTERN(__cs3_interrupt_vector_lanchon_stm32)
-EXTERN(__cs3_start_c main __cs3_stack __cs3_heap_end)
-EXTERN(_start)
-
-PROVIDE(__cs3_stack = __cs3_region_start_ram + __cs3_region_size_ram);
-PROVIDE(__cs3_heap_start = _end);
-PROVIDE(__cs3_heap_end = __cs3_region_start_ram + __cs3_region_size_ram);
-
-SECTIONS
-{
- .text :
- {
- CREATE_OBJECT_SYMBOLS
- __cs3_region_start_ram = .;
- *(.cs3.region-head.ram)
- __cs3_interrupt_vector = __cs3_interrupt_vector_lanchon_stm32;
- *(.cs3.interrupt_vector)
- /* Make sure we pulled in an interrupt vector. */
- ASSERT (. != __cs3_interrupt_vector_lanchon_stm32, "No interrupt vector");
-
- PROVIDE(__cs3_reset_lanchon_stm32 = _start);
- __cs3_reset = __cs3_reset_lanchon_stm32;
- *(.cs3.reset)
-
- *(.text .text.* .gnu.linkonce.t.*)
- *(.plt)
- *(.gnu.warning)
- *(.glue_7t) *(.glue_7) *(.vfp11_veneer)
-
- *(.rodata .rodata.* .gnu.linkonce.r.*)
-
- *(.ARM.extab* .gnu.linkonce.armextab.*)
- *(.gcc_except_table)
- *(.eh_frame_hdr)
- *(.eh_frame)
-
- . = ALIGN(4);
- KEEP(*(.init))
-
- . = ALIGN(4);
- __preinit_array_start = .;
- KEEP (*(.preinit_array))
- __preinit_array_end = .;
+GROUP(libcs3_stm32_med_density.a)
- . = ALIGN(4);
- __init_array_start = .;
- KEEP (*(SORT(.init_array.*)))
- KEEP (*(.init_array))
- __init_array_end = .;
-
- . = ALIGN(0x4);
- KEEP (*crtbegin.o(.ctors))
- KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors))
- KEEP (*(SORT(.ctors.*)))
- KEEP (*crtend.o(.ctors))
-
- . = ALIGN(4);
- KEEP(*(.fini))
-
- . = ALIGN(4);
- __fini_array_start = .;
- KEEP (*(.fini_array))
- KEEP (*(SORT(.fini_array.*)))
- __fini_array_end = .;
-
- KEEP (*crtbegin.o(.dtors))
- KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors))
- KEEP (*(SORT(.dtors.*)))
- KEEP (*crtend.o(.dtors))
-
- . = ALIGN(4);
- __cs3_regions = .;
- LONG (0)
- LONG (__cs3_region_init_ram)
- LONG (__cs3_region_start_ram)
- LONG (__cs3_region_init_size_ram)
- LONG (__cs3_region_zero_size_ram)
- } >ram
-
- /* .ARM.exidx is sorted, so has to go in its own output section. */
- /* even cs3.rom is in ram since its running as user code under the Maple
- bootloader */
- __exidx_start = .;
- .ARM.exidx :
- {
- *(.ARM.exidx* .gnu.linkonce.armexidx.*)
- } >ram
- __exidx_end = .;
- .text.align :
- {
- . = ALIGN(8);
- _etext = .;
- } >ram
-
- .cs3.rom :
- {
- __cs3_region_start_rom = .;
- *(.cs3.region-head.rom)
- *(.rom)
- . = ALIGN (8);
- } >ram
-
- .cs3.rom.bss :
- {
- *(.rom.b)
- . = ALIGN (8);
- } >ram
- /* __cs3_region_end_rom is deprecated */
- __cs3_region_end_rom = __cs3_region_start_rom + LENGTH(ram);
- __cs3_region_size_rom = LENGTH(ram);
- __cs3_region_init_rom = LOADADDR (.cs3.rom);
- __cs3_region_init_size_rom = SIZEOF(.cs3.rom);
- __cs3_region_zero_size_rom = SIZEOF(.cs3.rom.bss);
-
- .data :
- {
-
- KEEP(*(.jcr))
- *(.got.plt) *(.got)
- *(.shdata)
- *(.data .data.* .gnu.linkonce.d.*)
- *(.ram)
- . = ALIGN (8);
- _edata = .;
- } >ram
- .bss :
- {
- *(.shbss)
- *(.bss .bss.* .gnu.linkonce.b.*)
- *(COMMON)
- *(.ram.b)
- . = ALIGN (8);
- _end = .;
- __end = .;
- } >ram
- /* __cs3_region_end_ram is deprecated */
- __cs3_region_end_ram = __cs3_region_start_ram + LENGTH(ram);
- __cs3_region_size_ram = LENGTH(ram);
- __cs3_region_init_ram = LOADADDR (.text);
- __cs3_region_init_size_ram = _edata - ADDR (.text);
- __cs3_region_zero_size_ram = _end - _edata;
- __cs3_region_num = 1;
-
- .stab 0 (NOLOAD) : { *(.stab) }
- .stabstr 0 (NOLOAD) : { *(.stabstr) }
- /* DWARF debug sections.
- * Symbols in the DWARF debugging sections are relative to the beginning
- * of the section so we begin them at 0. */
- /* DWARF 1 */
- .debug 0 : { *(.debug) }
- .line 0 : { *(.line) }
- /* GNU DWARF 1 extensions */
- .debug_srcinfo 0 : { *(.debug_srcinfo) }
- .debug_sfnames 0 : { *(.debug_sfnames) }
- /* DWARF 1.1 and DWARF 2 */
- .debug_aranges 0 : { *(.debug_aranges) }
- .debug_pubnames 0 : { *(.debug_pubnames) }
- /* DWARF 2 */
- .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) }
- .debug_abbrev 0 : { *(.debug_abbrev) }
- .debug_line 0 : { *(.debug_line) }
- .debug_frame 0 : { *(.debug_frame) }
- .debug_str 0 : { *(.debug_str) }
- .debug_loc 0 : { *(.debug_loc) }
- .debug_macinfo 0 : { *(.debug_macinfo) }
- /* SGI/MIPS DWARF 2 extensions */
- .debug_weaknames 0 : { *(.debug_weaknames) }
- .debug_funcnames 0 : { *(.debug_funcnames) }
- .debug_typenames 0 : { *(.debug_typenames) }
- .debug_varnames 0 : { *(.debug_varnames) }
-
- .note.gnu.arm.ident 0 : { KEEP (*(.note.gnu.arm.ident)) }
- .ARM.attributes 0 : { KEEP (*(.ARM.attributes)) }
- /DISCARD/ : { *(.note.GNU-stack) }
-}
+REGION_ALIAS("REGION_TEXT", ram);
+REGION_ALIAS("REGION_DATA", ram);
+REGION_ALIAS("REGION_BSS", ram);
+/*
+ * Define the rest of the sections
+ */
+INCLUDE common_ram.inc
diff --git a/support/ld/maple_RET6/flash.ld b/support/ld/maple_RET6/flash.ld new file mode 100644 index 0000000..2a4f0c7 --- /dev/null +++ b/support/ld/maple_RET6/flash.ld @@ -0,0 +1,18 @@ +/* + * STM32F103RET6 high density chip linker script for use with Maple + * bootloader. Loads to Flash. + */ + +MEMORY +{ + ram (rwx) : ORIGIN = 0x20000C00, LENGTH = 61K + rom (rx) : ORIGIN = 0x08005000, LENGTH = 492K +} + +GROUP(libcs3_stm32_high_density.a) + +REGION_ALIAS("REGION_TEXT", rom); +REGION_ALIAS("REGION_DATA", ram); +REGION_ALIAS("REGION_BSS", ram); + +INCLUDE common_rom.inc diff --git a/support/ld/maple_RET6/jtag.ld b/support/ld/maple_RET6/jtag.ld new file mode 100644 index 0000000..88f45e0 --- /dev/null +++ b/support/ld/maple_RET6/jtag.ld @@ -0,0 +1,17 @@ +/* + * STM32F103RET6 high density bare metal linker script. + */ + +MEMORY +{ + ram (rwx) : ORIGIN = 0x20000000, LENGTH = 64K + rom (rx) : ORIGIN = 0x08000000, LENGTH = 512K +} + +GROUP(libcs3_stm32_high_density.a) + +REGION_ALIAS("REGION_TEXT", rom); +REGION_ALIAS("REGION_DATA", ram); +REGION_ALIAS("REGION_BSS", ram); + +INCLUDE common_rom.inc diff --git a/support/ld/maple_RET6/ram.ld b/support/ld/maple_RET6/ram.ld new file mode 100644 index 0000000..f09acd9 --- /dev/null +++ b/support/ld/maple_RET6/ram.ld @@ -0,0 +1,18 @@ +/* + * STM32F103RET6 high density chip linker script for use with Maple + * bootloader. Loads to RAM. + */ + +MEMORY +{ + ram (rwx) : ORIGIN = 0x20000C00, LENGTH = 61K + rom (rx) : ORIGIN = 0x08005000, LENGTH = 0K +} + +GROUP(libcs3_stm32_high_density.a) + +REGION_ALIAS("REGION_TEXT", ram); +REGION_ALIAS("REGION_DATA", ram); +REGION_ALIAS("REGION_BSS", ram); + +INCLUDE common_ram.inc diff --git a/support/ld/maple_mini/flash.ld b/support/ld/maple_mini/flash.ld index 2d40100..4c26da2 100644 --- a/support/ld/maple_mini/flash.ld +++ b/support/ld/maple_mini/flash.ld @@ -1,211 +1,27 @@ -/* Linker script for STM32 (by Lanchon with Mods by LeafLabs)
- *
- * Version:Sourcery G++ 4.2-84
- * BugURL:https://support.codesourcery.com/GNUToolchain/
- *
- * Copyright 2007 CodeSourcery.
- *
- * The authors hereby grant permission to use, copy, modify, distribute,
- * and license this software and its documentation for any purpose, provided
- * that existing copyright notices are retained in all copies and that this
- * notice is included verbatim in any distributions. No written agreement,
- * license, or royalty fee is required for any of the authorized uses.
- * Modifications to this software may be copyrighted by their authors
- * and need not follow the licensing terms described here, provided that
- * the new terms are clearly indicated on the first page of each file where
- * they apply. */
-
-/* Linker script for STM32 (by Lanchon),
- * ROM and RAM relocated to their positions
- * as placed by Maple bootloader
- *
- * Configure target memory and included script
- * according to your application requirements. */
+/*
+ * Linker script for STM32.
+ * Maple mini flash linker script.
+ */
-/* Define memory spaces. */
+/*
+ * Define memory spaces.
+ */
MEMORY
{
ram (rwx) : ORIGIN = 0x20000C00, LENGTH = 17K
rom (rx) : ORIGIN = 0x08005000, LENGTH = 108K
}
-OUTPUT_FORMAT ("elf32-littlearm", "elf32-bigarm", "elf32-littlearm")
-ENTRY(_start)
-SEARCH_DIR(.)
-/* GROUP(-lgcc -lc -lcs3 -lcs3unhosted -lcs3-lanchon-stm32) */
-GROUP(libgcc.a libc.a libm.a libcs3-lanchon-stm32.a)
-
-/* These force the linker to search for particular symbols from
- * the start of the link process and thus ensure the user's
- * overrides are picked up
+/*
+ * Use medium density device vector table
*/
-EXTERN(__cs3_reset_lanchon_stm32)
-INCLUDE names.inc
-EXTERN(__cs3_interrupt_vector_lanchon_stm32)
-EXTERN(__cs3_start_c main __cs3_stack __cs3_heap_end)
-EXTERN(_start)
-
-PROVIDE(__cs3_stack = __cs3_region_start_ram + __cs3_region_size_ram);
-PROVIDE(__cs3_heap_start = _end);
-PROVIDE(__cs3_heap_end = __cs3_region_start_ram + __cs3_region_size_ram);
-
-SECTIONS
-{
- .text :
- {
- CREATE_OBJECT_SYMBOLS
- __cs3_region_start_rom = .;
- *(.cs3.region-head.rom)
- __cs3_interrupt_vector = __cs3_interrupt_vector_lanchon_stm32;
- *(.cs3.interrupt_vector)
- /* Make sure we pulled in an interrupt vector. */
- ASSERT (. != __cs3_interrupt_vector_lanchon_stm32, "No interrupt vector");
- *(.rom)
- *(.rom.b)
-
- PROVIDE(__cs3_reset_lanchon_stm32 = _start);
- __cs3_reset = __cs3_reset_lanchon_stm32;
- *(.cs3.reset)
-
- *(.text .text.* .gnu.linkonce.t.*)
- *(.plt)
- *(.gnu.warning)
- *(.glue_7t) *(.glue_7) *(.vfp11_veneer)
-
- *(.rodata .rodata.* .gnu.linkonce.r.*)
-
- *(.ARM.extab* .gnu.linkonce.armextab.*)
- *(.gcc_except_table)
- *(.eh_frame_hdr)
- *(.eh_frame)
-
- . = ALIGN(4);
- KEEP(*(.init))
-
- . = ALIGN(4);
- __preinit_array_start = .;
- KEEP (*(.preinit_array))
- __preinit_array_end = .;
+GROUP(libcs3_stm32_med_density.a)
- . = ALIGN(4);
- __init_array_start = .;
- KEEP (*(SORT(.init_array.*)))
- KEEP (*(.init_array))
- __init_array_end = .;
+REGION_ALIAS("REGION_TEXT", rom);
+REGION_ALIAS("REGION_DATA", ram);
+REGION_ALIAS("REGION_BSS", ram);
- . = ALIGN(0x4);
- KEEP (*crtbegin.o(.ctors))
- KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors))
- KEEP (*(SORT(.ctors.*)))
- KEEP (*crtend.o(.ctors))
-
- . = ALIGN(4);
- KEEP(*(.fini))
-
- . = ALIGN(4);
- __fini_array_start = .;
- KEEP (*(.fini_array))
- KEEP (*(SORT(.fini_array.*)))
- __fini_array_end = .;
-
- KEEP (*crtbegin.o(.dtors))
- KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors))
- KEEP (*(SORT(.dtors.*)))
- KEEP (*crtend.o(.dtors))
-
- . = ALIGN(4);
- __cs3_regions = .;
- LONG (0)
- LONG (__cs3_region_init_ram)
- LONG (__cs3_region_start_ram)
- LONG (__cs3_region_init_size_ram)
- LONG (__cs3_region_zero_size_ram)
- } >rom
-
- /* .ARM.exidx is sorted, so has to go in its own output section. */
- __exidx_start = .;
- .ARM.exidx :
- {
- *(.ARM.exidx* .gnu.linkonce.armexidx.*)
- } >rom
- __exidx_end = .;
- .text.align :
- {
- . = ALIGN(8);
- _etext = .;
- } >rom
-
-/* expose a custom rom only section */
- .USER_FLASH :
- {
- *(.USER_FLASH)
- } >rom
-
-
- /* __cs3_region_end_rom is deprecated */
- __cs3_region_end_rom = __cs3_region_start_rom + LENGTH(rom);
- __cs3_region_size_rom = LENGTH(rom);
- __cs3_region_num = 1;
-
- .data :
- {
- __cs3_region_start_ram = .;
- *(.cs3.region-head.ram)
- KEEP(*(.jcr))
- *(.got.plt) *(.got)
- *(.shdata)
- *(.data .data.* .gnu.linkonce.d.*)
- *(.ram)
- . = ALIGN (8);
- _edata = .;
- } >ram AT>rom
- .bss :
- {
- *(.shbss)
- *(.bss .bss.* .gnu.linkonce.b.*)
- *(COMMON)
- *(.ram.b)
- . = ALIGN (8);
- _end = .;
- __end = .;
- } >ram AT>rom
- /* __cs3_region_end_ram is deprecated */
- __cs3_region_end_ram = __cs3_region_start_ram + LENGTH(ram);
- __cs3_region_size_ram = LENGTH(ram);
- __cs3_region_init_ram = LOADADDR (.data);
- __cs3_region_init_size_ram = _edata - ADDR (.data);
- __cs3_region_zero_size_ram = _end - _edata;
- __cs3_region_num = 1;
-
- .stab 0 (NOLOAD) : { *(.stab) }
- .stabstr 0 (NOLOAD) : { *(.stabstr) }
- /* DWARF debug sections.
- * Symbols in the DWARF debugging sections are relative to the beginning
- * of the section so we begin them at 0. */
- /* DWARF 1 */
- .debug 0 : { *(.debug) }
- .line 0 : { *(.line) }
- /* GNU DWARF 1 extensions */
- .debug_srcinfo 0 : { *(.debug_srcinfo) }
- .debug_sfnames 0 : { *(.debug_sfnames) }
- /* DWARF 1.1 and DWARF 2 */
- .debug_aranges 0 : { *(.debug_aranges) }
- .debug_pubnames 0 : { *(.debug_pubnames) }
- /* DWARF 2 */
- .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) }
- .debug_abbrev 0 : { *(.debug_abbrev) }
- .debug_line 0 : { *(.debug_line) }
- .debug_frame 0 : { *(.debug_frame) }
- .debug_str 0 : { *(.debug_str) }
- .debug_loc 0 : { *(.debug_loc) }
- .debug_macinfo 0 : { *(.debug_macinfo) }
- /* SGI/MIPS DWARF 2 extensions */
- .debug_weaknames 0 : { *(.debug_weaknames) }
- .debug_funcnames 0 : { *(.debug_funcnames) }
- .debug_typenames 0 : { *(.debug_typenames) }
- .debug_varnames 0 : { *(.debug_varnames) }
-
- .note.gnu.arm.ident 0 : { KEEP (*(.note.gnu.arm.ident)) }
- .ARM.attributes 0 : { KEEP (*(.ARM.attributes)) }
- /DISCARD/ : { *(.note.GNU-stack) }
-}
+/*
+ * Define the rest of the sections
+ */
+INCLUDE common_rom.inc
diff --git a/support/ld/maple_mini/jtag.ld b/support/ld/maple_mini/jtag.ld index 435e3f0..31768ed 100644 --- a/support/ld/maple_mini/jtag.ld +++ b/support/ld/maple_mini/jtag.ld @@ -1,186 +1,28 @@ -/* Linker script for STM32 (by Lanchon),
- * ROM and RAM relocated to their positions
- * as placed by Maple bootloader
- *
- * Configure target memory and included script
- * according to your application requirements. */
+/*
+ * Linker script for STM32.
+ * Maple mini linker script bare metal target linker script.
+ */
-/* Define memory spaces. */
+/*
+ * Define memory spaces.
+ */
MEMORY
{
ram (rwx) : ORIGIN = 0x20000000, LENGTH = 20K
rom (rx) : ORIGIN = 0x08000000, LENGTH = 128K
}
-OUTPUT_FORMAT ("elf32-littlearm", "elf32-bigarm", "elf32-littlearm")
-ENTRY(_start)
-SEARCH_DIR(.)
-/* GROUP(-lgcc -lc -lcs3 -lcs3unhosted -lcs3-lanchon-stm32) */
-GROUP(libgcc.a libc.a libm.a libcs3-lanchon-stm32.a)
-
-/* These force the linker to search for particular symbols from
- * the start of the link process and thus ensure the user's
- * overrides are picked up
+/*
+ * Use medium density device vector table
*/
-EXTERN(__cs3_reset_lanchon_stm32)
-INCLUDE names.inc
-EXTERN(__cs3_interrupt_vector_lanchon_stm32)
-EXTERN(__cs3_start_c main __cs3_stack __cs3_heap_end)
-EXTERN(_start)
-
-PROVIDE(__cs3_stack = __cs3_region_start_ram + __cs3_region_size_ram);
-PROVIDE(__cs3_heap_start = _end);
-PROVIDE(__cs3_heap_end = __cs3_region_start_ram + __cs3_region_size_ram);
-
-SECTIONS
-{
- .text :
- {
- CREATE_OBJECT_SYMBOLS
- __cs3_region_start_rom = .;
- *(.cs3.region-head.rom)
- __cs3_interrupt_vector = __cs3_interrupt_vector_lanchon_stm32;
- *(.cs3.interrupt_vector)
- /* Make sure we pulled in an interrupt vector. */
- ASSERT (. != __cs3_interrupt_vector_lanchon_stm32, "No interrupt vector");
- *(.rom)
- *(.rom.b)
-
- PROVIDE(__cs3_reset_lanchon_stm32 = _start);
- __cs3_reset = __cs3_reset_lanchon_stm32;
- *(.cs3.reset)
-
- *(.text .text.* .gnu.linkonce.t.*)
- *(.plt)
- *(.gnu.warning)
- *(.glue_7t) *(.glue_7) *(.vfp11_veneer)
-
- *(.rodata .rodata.* .gnu.linkonce.r.*)
-
- *(.ARM.extab* .gnu.linkonce.armextab.*)
- *(.gcc_except_table)
- *(.eh_frame_hdr)
- *(.eh_frame)
-
- . = ALIGN(4);
- KEEP(*(.init))
+GROUP(libcs3_stm32_med_density.a)
- . = ALIGN(4);
- __preinit_array_start = .;
- KEEP (*(.preinit_array))
- __preinit_array_end = .;
+REGION_ALIAS("REGION_TEXT", rom);
+REGION_ALIAS("REGION_DATA", ram);
+REGION_ALIAS("REGION_BSS", ram);
- . = ALIGN(4);
- __init_array_start = .;
- KEEP (*(SORT(.init_array.*)))
- KEEP (*(.init_array))
- __init_array_end = .;
-
- . = ALIGN(0x4);
- KEEP (*crtbegin.o(.ctors))
- KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors))
- KEEP (*(SORT(.ctors.*)))
- KEEP (*crtend.o(.ctors))
-
- . = ALIGN(4);
- KEEP(*(.fini))
-
- . = ALIGN(4);
- __fini_array_start = .;
- KEEP (*(.fini_array))
- KEEP (*(SORT(.fini_array.*)))
- __fini_array_end = .;
-
- KEEP (*crtbegin.o(.dtors))
- KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors))
- KEEP (*(SORT(.dtors.*)))
- KEEP (*crtend.o(.dtors))
-
- . = ALIGN(4);
- __cs3_regions = .;
- LONG (0)
- LONG (__cs3_region_init_ram)
- LONG (__cs3_region_start_ram)
- LONG (__cs3_region_init_size_ram)
- LONG (__cs3_region_zero_size_ram)
- } >rom
-
- /* .ARM.exidx is sorted, so has to go in its own output section. */
- __exidx_start = .;
- .ARM.exidx :
- {
- *(.ARM.exidx* .gnu.linkonce.armexidx.*)
- } >rom
- __exidx_end = .;
- .text.align :
- {
- . = ALIGN(8);
- _etext = .;
- } >rom
- /* __cs3_region_end_rom is deprecated */
- __cs3_region_end_rom = __cs3_region_start_rom + LENGTH(rom);
- __cs3_region_size_rom = LENGTH(rom);
- __cs3_region_num = 1;
-
- .data :
- {
- __cs3_region_start_ram = .;
- *(.cs3.region-head.ram)
- KEEP(*(.jcr))
- *(.got.plt) *(.got)
- *(.shdata)
- *(.data .data.* .gnu.linkonce.d.*)
- *(.ram)
- . = ALIGN (8);
- _edata = .;
- } >ram AT>rom
- .bss :
- {
- *(.shbss)
- *(.bss .bss.* .gnu.linkonce.b.*)
- *(COMMON)
- *(.ram.b)
- . = ALIGN (8);
- _end = .;
- __end = .;
- } >ram AT>rom
- /* __cs3_region_end_ram is deprecated */
- __cs3_region_end_ram = __cs3_region_start_ram + LENGTH(ram);
- __cs3_region_size_ram = LENGTH(ram);
- __cs3_region_init_ram = LOADADDR (.data);
- __cs3_region_init_size_ram = _edata - ADDR (.data);
- __cs3_region_zero_size_ram = _end - _edata;
- __cs3_region_num = 1;
-
- .stab 0 (NOLOAD) : { *(.stab) }
- .stabstr 0 (NOLOAD) : { *(.stabstr) }
- /* DWARF debug sections.
- * Symbols in the DWARF debugging sections are relative to the beginning
- * of the section so we begin them at 0. */
- /* DWARF 1 */
- .debug 0 : { *(.debug) }
- .line 0 : { *(.line) }
- /* GNU DWARF 1 extensions */
- .debug_srcinfo 0 : { *(.debug_srcinfo) }
- .debug_sfnames 0 : { *(.debug_sfnames) }
- /* DWARF 1.1 and DWARF 2 */
- .debug_aranges 0 : { *(.debug_aranges) }
- .debug_pubnames 0 : { *(.debug_pubnames) }
- /* DWARF 2 */
- .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) }
- .debug_abbrev 0 : { *(.debug_abbrev) }
- .debug_line 0 : { *(.debug_line) }
- .debug_frame 0 : { *(.debug_frame) }
- .debug_str 0 : { *(.debug_str) }
- .debug_loc 0 : { *(.debug_loc) }
- .debug_macinfo 0 : { *(.debug_macinfo) }
- /* SGI/MIPS DWARF 2 extensions */
- .debug_weaknames 0 : { *(.debug_weaknames) }
- .debug_funcnames 0 : { *(.debug_funcnames) }
- .debug_typenames 0 : { *(.debug_typenames) }
- .debug_varnames 0 : { *(.debug_varnames) }
+/*
+ * Define the rest of the sections
+ */
+INCLUDE common_rom.inc
- .note.gnu.arm.ident 0 : { KEEP (*(.note.gnu.arm.ident)) }
- .ARM.attributes 0 : { KEEP (*(.ARM.attributes)) }
- /DISCARD/ : { *(.note.GNU-stack) }
-}
diff --git a/support/ld/maple_mini/ram.ld b/support/ld/maple_mini/ram.ld index 1fbecc5..7dd7ee5 100644 --- a/support/ld/maple_mini/ram.ld +++ b/support/ld/maple_mini/ram.ld @@ -1,220 +1,27 @@ -/* Linker script for STM32 (by Lanchon with Mods by LeafLabs)
- *
- * Version:Sourcery G++ 4.2-84
- * BugURL:https://support.codesourcery.com/GNUToolchain/
- *
- * Copyright 2007 CodeSourcery.
- *
- * The authors hereby grant permission to use, copy, modify, distribute,
- * and license this software and its documentation for any purpose, provided
- * that existing copyright notices are retained in all copies and that this
- * notice is included verbatim in any distributions. No written agreement,
- * license, or royalty fee is required for any of the authorized uses.
- * Modifications to this software may be copyrighted by their authors
- * and need not follow the licensing terms described here, provided that
- * the new terms are clearly indicated on the first page of each file where
- * they apply. */
-
-/* Linker script for STM32 (by Lanchon),
- * ROM and RAM relocated to their positions
- * as placed by Maple bootloader
- *
- * Configure target memory and included script
- * according to your application requirements. */
+/*
+ * Linker script for STM32.
+ * Maple mini ram target linker script.
+ */
-/* Define memory spaces. */
+/*
+ * Define memory spaces.
+ */
MEMORY
{
ram (rwx) : ORIGIN = 0x20000C00, LENGTH = 17K
rom (rx) : ORIGIN = 0x08005000, LENGTH = 0K
}
-
-OUTPUT_FORMAT ("elf32-littlearm", "elf32-bigarm", "elf32-littlearm")
-ENTRY(_start)
-SEARCH_DIR(.)
-/* GROUP(-lgcc -lc -lcs3 -lcs3unhosted -lcs3-lanchon-stm32) */
-GROUP(libgcc.a libc.a libm.a libcs3-lanchon-stm32.a)
-
-/* These force the linker to search for particular symbols from
- * the start of the link process and thus ensure the user's
- * overrides are picked up
+/*
+ * Use medium density device vector table
*/
-EXTERN(__cs3_reset_lanchon_stm32)
-INCLUDE names.inc
-EXTERN(__cs3_interrupt_vector_lanchon_stm32)
-EXTERN(__cs3_start_c main __cs3_stack __cs3_heap_end)
-EXTERN(_start)
-
-PROVIDE(__cs3_stack = __cs3_region_start_ram + __cs3_region_size_ram);
-PROVIDE(__cs3_heap_start = _end);
-PROVIDE(__cs3_heap_end = __cs3_region_start_ram + __cs3_region_size_ram);
-
-SECTIONS
-{
- .text :
- {
- CREATE_OBJECT_SYMBOLS
- __cs3_region_start_ram = .;
- *(.cs3.region-head.ram)
- __cs3_interrupt_vector = __cs3_interrupt_vector_lanchon_stm32;
- *(.cs3.interrupt_vector)
- /* Make sure we pulled in an interrupt vector. */
- ASSERT (. != __cs3_interrupt_vector_lanchon_stm32, "No interrupt vector");
-
- PROVIDE(__cs3_reset_lanchon_stm32 = _start);
- __cs3_reset = __cs3_reset_lanchon_stm32;
- *(.cs3.reset)
-
- *(.text .text.* .gnu.linkonce.t.*)
- *(.plt)
- *(.gnu.warning)
- *(.glue_7t) *(.glue_7) *(.vfp11_veneer)
-
- *(.rodata .rodata.* .gnu.linkonce.r.*)
-
- *(.ARM.extab* .gnu.linkonce.armextab.*)
- *(.gcc_except_table)
- *(.eh_frame_hdr)
- *(.eh_frame)
-
- . = ALIGN(4);
- KEEP(*(.init))
-
- . = ALIGN(4);
- __preinit_array_start = .;
- KEEP (*(.preinit_array))
- __preinit_array_end = .;
+GROUP(libcs3_stm32_med_density.a)
- . = ALIGN(4);
- __init_array_start = .;
- KEEP (*(SORT(.init_array.*)))
- KEEP (*(.init_array))
- __init_array_end = .;
-
- . = ALIGN(0x4);
- KEEP (*crtbegin.o(.ctors))
- KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors))
- KEEP (*(SORT(.ctors.*)))
- KEEP (*crtend.o(.ctors))
-
- . = ALIGN(4);
- KEEP(*(.fini))
-
- . = ALIGN(4);
- __fini_array_start = .;
- KEEP (*(.fini_array))
- KEEP (*(SORT(.fini_array.*)))
- __fini_array_end = .;
-
- KEEP (*crtbegin.o(.dtors))
- KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors))
- KEEP (*(SORT(.dtors.*)))
- KEEP (*crtend.o(.dtors))
-
- . = ALIGN(4);
- __cs3_regions = .;
- LONG (0)
- LONG (__cs3_region_init_ram)
- LONG (__cs3_region_start_ram)
- LONG (__cs3_region_init_size_ram)
- LONG (__cs3_region_zero_size_ram)
- } >ram
-
- /* .ARM.exidx is sorted, so has to go in its own output section. */
- /* even cs3.rom is in ram since its running as user code under the Maple
- bootloader */
- __exidx_start = .;
- .ARM.exidx :
- {
- *(.ARM.exidx* .gnu.linkonce.armexidx.*)
- } >ram
- __exidx_end = .;
- .text.align :
- {
- . = ALIGN(8);
- _etext = .;
- } >ram
-
- .cs3.rom :
- {
- __cs3_region_start_rom = .;
- *(.cs3.region-head.rom)
- *(.rom)
- . = ALIGN (8);
- } >ram
-
- .cs3.rom.bss :
- {
- *(.rom.b)
- . = ALIGN (8);
- } >ram
- /* __cs3_region_end_rom is deprecated */
- __cs3_region_end_rom = __cs3_region_start_rom + LENGTH(ram);
- __cs3_region_size_rom = LENGTH(ram);
- __cs3_region_init_rom = LOADADDR (.cs3.rom);
- __cs3_region_init_size_rom = SIZEOF(.cs3.rom);
- __cs3_region_zero_size_rom = SIZEOF(.cs3.rom.bss);
-
- .data :
- {
-
- KEEP(*(.jcr))
- *(.got.plt) *(.got)
- *(.shdata)
- *(.data .data.* .gnu.linkonce.d.*)
- *(.ram)
- . = ALIGN (8);
- _edata = .;
- } >ram
- .bss :
- {
- *(.shbss)
- *(.bss .bss.* .gnu.linkonce.b.*)
- *(COMMON)
- *(.ram.b)
- . = ALIGN (8);
- _end = .;
- __end = .;
- } >ram
- /* __cs3_region_end_ram is deprecated */
- __cs3_region_end_ram = __cs3_region_start_ram + LENGTH(ram);
- __cs3_region_size_ram = LENGTH(ram);
- __cs3_region_init_ram = LOADADDR (.text);
- __cs3_region_init_size_ram = _edata - ADDR (.text);
- __cs3_region_zero_size_ram = _end - _edata;
- __cs3_region_num = 1;
-
- .stab 0 (NOLOAD) : { *(.stab) }
- .stabstr 0 (NOLOAD) : { *(.stabstr) }
- /* DWARF debug sections.
- * Symbols in the DWARF debugging sections are relative to the beginning
- * of the section so we begin them at 0. */
- /* DWARF 1 */
- .debug 0 : { *(.debug) }
- .line 0 : { *(.line) }
- /* GNU DWARF 1 extensions */
- .debug_srcinfo 0 : { *(.debug_srcinfo) }
- .debug_sfnames 0 : { *(.debug_sfnames) }
- /* DWARF 1.1 and DWARF 2 */
- .debug_aranges 0 : { *(.debug_aranges) }
- .debug_pubnames 0 : { *(.debug_pubnames) }
- /* DWARF 2 */
- .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) }
- .debug_abbrev 0 : { *(.debug_abbrev) }
- .debug_line 0 : { *(.debug_line) }
- .debug_frame 0 : { *(.debug_frame) }
- .debug_str 0 : { *(.debug_str) }
- .debug_loc 0 : { *(.debug_loc) }
- .debug_macinfo 0 : { *(.debug_macinfo) }
- /* SGI/MIPS DWARF 2 extensions */
- .debug_weaknames 0 : { *(.debug_weaknames) }
- .debug_funcnames 0 : { *(.debug_funcnames) }
- .debug_typenames 0 : { *(.debug_typenames) }
- .debug_varnames 0 : { *(.debug_varnames) }
-
- .note.gnu.arm.ident 0 : { KEEP (*(.note.gnu.arm.ident)) }
- .ARM.attributes 0 : { KEEP (*(.ARM.attributes)) }
- /DISCARD/ : { *(.note.GNU-stack) }
-}
+REGION_ALIAS("REGION_TEXT", ram);
+REGION_ALIAS("REGION_DATA", ram);
+REGION_ALIAS("REGION_BSS", ram);
+/*
+ * Define the rest of the sections
+ */
+INCLUDE common_ram.inc
diff --git a/support/ld/maple_native/flash.ld b/support/ld/maple_native/flash.ld index 4e820d2..4358419 100644 --- a/support/ld/maple_native/flash.ld +++ b/support/ld/maple_native/flash.ld @@ -1,211 +1,22 @@ -/* Linker script for STM32 (by Lanchon with Mods by LeafLabs)
- *
- * Version:Sourcery G++ 4.2-84
- * BugURL:https://support.codesourcery.com/GNUToolchain/
- *
- * Copyright 2007 CodeSourcery.
- *
- * The authors hereby grant permission to use, copy, modify, distribute,
- * and license this software and its documentation for any purpose, provided
- * that existing copyright notices are retained in all copies and that this
- * notice is included verbatim in any distributions. No written agreement,
- * license, or royalty fee is required for any of the authorized uses.
- * Modifications to this software may be copyrighted by their authors
- * and need not follow the licensing terms described here, provided that
- * the new terms are clearly indicated on the first page of each file where
- * they apply. */
-
-/* Linker script for STM32 (by Lanchon),
- * ROM and RAM relocated to their positions
- * as placed by Maple bootloader
- *
- * Configure target memory and included script
- * according to your application requirements. */
+/*
+ * Linker script for STM32.
+ * STM32 high density chip linker script. Loads to flash with Maple bootloader
+ */
-/* Define memory spaces. */
MEMORY
{
ram (rwx) : ORIGIN = 0x20000C00, LENGTH = 61K
rom (rx) : ORIGIN = 0x08005000, LENGTH = 492K
}
-OUTPUT_FORMAT ("elf32-littlearm", "elf32-bigarm", "elf32-littlearm")
-ENTRY(_start)
-SEARCH_DIR(.)
-/* GROUP(-lgcc -lc -lcs3 -lcs3unhosted -lcs3-lanchon-stm32) */
-GROUP(libgcc.a libc.a libm.a libcs3-lanchon-stm32.a)
-
-/* These force the linker to search for particular symbols from
- * the start of the link process and thus ensure the user's
- * overrides are picked up
+/*
+ * Use high density device vector table
*/
-EXTERN(__cs3_reset_lanchon_stm32)
-INCLUDE names.inc
-EXTERN(__cs3_interrupt_vector_lanchon_stm32)
-EXTERN(__cs3_start_c main __cs3_stack __cs3_heap_end)
-EXTERN(_start)
-
-PROVIDE(__cs3_stack = __cs3_region_start_ram + __cs3_region_size_ram);
-PROVIDE(__cs3_heap_start = _end);
-PROVIDE(__cs3_heap_end = __cs3_region_start_ram + __cs3_region_size_ram);
-
-SECTIONS
-{
- .text :
- {
- CREATE_OBJECT_SYMBOLS
- __cs3_region_start_rom = .;
- *(.cs3.region-head.rom)
- __cs3_interrupt_vector = __cs3_interrupt_vector_lanchon_stm32;
- *(.cs3.interrupt_vector)
- /* Make sure we pulled in an interrupt vector. */
- ASSERT (. != __cs3_interrupt_vector_lanchon_stm32, "No interrupt vector");
- *(.rom)
- *(.rom.b)
-
- PROVIDE(__cs3_reset_lanchon_stm32 = _start);
- __cs3_reset = __cs3_reset_lanchon_stm32;
- *(.cs3.reset)
-
- *(.text .text.* .gnu.linkonce.t.*)
- *(.plt)
- *(.gnu.warning)
- *(.glue_7t) *(.glue_7) *(.vfp11_veneer)
-
- *(.rodata .rodata.* .gnu.linkonce.r.*)
-
- *(.ARM.extab* .gnu.linkonce.armextab.*)
- *(.gcc_except_table)
- *(.eh_frame_hdr)
- *(.eh_frame)
-
- . = ALIGN(4);
- KEEP(*(.init))
-
- . = ALIGN(4);
- __preinit_array_start = .;
- KEEP (*(.preinit_array))
- __preinit_array_end = .;
+GROUP(libcs3_stm32_high_density.a)
- . = ALIGN(4);
- __init_array_start = .;
- KEEP (*(SORT(.init_array.*)))
- KEEP (*(.init_array))
- __init_array_end = .;
+REGION_ALIAS("REGION_TEXT", rom);
+REGION_ALIAS("REGION_DATA", ram);
+REGION_ALIAS("REGION_BSS", ram);
- . = ALIGN(0x4);
- KEEP (*crtbegin.o(.ctors))
- KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors))
- KEEP (*(SORT(.ctors.*)))
- KEEP (*crtend.o(.ctors))
+INCLUDE common_rom.inc
- . = ALIGN(4);
- KEEP(*(.fini))
-
- . = ALIGN(4);
- __fini_array_start = .;
- KEEP (*(.fini_array))
- KEEP (*(SORT(.fini_array.*)))
- __fini_array_end = .;
-
- KEEP (*crtbegin.o(.dtors))
- KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors))
- KEEP (*(SORT(.dtors.*)))
- KEEP (*crtend.o(.dtors))
-
- . = ALIGN(4);
- __cs3_regions = .;
- LONG (0)
- LONG (__cs3_region_init_ram)
- LONG (__cs3_region_start_ram)
- LONG (__cs3_region_init_size_ram)
- LONG (__cs3_region_zero_size_ram)
- } >rom
-
- /* .ARM.exidx is sorted, so has to go in its own output section. */
- __exidx_start = .;
- .ARM.exidx :
- {
- *(.ARM.exidx* .gnu.linkonce.armexidx.*)
- } >rom
- __exidx_end = .;
- .text.align :
- {
- . = ALIGN(8);
- _etext = .;
- } >rom
-
-/* expose a custom rom only section */
- .USER_FLASH :
- {
- *(.USER_FLASH)
- } >rom
-
-
- /* __cs3_region_end_rom is deprecated */
- __cs3_region_end_rom = __cs3_region_start_rom + LENGTH(rom);
- __cs3_region_size_rom = LENGTH(rom);
- __cs3_region_num = 1;
-
- .data :
- {
- __cs3_region_start_ram = .;
- *(.cs3.region-head.ram)
- KEEP(*(.jcr))
- *(.got.plt) *(.got)
- *(.shdata)
- *(.data .data.* .gnu.linkonce.d.*)
- *(.ram)
- . = ALIGN (8);
- _edata = .;
- } >ram AT>rom
- .bss :
- {
- *(.shbss)
- *(.bss .bss.* .gnu.linkonce.b.*)
- *(COMMON)
- *(.ram.b)
- . = ALIGN (8);
- _end = .;
- __end = .;
- } >ram AT>rom
- /* __cs3_region_end_ram is deprecated */
- __cs3_region_end_ram = __cs3_region_start_ram + LENGTH(ram);
- __cs3_region_size_ram = LENGTH(ram);
- __cs3_region_init_ram = LOADADDR (.data);
- __cs3_region_init_size_ram = _edata - ADDR (.data);
- __cs3_region_zero_size_ram = _end - _edata;
- __cs3_region_num = 1;
-
- .stab 0 (NOLOAD) : { *(.stab) }
- .stabstr 0 (NOLOAD) : { *(.stabstr) }
- /* DWARF debug sections.
- * Symbols in the DWARF debugging sections are relative to the beginning
- * of the section so we begin them at 0. */
- /* DWARF 1 */
- .debug 0 : { *(.debug) }
- .line 0 : { *(.line) }
- /* GNU DWARF 1 extensions */
- .debug_srcinfo 0 : { *(.debug_srcinfo) }
- .debug_sfnames 0 : { *(.debug_sfnames) }
- /* DWARF 1.1 and DWARF 2 */
- .debug_aranges 0 : { *(.debug_aranges) }
- .debug_pubnames 0 : { *(.debug_pubnames) }
- /* DWARF 2 */
- .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) }
- .debug_abbrev 0 : { *(.debug_abbrev) }
- .debug_line 0 : { *(.debug_line) }
- .debug_frame 0 : { *(.debug_frame) }
- .debug_str 0 : { *(.debug_str) }
- .debug_loc 0 : { *(.debug_loc) }
- .debug_macinfo 0 : { *(.debug_macinfo) }
- /* SGI/MIPS DWARF 2 extensions */
- .debug_weaknames 0 : { *(.debug_weaknames) }
- .debug_funcnames 0 : { *(.debug_funcnames) }
- .debug_typenames 0 : { *(.debug_typenames) }
- .debug_varnames 0 : { *(.debug_varnames) }
-
- .note.gnu.arm.ident 0 : { KEEP (*(.note.gnu.arm.ident)) }
- .ARM.attributes 0 : { KEEP (*(.ARM.attributes)) }
- /DISCARD/ : { *(.note.GNU-stack) }
-}
diff --git a/support/ld/maple_native/jtag.ld b/support/ld/maple_native/jtag.ld index 90a0a3f..0e99c3b 100644 --- a/support/ld/maple_native/jtag.ld +++ b/support/ld/maple_native/jtag.ld @@ -1,186 +1,21 @@ -/* Linker script for STM32 (by Lanchon),
- * ROM and RAM relocated to their positions
- * as placed by Maple bootloader
- *
- * Configure target memory and included script
- * according to your application requirements. */
-
-/* Define memory spaces. */
+/*
+ * Linker script for STM32.
+ * STM32 high density chip linker script. Bare metal linker script.
+ */
MEMORY
{
ram (rwx) : ORIGIN = 0x20000000, LENGTH = 64K
rom (rx) : ORIGIN = 0x08000000, LENGTH = 512K
}
-OUTPUT_FORMAT ("elf32-littlearm", "elf32-bigarm", "elf32-littlearm")
-ENTRY(_start)
-SEARCH_DIR(.)
-/* GROUP(-lgcc -lc -lcs3 -lcs3unhosted -lcs3-lanchon-stm32) */
-GROUP(libgcc.a libc.a libm.a libcs3-lanchon-stm32.a)
-
-/* These force the linker to search for particular symbols from
- * the start of the link process and thus ensure the user's
- * overrides are picked up
+/*
+ * Use high density device vector table
*/
-EXTERN(__cs3_reset_lanchon_stm32)
-INCLUDE names.inc
-EXTERN(__cs3_interrupt_vector_lanchon_stm32)
-EXTERN(__cs3_start_c main __cs3_stack __cs3_heap_end)
-EXTERN(_start)
-
-PROVIDE(__cs3_stack = __cs3_region_start_ram + __cs3_region_size_ram);
-PROVIDE(__cs3_heap_start = _end);
-PROVIDE(__cs3_heap_end = __cs3_region_start_ram + __cs3_region_size_ram);
-
-SECTIONS
-{
- .text :
- {
- CREATE_OBJECT_SYMBOLS
- __cs3_region_start_rom = .;
- *(.cs3.region-head.rom)
- __cs3_interrupt_vector = __cs3_interrupt_vector_lanchon_stm32;
- *(.cs3.interrupt_vector)
- /* Make sure we pulled in an interrupt vector. */
- ASSERT (. != __cs3_interrupt_vector_lanchon_stm32, "No interrupt vector");
- *(.rom)
- *(.rom.b)
-
- PROVIDE(__cs3_reset_lanchon_stm32 = _start);
- __cs3_reset = __cs3_reset_lanchon_stm32;
- *(.cs3.reset)
-
- *(.text .text.* .gnu.linkonce.t.*)
- *(.plt)
- *(.gnu.warning)
- *(.glue_7t) *(.glue_7) *(.vfp11_veneer)
-
- *(.rodata .rodata.* .gnu.linkonce.r.*)
-
- *(.ARM.extab* .gnu.linkonce.armextab.*)
- *(.gcc_except_table)
- *(.eh_frame_hdr)
- *(.eh_frame)
+GROUP(libcs3_stm32_high_density.a)
- . = ALIGN(4);
- KEEP(*(.init))
+REGION_ALIAS("REGION_TEXT", rom);
+REGION_ALIAS("REGION_DATA", ram);
+REGION_ALIAS("REGION_BSS", ram);
- . = ALIGN(4);
- __preinit_array_start = .;
- KEEP (*(.preinit_array))
- __preinit_array_end = .;
+INCLUDE common_rom.inc
- . = ALIGN(4);
- __init_array_start = .;
- KEEP (*(SORT(.init_array.*)))
- KEEP (*(.init_array))
- __init_array_end = .;
-
- . = ALIGN(0x4);
- KEEP (*crtbegin.o(.ctors))
- KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors))
- KEEP (*(SORT(.ctors.*)))
- KEEP (*crtend.o(.ctors))
-
- . = ALIGN(4);
- KEEP(*(.fini))
-
- . = ALIGN(4);
- __fini_array_start = .;
- KEEP (*(.fini_array))
- KEEP (*(SORT(.fini_array.*)))
- __fini_array_end = .;
-
- KEEP (*crtbegin.o(.dtors))
- KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors))
- KEEP (*(SORT(.dtors.*)))
- KEEP (*crtend.o(.dtors))
-
- . = ALIGN(4);
- __cs3_regions = .;
- LONG (0)
- LONG (__cs3_region_init_ram)
- LONG (__cs3_region_start_ram)
- LONG (__cs3_region_init_size_ram)
- LONG (__cs3_region_zero_size_ram)
- } >rom
-
- /* .ARM.exidx is sorted, so has to go in its own output section. */
- __exidx_start = .;
- .ARM.exidx :
- {
- *(.ARM.exidx* .gnu.linkonce.armexidx.*)
- } >rom
- __exidx_end = .;
- .text.align :
- {
- . = ALIGN(8);
- _etext = .;
- } >rom
- /* __cs3_region_end_rom is deprecated */
- __cs3_region_end_rom = __cs3_region_start_rom + LENGTH(rom);
- __cs3_region_size_rom = LENGTH(rom);
- __cs3_region_num = 1;
-
- .data :
- {
- __cs3_region_start_ram = .;
- *(.cs3.region-head.ram)
- KEEP(*(.jcr))
- *(.got.plt) *(.got)
- *(.shdata)
- *(.data .data.* .gnu.linkonce.d.*)
- *(.ram)
- . = ALIGN (8);
- _edata = .;
- } >ram AT>rom
- .bss :
- {
- *(.shbss)
- *(.bss .bss.* .gnu.linkonce.b.*)
- *(COMMON)
- *(.ram.b)
- . = ALIGN (8);
- _end = .;
- __end = .;
- } >ram AT>rom
- /* __cs3_region_end_ram is deprecated */
- __cs3_region_end_ram = __cs3_region_start_ram + LENGTH(ram);
- __cs3_region_size_ram = LENGTH(ram);
- __cs3_region_init_ram = LOADADDR (.data);
- __cs3_region_init_size_ram = _edata - ADDR (.data);
- __cs3_region_zero_size_ram = _end - _edata;
- __cs3_region_num = 1;
-
- .stab 0 (NOLOAD) : { *(.stab) }
- .stabstr 0 (NOLOAD) : { *(.stabstr) }
- /* DWARF debug sections.
- * Symbols in the DWARF debugging sections are relative to the beginning
- * of the section so we begin them at 0. */
- /* DWARF 1 */
- .debug 0 : { *(.debug) }
- .line 0 : { *(.line) }
- /* GNU DWARF 1 extensions */
- .debug_srcinfo 0 : { *(.debug_srcinfo) }
- .debug_sfnames 0 : { *(.debug_sfnames) }
- /* DWARF 1.1 and DWARF 2 */
- .debug_aranges 0 : { *(.debug_aranges) }
- .debug_pubnames 0 : { *(.debug_pubnames) }
- /* DWARF 2 */
- .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) }
- .debug_abbrev 0 : { *(.debug_abbrev) }
- .debug_line 0 : { *(.debug_line) }
- .debug_frame 0 : { *(.debug_frame) }
- .debug_str 0 : { *(.debug_str) }
- .debug_loc 0 : { *(.debug_loc) }
- .debug_macinfo 0 : { *(.debug_macinfo) }
- /* SGI/MIPS DWARF 2 extensions */
- .debug_weaknames 0 : { *(.debug_weaknames) }
- .debug_funcnames 0 : { *(.debug_funcnames) }
- .debug_typenames 0 : { *(.debug_typenames) }
- .debug_varnames 0 : { *(.debug_varnames) }
-
- .note.gnu.arm.ident 0 : { KEEP (*(.note.gnu.arm.ident)) }
- .ARM.attributes 0 : { KEEP (*(.ARM.attributes)) }
- /DISCARD/ : { *(.note.GNU-stack) }
-}
diff --git a/support/ld/maple_native/ram.ld b/support/ld/maple_native/ram.ld index a5e1482..d5bd3b0 100644 --- a/support/ld/maple_native/ram.ld +++ b/support/ld/maple_native/ram.ld @@ -1,220 +1,21 @@ -/* Linker script for STM32 (by Lanchon with Mods by LeafLabs)
- *
- * Version:Sourcery G++ 4.2-84
- * BugURL:https://support.codesourcery.com/GNUToolchain/
- *
- * Copyright 2007 CodeSourcery.
- *
- * The authors hereby grant permission to use, copy, modify, distribute,
- * and license this software and its documentation for any purpose, provided
- * that existing copyright notices are retained in all copies and that this
- * notice is included verbatim in any distributions. No written agreement,
- * license, or royalty fee is required for any of the authorized uses.
- * Modifications to this software may be copyrighted by their authors
- * and need not follow the licensing terms described here, provided that
- * the new terms are clearly indicated on the first page of each file where
- * they apply. */
-
-/* Linker script for STM32 (by Lanchon),
- * ROM and RAM relocated to their positions
- * as placed by Maple bootloader
- *
- * Configure target memory and included script
- * according to your application requirements. */
+/*
+ * Linker script for STM32.
+ * STM32 high density chip linker script. Loads to RAM with Maple bootloader
+ */
-/* Define memory spaces. */
MEMORY
{
ram (rwx) : ORIGIN = 0x20000C00, LENGTH = 61K
rom (rx) : ORIGIN = 0x08005000, LENGTH = 0K
}
-
-OUTPUT_FORMAT ("elf32-littlearm", "elf32-bigarm", "elf32-littlearm")
-ENTRY(_start)
-SEARCH_DIR(.)
-/* GROUP(-lgcc -lc -lcs3 -lcs3unhosted -lcs3-lanchon-stm32) */
-GROUP(libgcc.a libc.a libm.a libcs3-lanchon-stm32.a)
-
-/* These force the linker to search for particular symbols from
- * the start of the link process and thus ensure the user's
- * overrides are picked up
+/*
+ * Use high density device vector table
*/
-EXTERN(__cs3_reset_lanchon_stm32)
-INCLUDE names.inc
-EXTERN(__cs3_interrupt_vector_lanchon_stm32)
-EXTERN(__cs3_start_c main __cs3_stack __cs3_heap_end)
-EXTERN(_start)
-
-PROVIDE(__cs3_stack = __cs3_region_start_ram + __cs3_region_size_ram);
-PROVIDE(__cs3_heap_start = _end);
-PROVIDE(__cs3_heap_end = __cs3_region_start_ram + __cs3_region_size_ram);
-
-SECTIONS
-{
- .text :
- {
- CREATE_OBJECT_SYMBOLS
- __cs3_region_start_ram = .;
- *(.cs3.region-head.ram)
- __cs3_interrupt_vector = __cs3_interrupt_vector_lanchon_stm32;
- *(.cs3.interrupt_vector)
- /* Make sure we pulled in an interrupt vector. */
- ASSERT (. != __cs3_interrupt_vector_lanchon_stm32, "No interrupt vector");
-
- PROVIDE(__cs3_reset_lanchon_stm32 = _start);
- __cs3_reset = __cs3_reset_lanchon_stm32;
- *(.cs3.reset)
-
- *(.text .text.* .gnu.linkonce.t.*)
- *(.plt)
- *(.gnu.warning)
- *(.glue_7t) *(.glue_7) *(.vfp11_veneer)
-
- *(.rodata .rodata.* .gnu.linkonce.r.*)
-
- *(.ARM.extab* .gnu.linkonce.armextab.*)
- *(.gcc_except_table)
- *(.eh_frame_hdr)
- *(.eh_frame)
-
- . = ALIGN(4);
- KEEP(*(.init))
-
- . = ALIGN(4);
- __preinit_array_start = .;
- KEEP (*(.preinit_array))
- __preinit_array_end = .;
+GROUP(libcs3_stm32_high_density.a)
- . = ALIGN(4);
- __init_array_start = .;
- KEEP (*(SORT(.init_array.*)))
- KEEP (*(.init_array))
- __init_array_end = .;
-
- . = ALIGN(0x4);
- KEEP (*crtbegin.o(.ctors))
- KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors))
- KEEP (*(SORT(.ctors.*)))
- KEEP (*crtend.o(.ctors))
-
- . = ALIGN(4);
- KEEP(*(.fini))
-
- . = ALIGN(4);
- __fini_array_start = .;
- KEEP (*(.fini_array))
- KEEP (*(SORT(.fini_array.*)))
- __fini_array_end = .;
-
- KEEP (*crtbegin.o(.dtors))
- KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors))
- KEEP (*(SORT(.dtors.*)))
- KEEP (*crtend.o(.dtors))
-
- . = ALIGN(4);
- __cs3_regions = .;
- LONG (0)
- LONG (__cs3_region_init_ram)
- LONG (__cs3_region_start_ram)
- LONG (__cs3_region_init_size_ram)
- LONG (__cs3_region_zero_size_ram)
- } >ram
-
- /* .ARM.exidx is sorted, so has to go in its own output section. */
- /* even cs3.rom is in ram since its running as user code under the Maple
- bootloader */
- __exidx_start = .;
- .ARM.exidx :
- {
- *(.ARM.exidx* .gnu.linkonce.armexidx.*)
- } >ram
- __exidx_end = .;
- .text.align :
- {
- . = ALIGN(8);
- _etext = .;
- } >ram
-
- .cs3.rom :
- {
- __cs3_region_start_rom = .;
- *(.cs3.region-head.rom)
- *(.rom)
- . = ALIGN (8);
- } >ram
-
- .cs3.rom.bss :
- {
- *(.rom.b)
- . = ALIGN (8);
- } >ram
- /* __cs3_region_end_rom is deprecated */
- __cs3_region_end_rom = __cs3_region_start_rom + LENGTH(ram);
- __cs3_region_size_rom = LENGTH(ram);
- __cs3_region_init_rom = LOADADDR (.cs3.rom);
- __cs3_region_init_size_rom = SIZEOF(.cs3.rom);
- __cs3_region_zero_size_rom = SIZEOF(.cs3.rom.bss);
-
- .data :
- {
-
- KEEP(*(.jcr))
- *(.got.plt) *(.got)
- *(.shdata)
- *(.data .data.* .gnu.linkonce.d.*)
- *(.ram)
- . = ALIGN (8);
- _edata = .;
- } >ram
- .bss :
- {
- *(.shbss)
- *(.bss .bss.* .gnu.linkonce.b.*)
- *(COMMON)
- *(.ram.b)
- . = ALIGN (8);
- _end = .;
- __end = .;
- } >ram
- /* __cs3_region_end_ram is deprecated */
- __cs3_region_end_ram = __cs3_region_start_ram + LENGTH(ram);
- __cs3_region_size_ram = LENGTH(ram);
- __cs3_region_init_ram = LOADADDR (.text);
- __cs3_region_init_size_ram = _edata - ADDR (.text);
- __cs3_region_zero_size_ram = _end - _edata;
- __cs3_region_num = 1;
-
- .stab 0 (NOLOAD) : { *(.stab) }
- .stabstr 0 (NOLOAD) : { *(.stabstr) }
- /* DWARF debug sections.
- * Symbols in the DWARF debugging sections are relative to the beginning
- * of the section so we begin them at 0. */
- /* DWARF 1 */
- .debug 0 : { *(.debug) }
- .line 0 : { *(.line) }
- /* GNU DWARF 1 extensions */
- .debug_srcinfo 0 : { *(.debug_srcinfo) }
- .debug_sfnames 0 : { *(.debug_sfnames) }
- /* DWARF 1.1 and DWARF 2 */
- .debug_aranges 0 : { *(.debug_aranges) }
- .debug_pubnames 0 : { *(.debug_pubnames) }
- /* DWARF 2 */
- .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) }
- .debug_abbrev 0 : { *(.debug_abbrev) }
- .debug_line 0 : { *(.debug_line) }
- .debug_frame 0 : { *(.debug_frame) }
- .debug_str 0 : { *(.debug_str) }
- .debug_loc 0 : { *(.debug_loc) }
- .debug_macinfo 0 : { *(.debug_macinfo) }
- /* SGI/MIPS DWARF 2 extensions */
- .debug_weaknames 0 : { *(.debug_weaknames) }
- .debug_funcnames 0 : { *(.debug_funcnames) }
- .debug_typenames 0 : { *(.debug_typenames) }
- .debug_varnames 0 : { *(.debug_varnames) }
-
- .note.gnu.arm.ident 0 : { KEEP (*(.note.gnu.arm.ident)) }
- .ARM.attributes 0 : { KEEP (*(.ARM.attributes)) }
- /DISCARD/ : { *(.note.GNU-stack) }
-}
+REGION_ALIAS("REGION_TEXT", ram);
+REGION_ALIAS("REGION_DATA", ram);
+REGION_ALIAS("REGION_BSS", ram);
+INCLUDE common_ram.inc
diff --git a/support/ld/names.inc b/support/ld/names.inc index a0e1fb2..9fab36c 100644 --- a/support/ld/names.inc +++ b/support/ld/names.inc @@ -1,61 +1,78 @@ -/* ISR names for STM32 (by Lanchon) */
+EXTERN(__cs3_stack)
+EXTERN(__cs3_reset)
+EXTERN(__exc_nmi)
+EXTERN(__exc_hardfault)
+EXTERN(__exc_memmanage)
+EXTERN(__exc_busfault)
+EXTERN(__exc_usagefault)
+EXTERN(__stm32reservedexception7)
+EXTERN(__stm32reservedexception8)
+EXTERN(__stm32reservedexception9)
+EXTERN(__stm32reservedexception10)
+EXTERN(__exc_svc)
+EXTERN(__exc_debug_monitor)
+EXTERN(__stm32reservedexception13)
+EXTERN(__exc_pendsv)
+EXTERN(__exc_systick)
-EXTERN (__cs3_stack)
-EXTERN (__cs3_reset)
-EXTERN (NMIException)
-EXTERN (HardFaultException)
-EXTERN (MemManageException)
-EXTERN (BusFaultException)
-EXTERN (UsageFaultException)
-EXTERN (__STM32ReservedException7)
-EXTERN (__STM32ReservedException8)
-EXTERN (__STM32ReservedException9)
-EXTERN (__STM32ReservedException10)
-EXTERN (SVCHandler)
-EXTERN (DebugMonitor)
-EXTERN (__STM32ReservedException13)
-EXTERN (PendSVC)
-EXTERN (SysTickHandler)
-EXTERN (WWDG_IRQHandler)
-EXTERN (PVD_IRQHandler)
-EXTERN (TAMPER_IRQHandler)
-EXTERN (RTC_IRQHandler)
-EXTERN (FLASH_IRQHandler)
-EXTERN (RCC_IRQHandler)
-EXTERN (EXTI0_IRQHandler)
-EXTERN (EXTI1_IRQHandler)
-EXTERN (EXTI2_IRQHandler)
-EXTERN (EXTI3_IRQHandler)
-EXTERN (EXTI4_IRQHandler)
-EXTERN (DMAChannel1_IRQHandler)
-EXTERN (DMAChannel2_IRQHandler)
-EXTERN (DMAChannel3_IRQHandler)
-EXTERN (DMAChannel4_IRQHandler)
-EXTERN (DMAChannel5_IRQHandler)
-EXTERN (DMAChannel6_IRQHandler)
-EXTERN (DMAChannel7_IRQHandler)
-EXTERN (ADC_IRQHandler)
-EXTERN (USB_HP_CAN_TX_IRQHandler)
-EXTERN (USB_LP_CAN_RX0_IRQHandler)
-EXTERN (CAN_RX1_IRQHandler)
-EXTERN (CAN_SCE_IRQHandler)
-EXTERN (EXTI9_5_IRQHandler)
-EXTERN (TIM1_BRK_IRQHandler)
-EXTERN (TIM1_UP_IRQHandler)
-EXTERN (TIM1_TRG_COM_IRQHandler)
-EXTERN (TIM1_CC_IRQHandler)
-EXTERN (TIM2_IRQHandler)
-EXTERN (TIM3_IRQHandler)
-EXTERN (TIM4_IRQHandler)
-EXTERN (I2C1_EV_IRQHandler)
-EXTERN (I2C1_ER_IRQHandler)
-EXTERN (I2C2_EV_IRQHandler)
-EXTERN (I2C2_ER_IRQHandler)
-EXTERN (SPI1_IRQHandler)
-EXTERN (SPI2_IRQHandler)
-EXTERN (USART1_IRQHandler)
-EXTERN (USART2_IRQHandler)
-EXTERN (USART3_IRQHandler)
-EXTERN (EXTI15_10_IRQHandler)
-EXTERN (RTCAlarm_IRQHandler)
-EXTERN (USBWakeUp_IRQHandler)
+EXTERN(__irq_wwdg)
+EXTERN(__irq_pvd)
+EXTERN(__irq_tamper)
+EXTERN(__irq_rtc)
+EXTERN(__irq_flash)
+EXTERN(__irq_rcc)
+EXTERN(__irq_exti0)
+EXTERN(__irq_exti1)
+EXTERN(__irq_exti2)
+EXTERN(__irq_exti3)
+EXTERN(__irq_exti4)
+EXTERN(__irq_dma_channel1)
+EXTERN(__irq_dma_channel2)
+EXTERN(__irq_dma_channel3)
+EXTERN(__irq_dma_channel4)
+EXTERN(__irq_dma_channel5)
+EXTERN(__irq_dma_channel6)
+EXTERN(__irq_dma_channel7)
+EXTERN(__irq_adc)
+EXTERN(__irq_usb_hp_can_tx)
+EXTERN(__irq_usb_lp_can_rx0)
+EXTERN(__irq_can_rx1)
+EXTERN(__irq_can_sce)
+EXTERN(__irq_exti9_5)
+EXTERN(__irq_tim1_brk)
+EXTERN(__irq_tim1_up)
+EXTERN(__irq_tim1_trg_com)
+EXTERN(__irq_tim1_cc)
+EXTERN(__irq_tim2)
+EXTERN(__irq_tim3)
+EXTERN(__irq_tim4)
+EXTERN(__irq_i2c1_ev)
+EXTERN(__irq_i2c1_er)
+EXTERN(__irq_i2c2_ev)
+EXTERN(__irq_i2c2_er)
+EXTERN(__irq_spi1)
+EXTERN(__irq_spi2)
+EXTERN(__irq_usart1)
+EXTERN(__irq_usart2)
+EXTERN(__irq_usart3)
+EXTERN(__irq_exti15_10)
+EXTERN(__irq_rtcalarm)
+EXTERN(__irq_usbwakeup)
+
+EXTERN(__irq_tim8_brk)
+EXTERN(__irq_tim8_up)
+EXTERN(__irq_tim8_trg_com)
+EXTERN(__irq_tim8_cc)
+EXTERN(__irq_adc3)
+EXTERN(__irq_fsmc)
+EXTERN(__irq_sdio)
+EXTERN(__irq_tim5)
+EXTERN(__irq_spi3)
+EXTERN(__irq_uart4)
+EXTERN(__irq_uart5)
+EXTERN(__irq_tim6)
+EXTERN(__irq_tim7)
+EXTERN(__irq_dma2_channel1)
+EXTERN(__irq_dma2_channel2)
+EXTERN(__irq_dma2_channel3)
+EXTERN(__irq_dma2_channel4_5)
diff --git a/support/ld/src.zip b/support/ld/src.zip Binary files differdeleted file mode 100644 index 58ff908..0000000 --- a/support/ld/src.zip +++ /dev/null diff --git a/support/scripts/copy-to-ide b/support/scripts/copy-to-ide index 301126d..674615e 100755 --- a/support/scripts/copy-to-ide +++ b/support/scripts/copy-to-ide @@ -1,7 +1,8 @@ #!/bin/sh -# This hack copies the necessary library files into the Maple IDE -# repository. +# This hack copies libmaple's source, linker scripts, and built +# documentation into the Maple IDE repository (which is expected as +# its first argument). DEST=$1 @@ -18,14 +19,20 @@ LMAPLE_SRC="LICENSE ./libmaple/usb/usb_lib/*.h ./libmaple/usb/usb_lib/*.c ./wirish/*.h - ./wirish/*.c ./wirish/main.cxx ./wirish/*.cpp ./wirish/comm/*.cpp ./wirish/comm/*.h + ./wirish/boards/*.h + ./wirish/boards/*.cpp + ./support/ld/common_ram.inc + ./support/ld/common_rom.inc + ./support/ld/libcs3_stm32_high_density.a + ./support/ld/libcs3_stm32_med_density.a ./support/ld/maple + ./support/ld/maple_mini ./support/ld/maple_native - ./support/ld/libcs3-lanchon-stm32.a + ./support/ld/maple_RET6 ./support/ld/names.inc" LMAPLE_DOCS=./docs @@ -40,21 +47,25 @@ fi # source echo Copying libmaple source -rm -rf $DEST_CORES/*.c $DEST_CORES/*.cpp $DEST_CORES/*.h $DEST_CORES/*.cxx $DEST_CORES/*.inc $DEST_CORES/*.a $DEST_CORES/*.S $DEST_CORES/maple $DEST_CORES/maple_native +rm -rf $DEST_CORES/*.c $DEST_CORES/*.cpp $DEST_CORES/*.h $DEST_CORES/*.cxx $DEST_CORES/*.S +rm -rf $DEST_CORES/*.inc $DEST_CORES/*.a $DEST_CORES/maple $DEST_CORES/maple_* cp -R $LMAPLE_SRC $DEST_CORES echo Copying over libraries cp -R libraries/* $DEST_LIBS +# libmaple version +echo Creating libmaple-version.txt +git show-ref HEAD | cut -c 1-10 > $DEST/libmaple-version.txt + # docs echo Deleting old reference directory contents rm -rf $DEST_REF/* echo Rebuilding documentation -( cd $LMAPLE_DOCS; doxygen && make clean 2>/dev/null 1>/dev/null && make html ) +( cd $LMAPLE_DOCS; doxygen >/dev/null 2>&1 && make clean >/dev/null 2>&1 && make html ) echo Copying over documentation cp -R $LMAPLE_DOCS_BUILD/* $DEST_REF - echo Done. diff --git a/support/stm32loader.py b/support/stm32loader.py index 874d278..b0df7c0 100755 --- a/support/stm32loader.py +++ b/support/stm32loader.py @@ -294,6 +294,10 @@ class CommandInterface: def writeMemory(self, addr, data): lng = len(data) + + mdebug(5, "Writing %(lng)d bytes to start address 0x%(addr)X" % + { 'lng': lng, 'addr': addr}) + if usepbar: widgets = ['Writing: ', Percentage(),' ', ETA(), ' ', Bar()] pbar = ProgressBar(widgets=widgets, maxval=lng, term_width=79).start() @@ -316,12 +320,6 @@ class CommandInterface: self.cmdWriteMemory(addr, data[offs:offs+lng] + ([0xFF] * (256-lng)) ) - - - def __init__(self) : - pass - - def usage(): print """Usage: %s [-hqVewvr] [-l length] [-p port] [-b baud] [-a addr] [file.bin] -h This help @@ -452,6 +450,7 @@ if __name__ == "__main__": 'baud':conf['baud']}) try: if (conf['write'] or conf['verify']): + mdebug(5, "Reading data from %s" % args[0]) data = read(args[0]) try: diff --git a/wirish/HardwareTimer.cpp b/wirish/HardwareTimer.cpp index 354663e..d0e32c3 100644 --- a/wirish/HardwareTimer.cpp +++ b/wirish/HardwareTimer.cpp @@ -22,205 +22,125 @@ * THE SOFTWARE. *****************************************************************************/ -/* - * wirish timer class to manage the four 16-bit timer peripherals - */ - -#include "wirish.h" #include "HardwareTimer.h" +#include "boards.h" // for CYCLES_PER_MICROSECOND +#include "wirish_math.h" -HardwareTimer::HardwareTimer(timer_dev_num timerNum) { - ASSERT(timerNum != TIMER_INVALID); +// TODO [0.1.0] Remove deprecated pieces - this->timerNum = timerNum; -} +#ifdef STM32_MEDIUM_DENSITY +#define NR_TIMERS 4 +#elif defined(STM32_HIGH_DENSITY) +#define NR_TIMERS 8 +#else +#error "Unsupported density" +#endif -void HardwareTimer::resume(void) { - timer_resume(this->timerNum); +#define MAX_RELOAD ((1 << 16) - 1) + +HardwareTimer::HardwareTimer(uint8 timerNum) { + if (timerNum > NR_TIMERS) { + ASSERT(0); + } + timer_dev *devs[] = { + TIMER1, + TIMER2, + TIMER3, + TIMER4, +#ifdef STM32_HIGH_DENSITY + TIMER5, + TIMER6, + TIMER7, + TIMER8, +#endif + }; + this->dev = devs[timerNum - 1]; } void HardwareTimer::pause(void) { - timer_pause(this->timerNum); + timer_pause(this->dev); } -uint16 HardwareTimer::getPrescaleFactor(void) { - return timer_get_prescaler(this->timerNum) + 1; +void HardwareTimer::resume(void) { + timer_resume(this->dev); +} + +uint32 HardwareTimer::getPrescaleFactor(void) { + return timer_get_prescaler(this->dev) + 1; } -void HardwareTimer::setPrescaleFactor(uint16 factor) { - // The prescaler register is zero-indexed - timer_set_prescaler(this->timerNum, factor-1); +void HardwareTimer::setPrescaleFactor(uint32 factor) { + timer_set_prescaler(this->dev, (uint16)(factor - 1)); } uint16 HardwareTimer::getOverflow() { - return timer_get_reload(this->timerNum); + return timer_get_reload(this->dev); } void HardwareTimer::setOverflow(uint16 val) { - timer_set_reload(this->timerNum, val); + timer_set_reload(this->dev, val); } uint16 HardwareTimer::getCount(void) { - return timer_get_count(this->timerNum); + return timer_get_count(this->dev); } void HardwareTimer::setCount(uint16 val) { uint16 ovf = this->getOverflow(); - timer_set_count(this->timerNum, min(val, ovf)); + timer_set_count(this->dev, min(val, ovf)); } +// FIXME [0.0.10 beta] test! uint16 HardwareTimer::setPeriod(uint32 microseconds) { // Not the best way to handle this edge case? - if(!microseconds) { - setPrescaleFactor(1); - setOverflow(1); + if (!microseconds) { + this->setPrescaleFactor(1); + this->setOverflow(1); return this->getOverflow(); } - uint32 cycles = microseconds * CYCLES_PER_MICROSECOND; - - // With a prescale factor of 1, there are CYCLES_PER_MICROSECOND - // counts/ms - uint16 ps = (uint16)((cycles >> 16) + 1); - setPrescaleFactor(ps); - - // Finally, this overflow will always be less than 65536 - setOverflow((cycles/ps) - 1); - - return this->getOverflow(); -} - -inline void HardwareTimer::setChannelMode(int channel, TimerMode mode) { - timer_set_mode(this->timerNum, channel, mode); -} - -void HardwareTimer::setChannel1Mode(TimerMode mode) { - this->setChannelMode(1, mode); -} - -void HardwareTimer::setChannel2Mode(TimerMode mode) { - this->setChannelMode(2, mode); -} - -void HardwareTimer::setChannel3Mode(TimerMode mode) { - this->setChannelMode(3, mode); -} - -void HardwareTimer::setChannel4Mode(TimerMode mode) { - this->setChannelMode(4, mode); -} - -inline uint16 HardwareTimer::getCompare(int channel) { - return timer_get_compare_value(this->timerNum, channel); -} - -uint16 HardwareTimer::getCompare1() { - return this->getCompare(1); -} -uint16 HardwareTimer::getCompare2() { - return this->getCompare(2); + uint32 period_cyc = microseconds * CYCLES_PER_MICROSECOND; + uint16 prescaler = (uint16)(period_cyc / MAX_RELOAD); + uint16 overflow = (uint16)round(period_cyc / prescaler); + this->setPrescaleFactor(prescaler); + this->setOverflow(overflow); + return overflow; } -uint16 HardwareTimer::getCompare3() { - return this->getCompare(3); +void HardwareTimer::setMode(int channel, timer_mode mode) { + timer_set_mode(this->dev, (uint8)channel, (timer_mode)mode); } -uint16 HardwareTimer::getCompare4() { - return this->getCompare(4); +uint16 HardwareTimer::getCompare(int channel) { + return timer_get_compare(this->dev, (uint8)channel); } -inline void HardwareTimer::setCompare(int channel, uint16 val) { +void HardwareTimer::setCompare(int channel, uint16 val) { uint16 ovf = this->getOverflow(); - timer_set_compare_value(this->timerNum, channel, min(val, ovf)); + timer_set_compare(this->dev, (uint8)channel, min(val, ovf)); } -void HardwareTimer::setCompare1(uint16 val) { - this->setCompare(1, val); +void HardwareTimer::attachInterrupt(int channel, voidFuncPtr handler) { + timer_attach_interrupt(this->dev, (uint8)channel, handler); } -void HardwareTimer::setCompare2(uint16 val) { - this->setCompare(2, val); +void HardwareTimer::detachInterrupt(int channel) { + timer_detach_interrupt(this->dev, (uint8)channel); } -void HardwareTimer::setCompare3(uint16 val) { - this->setCompare(3, val); +void HardwareTimer::refresh(void) { + timer_generate_update(this->dev); } -void HardwareTimer::setCompare4(uint16 val) { - this->setCompare(4, val); -} - -inline void HardwareTimer::attachInterrupt(int channel, voidFuncPtr handler) { - timer_attach_interrupt(this->timerNum, channel, handler); -} - -void HardwareTimer::attachCompare1Interrupt(voidFuncPtr handler) { - this->attachInterrupt(1, handler); -} - -void HardwareTimer::attachCompare2Interrupt(voidFuncPtr handler) { - this->attachInterrupt(2, handler); -} - -void HardwareTimer::attachCompare3Interrupt(voidFuncPtr handler) { - this->attachInterrupt(3, handler); -} - -void HardwareTimer::attachCompare4Interrupt(voidFuncPtr handler) { - this->attachInterrupt(4, handler); -} - -inline void HardwareTimer::detachInterrupt(int channel) { - timer_detach_interrupt(this->timerNum, channel); -} - -void HardwareTimer::detachCompare1Interrupt(void) { - this->detachInterrupt(1); -} - -void HardwareTimer::detachCompare2Interrupt(void) { - this->detachInterrupt(2); -} - -void HardwareTimer::detachCompare3Interrupt(void) { - this->detachInterrupt(3); -} - -void HardwareTimer::detachCompare4Interrupt(void) { - this->detachInterrupt(4); -} +/* -- Deprecated predefined instances -------------------------------------- */ -void HardwareTimer::generateUpdate(void) { - timer_generate_update(this->timerNum); -} - - -HardwareTimer Timer1(TIMER1); -HardwareTimer Timer2(TIMER2); -HardwareTimer Timer3(TIMER3); -HardwareTimer Timer4(TIMER4); -#if NR_TIMERS >= 8 -HardwareTimer Timer5(TIMER5); // High-density devices only -HardwareTimer Timer8(TIMER8); // High-density devices only +HardwareTimer Timer1(1); +HardwareTimer Timer2(2); +HardwareTimer Timer3(3); +HardwareTimer Timer4(4); +#ifdef STM32_HIGH_DENSITY +HardwareTimer Timer5(5); +HardwareTimer Timer6(6); +HardwareTimer Timer7(7); +HardwareTimer Timer8(8); #endif - -HardwareTimer* getTimer(timer_dev_num timerNum) { - switch (timerNum) { - case TIMER1: - return &Timer1; - case TIMER2: - return &Timer2; - case TIMER3: - return &Timer3; - case TIMER4: - return &Timer4; -#if NR_TIMERS >= 8 - case TIMER5: - return &Timer5; - case TIMER8: - return &Timer8; -#endif - default: - return 0; - } -} diff --git a/wirish/HardwareTimer.h b/wirish/HardwareTimer.h index 4034b1f..fd8ca9a 100644 --- a/wirish/HardwareTimer.h +++ b/wirish/HardwareTimer.h @@ -23,66 +23,45 @@ *****************************************************************************/ /** - * @brief wirish timer class to manage the four 16-bit timer peripherals + * @brief Wirish timer class. */ #ifndef _HARDWARETIMER_H_ #define _HARDWARETIMER_H_ -#include "timers.h" +// TODO [0.1.0] Remove deprecated pieces, pick a better API + +#include "timer.h" + +/** Timer mode. */ +typedef timer_mode TimerMode; + +/** @brief Deprecated; use TIMER_OUTPUT_COMPARE instead. */ +#define TIMER_OUTPUTCOMPARE TIMER_OUTPUT_COMPARE /** - * Interface to one of the 16-bit timer peripherals. - * - * User code should not instantiate this class directly; instead, use - * one of the predefined Timer<n> instances (Timer1, Timer2, etc.). - * - * HardwareTimer instances can be configured to generate periodic or - * delayed events with minimal work done by the microcontroller. Each - * timer maintains a single 16-bit count that can be configured with a - * prescaler and overflow value. - * - * By default, a timer's counter is incremented once per clock cycle. - * The prescaler acts as a divider of the 72MHz Maple system clock; - * without prescaling, the timer's count would reach 65535 (2**16-1) - * and roll over over 1000 times per second. - * - * The overflow value is the maximum value the counter will reach. It - * defaults to 65535; smaller values will cause the counter to reset - * more frequently. + * @brief Interface to one of the 16-bit timer peripherals. */ class HardwareTimer { - private: - timer_dev_num timerNum; - - public: - HardwareTimer(timer_dev_num timer_num); +private: + timer_dev *dev; +public: /** - * Return this timer's device number. For example, - * Timer1.getTimerNum() == TIMER1 + * @brief Construct a new HardwareTimer instance. + * @param timerNum number of the timer to control. */ - timer_dev_num getTimerNum() { return timerNum; } + HardwareTimer(uint8 timerNum); /** - * Stop the counter, without affecting its configuration. - * - * The timer will no longer count or fire interrupts after this - * function is called, until it is resumed. This function is - * useful during timer setup periods, in order to prevent - * interrupts from firing before the timer is fully configured. - * - * Note that there is some function call overhead associated with - * this method, so using it in concert with - * HardwareTimer::resume() is not a robust way to align multiple - * timers to the same count value. + * @brief Stop the counter, without affecting its configuration. * * @see HardwareTimer::resume() */ void pause(void); /** - * Resume a paused timer, without affecting its configuration. + * @brief Resume a paused timer, without affecting its configuration. * * The timer will resume counting and firing interrupts as * appropriate. @@ -97,66 +76,51 @@ class HardwareTimer { void resume(void); /** - * Returns the timer's prescale factor. + * @brief Get the timer's prescale factor. + * @return Timer prescaler, from 1 to 65,536. * @see HardwareTimer::setPrescaleFactor() */ - uint16 getPrescaleFactor(); + uint32 getPrescaleFactor(); /** - * Set the timer's prescale factor. + * @brief Set the timer's prescale factor. * - * The prescaler acts as a clock divider to slow down the rate at - * which the counter increments. + * The new value won't take effect until the next time the counter + * overflows. You can force the counter to reset using + * HardwareTimer::refresh(). * - * For example, the system clock rate is 72MHz, so the counter - * will reach 65535 in (13.89 nanoseconds) * (65535 counts) = - * (910.22 microseconds), or about a thousand times a second. If - * the prescaler equals 1098, then the clock rate is effectively - * 65.56KHz, and the counter will reach 65536 in (15.25 - * microseconds) * (65536 counts) = (0.999 seconds), or about once - * per second. - * - * The HardwareTimer::setPeriod() method may also be used as a - * convenient alternative. - * - * @param factor The new prescale value to set. - * @see HardwareTimer::setPeriod() + * @param factor The new prescale value to set, from 1 to 65,536. + * @see HardwareTimer::refresh() */ - void setPrescaleFactor(uint16 factor); + void setPrescaleFactor(uint32 factor); /** - * Gets the timer overflow value. + * @brief Get the timer overflow value. * @see HardwareTimer::setOverflow() */ uint16 getOverflow(); /** - * Sets the timer overflow (or "reload") value. + * @brief Set the timer overflow (or "reload") value. * - * When the timer's counter reaches this, value it resets to - * zero. Its default value is 65535 (the largest unsigned 16-bit - * integer); setting the overflow to anything lower will cause - * interrupts to be called more frequently (see the setPeriod() - * function below for a shortcut). This number sets the maximum - * value for the channel compare values. + * The new value won't take effect until the next time the counter + * overflows. You can force the counter to reset using + * HardwareTimer::refresh(). * * @param val The new overflow value to set + * @see HardwareTimer::refresh() */ void setOverflow(uint16 val); /** - * Retrieve the current timer count. + * @brief Get the current timer count. * * @return The timer's current count value */ uint16 getCount(void); /** - * Set the current timer count. - * - * Note that there is some function call overhead associated with - * calling this method, so using it is not a robust way to get - * multiple timers to share a count value. + * @brief Set the current timer count. * * @param val The new count value to set. If this value exceeds * the timer's overflow value, it is truncated to the @@ -165,143 +129,50 @@ class HardwareTimer { void setCount(uint16 val); /** - * Configure the prescaler and overflow values to generate a timer + * @brief Set the timer's period in microseconds. + * + * Configures the prescaler and overflow values to generate a timer * reload with a period as close to the given number of * microseconds as possible. * - * The return value is the overflow, which may be used to set - * channel compare values. However, if a clock that fires an - * interrupt every given number of microseconds is all that is - * desired, and the relative "phases" are unimportant, channel - * compare values may all be set to 1. - * - * @param microseconds the desired period of the timer. - * @return the overflow value (and thus, the largest value that can be - * set as a compare). + * @param microseconds The desired period of the timer. This must be + * greater than zero. + * @return The new overflow value. */ uint16 setPeriod(uint32 microseconds); /** - * Set the given channel of this timer to the given mode. - * + * @brief Configure a timer channel's mode. * @param channel Timer channel, from 1 to 4 * @param mode Mode to set */ - void setChannelMode(int channel, TimerMode mode); - - /** - * Set channel 1 of this timer to the given mode. - * - * Note: Timer1.setChannel1Mode(TIMER_PWM) may not work as - * expected; if you want PWM functionality on a channel make sure - * you don't set it to something else! - * - * @see TimerMode - */ - void setChannel1Mode(TimerMode mode); - - /** - * Set channel 2 of this timer to the given mode. - * @see TimerMode - */ - void setChannel2Mode(TimerMode mode); - - /** - * Set channel 3 of this timer to the given mode. - * @see TimerMode - */ - void setChannel3Mode(TimerMode mode); - - /** - * Set channel 4 of this timer to the given mode. - * @see TimerMode - */ - void setChannel4Mode(TimerMode mode); + void setMode(int channel, timer_mode mode); /** - * Gets the compare value for the given channel. + * @brief Get the compare value for the given channel. * @see HardwareTimer::setCompare() */ uint16 getCompare(int channel); - /** Equivalent to getCompare(1) */ - uint16 getCompare1(); - - /** Equivalent to getCompare(2) */ - uint16 getCompare2(); - - /** Equivalent to getCompare(3) */ - uint16 getCompare3(); - - /** Equivalent to getCompare(4) */ - uint16 getCompare4(); - /** - * Sets the compare value for the given channel. - * - * When the counter reaches this value the interrupt for this - * channel will fire if the channel mode is TIMER_OUTPUTCOMPARE - * and an interrupt is attached. - * - * By default, this only changes the relative offsets between - * events on a single timer ("phase"); they don't control the - * frequency with which they occur. However, a common trick is to - * increment the compare value manually in the interrupt handler - * so that the event will fire again after the increment - * period. There can be a different increment value for each - * channel, so this trick allows events to be programmed at 4 - * different rates on a single timer. Note that function call - * overheads mean that the smallest increment rate is at least a - * few microseconds. + * @brief Set the compare value for the given channel. * * @param channel the channel whose compare to set, from 1 to 4. * @param compare The compare value to set. If greater than this * timer's overflow value, it will be truncated to * the overflow value. * - * @see TimerMode - * @see HardwareTimer::setChannelMode() + * @see timer_mode + * @see HardwareTimer::setMode() * @see HardwareTimer::attachInterrupt() */ void setCompare(int channel, uint16 compare); /** - * Equivalent to setCompare(1, compare). - */ - void setCompare1(uint16 compare); - - /** - * Equivalent to setCompare(2, compare). - */ - void setCompare2(uint16 compare); - - /** - * Equivalent to setCompare(3, compare). - */ - void setCompare3(uint16 compare); - - /** - * Equivalent to setCompare(4, compare). - */ - void setCompare4(uint16 compare); - - /** - * Attach an interrupt handler to the given channel. This - * interrupt handler will be called when the timer's counter - * reaches the given channel compare value. - * - * The argument should be a function which takes no arguments and - * has no return value; i.e. it should have signature + * @brief Attach an interrupt handler to the given channel. * - * void (*handler)(void); - * - * Note: The function (often called an interrupt service routine, - * or ISR) should attempt to return as quickly as possible. - * Blinking the LED, some logic, PWM updates, and Serial writes - * are fine; writing to SerialUSB or waiting for user input can - * take a long time and other compare interrupts won't fire. Tip: - * if you have a delay() in your interrupt routine, you're probably - * doing it wrong. + * This interrupt handler will be called when the timer's counter + * reaches the given channel compare value. * * @param channel the channel to attach the ISR to, from 1 to 4. * @param handler The ISR to attach to the given channel. @@ -310,32 +181,10 @@ class HardwareTimer { void attachInterrupt(int channel, voidFuncPtr handler); /** - * Equivalent to attachCompareInterrupt(1, handler). - * @see HardwareTimer::attachCompareInterrupt() - */ - void attachCompare1Interrupt(voidFuncPtr handler); - - /** - * Equivalent to attachCompareInterrupt(2, handler). - * @see HardwareTimer::attachCompareInterrupt() - */ - void attachCompare2Interrupt(voidFuncPtr handler); - - /** - * Equivalent to attachCompareInterrupt(3, handler). - * @see HardwareTimer::attachCompareInterrupt() - */ - void attachCompare3Interrupt(voidFuncPtr handler); - - /** - * Equivalent to attachCompareInterrupt(4, handler). - * @see HardwareTimer::attachCompareInterrupt() - */ - void attachCompare4Interrupt(voidFuncPtr handler); - - /** - * Remove the interrupt handler attached to the given channel, if - * any. The handler will no longer be called by this timer. + * @brief Remove the interrupt handler attached to the given + * channel, if any. + * + * The handler will no longer be called by this timer. * * @param channel the channel whose interrupt to detach, from 1 to 4. * @see HardwareTimer::attachInterrupt() @@ -343,71 +192,138 @@ class HardwareTimer { void detachInterrupt(int channel); /** - * Equivalent to detachInterrupt(1). - * @see HardwareTimer::detachInterrupt() + * @brief Reset the counter, and update the prescaler and overflow + * values. + * + * This will reset the counter to 0 in upcounting mode (the + * default). It will also update the timer's prescaler and + * overflow, if you have set them up to be changed using + * HardwareTimer::setPrescaleFactor() or + * HardwareTimer::setOverflow(). + * + * @see HardwareTimer::setPrescaleFactor() + * @see HardwareTimer::setOverflow() */ - void detachCompare1Interrupt(void); + void refresh(void); - /** - * Equivalent to detachInterrupt(2). - * @see HardwareTimer::detachInterrupt() - */ - void detachCompare2Interrupt(void); + /* -- Deprecated methods ----------------------------------------------- */ - /** - * Equivalent to detachInterrupt(3). - * @see HardwareTimer::detachInterrupt() - */ - void detachCompare3Interrupt(void); + /** @brief Deprecated; use setMode(channel, mode) instead. */ + void setChannelMode(int channel, timer_mode mode) { + setMode(channel, mode); + } - /** - * Equivalent to detachInterrupt(4). - * @see HardwareTimer::detachInterrupt() - */ - void detachCompare4Interrupt(void); + /** @brief Deprecated; use setMode(TIMER_CH1, mode) instead. */ + void setChannel1Mode(timer_mode mode) { setMode(TIMER_CH1, mode); } - /** - * Re-initializes the counter (to 0 in upcounting mode, which is - * the default), and generates an update of the prescale and - * overflow registers. - */ - void generateUpdate(void); + /** @brief Deprecated; use setMode(TIMER_CH2, mode) instead. */ + void setChannel2Mode(timer_mode mode) { setMode(TIMER_CH2, mode); } + + /** @brief Deprecated; use setMode(TIMER_CH3, mode) instead. */ + void setChannel3Mode(timer_mode mode) { setMode(TIMER_CH3, mode); } + + /** @brief Deprecated; use setMode(TIMER_CH4, mode) instead. */ + void setChannel4Mode(timer_mode mode) { setMode(TIMER_CH4, mode); } + + /** @brief Deprecated; use return getCompare(TIMER_CH1) instead. */ + uint16 getCompare1() { return getCompare(TIMER_CH1); } + + /** @brief Deprecated; use return getCompare(TIMER_CH2) instead. */ + uint16 getCompare2() { return getCompare(TIMER_CH2); } + + /** @brief Deprecated; use return getCompare(TIMER_CH3) instead. */ + uint16 getCompare3() { return getCompare(TIMER_CH3); } + + /** @brief Deprecated; use return getCompare(TIMER_CH4) instead. */ + uint16 getCompare4() { return getCompare(TIMER_CH4); } + + /** @brief Deprecated; use setCompare(TIMER_CH1, compare) instead. */ + void setCompare1(uint16 compare) { setCompare(TIMER_CH1, compare); } + + /** @brief Deprecated; use setCompare(TIMER_CH2, compare) instead. */ + void setCompare2(uint16 compare) { setCompare(TIMER_CH2, compare); } + + /** @brief Deprecated; use setCompare(TIMER_CH3, compare) instead. */ + void setCompare3(uint16 compare) { setCompare(TIMER_CH3, compare); } + + /** @brief Deprecated; use setCompare(TIMER_CH4, compare) instead. */ + void setCompare4(uint16 compare) { setCompare(TIMER_CH4, compare); } + + /** @brief Deprecated; use attachInterrupt(TIMER_CH1, handler) instead. */ + void attachCompare1Interrupt(voidFuncPtr handler) { + attachInterrupt(TIMER_CH1, handler); + } + + /** @brief Deprecated; use attachInterrupt(TIMER_CH2, handler) instead. */ + void attachCompare2Interrupt(voidFuncPtr handler) { + attachInterrupt(TIMER_CH2, handler); + } + + /** @brief Deprecated; use attachInterrupt(TIMER_CH3, handler) instead. */ + void attachCompare3Interrupt(voidFuncPtr handler) { + attachInterrupt(TIMER_CH3, handler); + } + + /** @brief Deprecated; use attachInterrupt(TIMER_CH4, handler) instead. */ + void attachCompare4Interrupt(voidFuncPtr handler) { + attachInterrupt(TIMER_CH4, handler); + } + + /** @brief Deprecated; use detachInterrupt(TIMER_CH1) instead. */ + void detachCompare1Interrupt(void) { detachInterrupt(TIMER_CH1); } + + /** @brief Deprecated; use detachInterrupt(TIMER_CH2) instead. */ + void detachCompare2Interrupt(void) { detachInterrupt(TIMER_CH2); } + + /** @brief Deprecated; use detachInterrupt(TIMER_CH3) instead. */ + void detachCompare3Interrupt(void) { detachInterrupt(TIMER_CH3); } + + /** @brief Deprecated; use detachInterrupt(TIMER_CH4) instead. */ + void detachCompare4Interrupt(void) { detachInterrupt(TIMER_CH4); } + + /** @brief Deprecated; use refresh() instead. */ + void generateUpdate(void) { refresh(); } }; -/** Pre-instantiated timer for use by user code. */ +/* -- The rest of this file is deprecated. --------------------------------- */ + +/** + * @brief Deprecated. + * + * Pre-instantiated timer. + */ extern HardwareTimer Timer1; -/** Pre-instantiated timer for use by user code. */ +/** + * @brief Deprecated. + * + * Pre-instantiated timer. + */ extern HardwareTimer Timer2; -/** Pre-instantiated timer for use by user code. */ -extern HardwareTimer Timer3; -/** Pre-instantiated timer for use by user code. */ -extern HardwareTimer Timer4; -#if NR_TIMERS >= 8 -/** Pre-instantiated timer for use by user code, on devices with - more than four timers (this does not include the Maple). */ -extern HardwareTimer Timer5; -/** Pre-instantiated timer for use by user code, on devices with - more than four timers (this does not include the Maple). */ -extern HardwareTimer Timer8; -#endif - /** - * Get one of the pre-instantiated HardwareTimer instances, given a - * timer device number. + * @brief Deprecated. * - * Be careful not to pass an actual number to this function. For - * example, getTimer(1) will not return Timer1. Use a real - * timer_dev_num, e.g. TIMER1, TIMER2, etc. + * Pre-instantiated timer. + */ +extern HardwareTimer Timer3; +/** + * @brief Deprecated. * - * @param timerNum the timer device number, e.g. TIMER1. + * Pre-instantiated timer. + */ +extern HardwareTimer Timer4; +#ifdef STM32_HIGH_DENSITY +/** + * @brief Deprecated. * - * @return Pointer to the HardwareTimer instance corresponding to the - * given timer device number. If timerNum is TIMER_INVALID, returns a - * null pointer. + * Pre-instantiated timer. + */ +extern HardwareTimer Timer5; +/** + * @brief Deprecated. * - * @see timer_dev_num + * Pre-instantiated timer. */ -HardwareTimer* getTimer(timer_dev_num timerNum); - +extern HardwareTimer Timer8; #endif +#endif diff --git a/wirish/Print.cpp b/wirish/Print.cpp index 9c52321..9130c9c 100644 --- a/wirish/Print.cpp +++ b/wirish/Print.cpp @@ -1,127 +1,121 @@ -/****************************************************************************** - * The MIT License +/* + * Print.cpp - Base class that provides print() and println() + * Copyright (c) 2008 David A. Mellis. All right reserved. * - * Copyright (c) 2011 LeafLabs, LLC. + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, copy, - * modify, merge, publish, distribute, sublicense, and/or sell copies - * of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - *****************************************************************************/ + * Modified 23 November 2006 by David A. Mellis + * Modified 12 April 2011 by Marti Bolivar <mbolivar@leaflabs.com> + */ #include "Print.h" -#include <cstdio> -#include <climits> -#include <cstring> - -// We'll allocate character buffers of size INT_BUF_SIZE to hold the -// string representations of numbers; this value ensures that they're -// big enough to accomodate the biggest integral value + null byte. -// -// E.g., consider -(2^63-1) = -9223372036854775807, which is 20 -// characters long, including the minus sign. The other edge cases -// are similar. -// -// (Nonetheless, use snprintf everywhere, just in case of error). -#define INT_BUF_SIZE 21 - -// An IEEE-754 double buys you about 16 digits of precision; there's -// the possibility of minus signs, a decimal point, 'e+'/'e-', etc. -// While the Right Thing is to follow Steele and White, I'm just going -// to double what I consider a safe number of bytes and hope for the -// best. -#define DOUBLE_BUF_SIZE 40 - -static void fillBase(char *buf, int buf_size, int64 n, - uint8 n_real_bits, int base); -static void fillBinary(char *buf, int64 n, int start_bit); -static char baseToFmtSpec(int base); +#include <limits.h> + +#ifndef LLONG_MAX +/* + * Note: + * + * At time of writing (12 April 2011), the limits.h that came with the + * newlib we distributed didn't include LLONG_MAX. Because we're + * staying away from using templates (see /notes/coding_standard.rst, + * "Language Features and Compiler Extensions"), this value was + * copy-pasted from a println() of the value + * + * std::numeric_limits<long long>::max(). + */ +#define LLONG_MAX 9223372036854775807LL +#endif + +#include "wirish_math.h" + +/* + * Public methods + */ void Print::write(const char *str) { - for (const char *c = str; *c != '\0'; c++) { - write(*c); + while (*str) { + write(*str++); } } -void Print::write(void *buffer, uint32 size) { - for (uint32 i = 0; i < size; i++) { - write(*((uint8*)buffer + i)); +void Print::write(const void *buffer, uint32 size) { + uint8 *ch = (uint8*)buffer; + while (size--) { + write(*ch++); } } +void Print::print(uint8 b) { + this->write(b); +} + void Print::print(char c) { - print((uint8) c); + print((uint8)c); } void Print::print(const char str[]) { write(str); } -void Print::print(uint8 b) { - write(b); +void Print::print(int n) { + print((long long)n); } -void Print::print(int32 n) { - print(n, DEC); +void Print::print(unsigned int n) { + print((unsigned long long)n); } -void Print::print(uint32 n) { - print((uint64) n); +void Print::print(long n) { + print((long long)n); } -void Print::print(int64 n) { - print(n, DEC); +void Print::print(unsigned long n) { + print((unsigned long long)n); } -void Print::print(uint64 n) { - char buf[INT_BUF_SIZE]; - snprintf(buf, INT_BUF_SIZE, "%llu", n); - write(buf); +void Print::print(long long n) { + if (n < 0) { + print('-'); + n = -n; + } + printNumber(n, 10); } -void Print::print(int32 n, int base) { - // Worst case: sign bit set && base == BIN: 32 bytes for digits + - // 1 null (base == BIN means no minus sign). - char buf[33]; - fillBase(buf, sizeof(buf), (int64)n, 32, base); - write(buf); +void Print::print(unsigned long long n) { + printNumber(n, 10); } -void Print::print(int64 n, int base) { - // As above, but now 64 bytes for bits + 1 null - char buf[65]; - fillBase(buf, sizeof(buf), n, 64, base); - write(buf); +void Print::print(unsigned long long n, int base) { + if (base == 0) { + print((char)n); + } else if (base == 10) { + print(n); + } else { + printNumber(n, base); + } } void Print::print(double n) { - char buf[DOUBLE_BUF_SIZE]; - // This breaks strict compliance with the Arduino library behavior - // (which is equivalent to using "%.2f"), but that's really not - // enough. According to Stroustrup, "%f" without precision is - // equivalent to ".6f", which is much better. - snprintf(buf, DOUBLE_BUF_SIZE, "%f", n); - write(buf); + printFloat(n, 2); } void Print::println(void) { - print("\r\n"); + print('\r'); + print('\n'); } void Print::println(char c) { @@ -139,32 +133,37 @@ void Print::println(uint8 b) { println(); } -void Print::println(int32 n) { +void Print::println(int n) { print(n); println(); } -void Print::println(uint32 n) { +void Print::println(unsigned int n) { print(n); println(); } -void Print::println(int64 n) { - print(n); +void Print::println(long n) { + print((long long)n); + println(); +} + +void Print::println(unsigned long n) { + print((unsigned long long)n); println(); } -void Print::println(uint64 n) { +void Print::println(long long n) { print(n); println(); } -void Print::println(int32 n, int base) { - print(n, base); +void Print::println(unsigned long long n) { + print(n); println(); } -void Print::println(int64 n, int base) { +void Print::println(unsigned long long n, int base) { print(n, base); println(); } @@ -174,54 +173,87 @@ void Print::println(double n) { println(); } -// -- Auxiliary functions ----------------------------------------------------- +/* + * Private methods + */ -static void fillBase(char *buf, int buf_size, int64 n, - uint8 n_real_bits, int base) { - if (base == BIN) { - fillBinary(buf, n, n_real_bits - 1); - } else { - char spec = baseToFmtSpec(base); - char fmt[5]; - - if (base == BYTE) - n = (uint8)n; - - if (n_real_bits == 32) { - snprintf(fmt, sizeof(fmt), "%%l%c", spec); - snprintf(buf, buf_size, fmt, (int32)n); - } else { - snprintf(fmt, sizeof(fmt), "%%ll%c", spec); - snprintf(buf, buf_size, fmt, n); - } +void Print::printNumber(unsigned long long n, uint8 base) { + unsigned char buf[CHAR_BIT * sizeof(long long)]; + unsigned long i = 0; + + if (n == 0) { + print('0'); + return; + } + + while (n > 0) { + buf[i++] = n % base; + n /= base; + } + + for (; i > 0; i--) { + print((char)(buf[i - 1] < 10 ? + '0' + buf[i - 1] : + 'A' + buf[i - 1] - 10)); } } -// Assumes sizeof(buf) > start_bit. -static void fillBinary(char *buf, int64 n, int start_bit) { - int b = 0; // position in buf - int i = start_bit; // position in n's bits - while(!(n & (1 << i))) { - i--; +/* According to snprintf(), + * + * nextafter((double)numeric_limits<long long>::max(), 0.0) ~= 9.22337e+18 + * + * This slightly smaller value was picked semi-arbitrarily. */ +#define LARGE_DOUBLE_TRESHOLD (9.1e18) + +/* THIS FUNCTION SHOULDN'T BE USED IF YOU NEED ACCURATE RESULTS. + * + * This implementation is meant to be simple and not occupy too much + * code size. However, printing floating point values accurately is a + * subtle task, best left to a well-tested library function. + * + * See Steele and White 2003 for more details: + * + * http://kurtstephens.com/files/p372-steele.pdf + */ +void Print::printFloat(double number, uint8 digits) { + // Hackish fail-fast behavior for large-magnitude doubles + if (abs(number) >= LARGE_DOUBLE_TRESHOLD) { + if (number < 0.0) { + print('-'); + } + print("<large double>"); + return; } - for(; i >= 0; i--) { - buf[b++] = '0' + ((n >> i) & 0x1); + + // Handle negative numbers + if (number < 0.0) { + print('-'); + number = -number; + } + + // Simplistic rounding strategy so that e.g. print(1.999, 2) + // prints as "2.00" + double rounding = 0.5; + for (uint8 i = 0; i < digits; i++) { + rounding /= 10.0; } - buf[b] = '\0'; -} - -static char baseToFmtSpec(int base) { - switch (base) { - case DEC: - return 'd'; - case HEX: - return 'x'; - case OCT: - return 'o'; - case BYTE: - return 'd'; - default: - // Shouldn't happen, but give a sensible default - return 'd'; + number += rounding; + + // Extract the integer part of the number and print it + long long int_part = (long long)number; + double remainder = number - int_part; + print(int_part); + + // Print the decimal point, but only if there are digits beyond + if (digits > 0) { + print("."); + } + + // Extract digits from the remainder one at a time + while (digits-- > 0) { + remainder *= 10.0; + int to_print = (int)remainder; + print(to_print); + remainder -= to_print; } } diff --git a/wirish/Print.h b/wirish/Print.h index 4527948..73d82e7 100644 --- a/wirish/Print.h +++ b/wirish/Print.h @@ -1,68 +1,69 @@ -/****************************************************************************** - * The MIT License +/* + * Print.h - Base class that provides print() and println() + * Copyright (c) 2008 David A. Mellis. All right reserved. * - * Copyright (c) 2011 LeafLabs, LLC. + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, copy, - * modify, merge, publish, distribute, sublicense, and/or sell copies - * of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA. * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - *****************************************************************************/ + * Modified 12 April 2011 by Marti Bolivar <mbolivar@leaflabs.com> + */ #ifndef _PRINT_H_ #define _PRINT_H_ #include "libmaple_types.h" -#define DEC 10 -#define HEX 16 -#define OCT 8 -#define BIN 2 -#define BYTE 0 // yuck +enum { + BYTE = 0, + BIN = 2, + OCT = 8, + DEC = 10, + HEX = 16 +}; class Print { - public: - virtual void write(uint8) = 0; +public: + virtual void write(uint8 ch) = 0; virtual void write(const char *str); - virtual void write(void *buf, uint32 size); - + virtual void write(const void *buf, uint32 len); void print(char); void print(const char[]); void print(uint8); - void print(int32); - void print(uint32); - void print(int64); - void print(uint64); - void print(int32, int); - void print(int64, int); + void print(int); + void print(unsigned int); + void print(long); + void print(unsigned long); + void print(long long); + void print(unsigned long long); + void print(unsigned long long, int); void print(double); - void println(void); void println(char); void println(const char[]); void println(uint8); - void println(int32); - void println(uint32); - void println(int64); - void println(uint64); - void println(int32, int); - void println(int64, int); + void println(int); + void println(unsigned int); + void println(long); + void println(unsigned long); + void println(long long); + void println(unsigned long long); + void println(unsigned long long, int); void println(double); +private: + void printNumber(unsigned long long, uint8); + void printFloat(double, uint8); }; #endif diff --git a/wirish/boards.cpp b/wirish/boards.cpp new file mode 100644 index 0000000..9e25eeb --- /dev/null +++ b/wirish/boards.cpp @@ -0,0 +1,160 @@ +/****************************************************************************** + * The MIT License + * + * Copyright (c) 2010 Perry Hung. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + *****************************************************************************/ + +/** + * @brief Generic board initialization routines. + * + * By default, we bring up all Maple boards to 72MHz, clocked off the + * PLL, driven by the 8MHz external crystal. AHB and APB2 are clocked + * at 72MHz. APB1 is clocked at 36MHz. + */ + +#include "boards.h" + +#include "flash.h" +#include "rcc.h" +#include "nvic.h" +#include "systick.h" +#include "gpio.h" +#include "adc.h" +#include "timer.h" +#include "usb.h" + +static void setupFlash(void); +static void setupClocks(void); +static void setupNVIC(void); +static void setupADC(void); +static void setupTimers(void); + +void init(void) { + setupFlash(); + setupClocks(); + setupNVIC(); + systick_init(SYSTICK_RELOAD_VAL); + gpio_init_all(); + afio_init(); + setupADC(); + setupTimers(); + setupUSB(); + boardInit(); +} + +/* You could farm this out to the files in boards/ if e.g. it takes + * too long to test on Maple Native (all those FSMC pins...). */ +bool boardUsesPin(uint8 pin) { + for (int i = 0; i < BOARD_NR_USED_PINS; i++) { + if (pin == boardUsedPins[i]) { + return true; + } + } + return false; +} + +static void setupFlash(void) { + flash_enable_prefetch(); + flash_set_latency(FLASH_WAIT_STATE_2); +} + +/* + * Clock setup. Note that some of this only takes effect if we're + * running bare metal and the bootloader hasn't done it for us + * already. + * + * If you change this function, you MUST change the file-level Doxygen + * comment above. + */ +static void setupClocks() { + rcc_clk_init(RCC_CLKSRC_PLL, RCC_PLLSRC_HSE, RCC_PLLMUL_9); + rcc_set_prescaler(RCC_PRESCALER_AHB, RCC_AHB_SYSCLK_DIV_1); + rcc_set_prescaler(RCC_PRESCALER_APB1, RCC_APB1_HCLK_DIV_2); + rcc_set_prescaler(RCC_PRESCALER_APB2, RCC_APB2_HCLK_DIV_1); +} + +static void setupNVIC() { +#ifdef VECT_TAB_FLASH + nvic_init(USER_ADDR_ROM, 0); +#elif defined VECT_TAB_RAM + nvic_init(USER_ADDR_RAM, 0); +#elif defined VECT_TAB_BASE + nvic_init((uint32)0x08000000, 0); +#else +#error "You must select a base address for the vector table." +#endif +} + +static void adcDefaultConfig(const adc_dev* dev); + +static void setupADC() { + adc_foreach(adcDefaultConfig); +} + +static void timerDefaultConfig(timer_dev*); + +static void setupTimers() { + timer_foreach(timerDefaultConfig); +} + +static void adcDefaultConfig(const adc_dev *dev) { + adc_init(dev); + + adc_set_extsel(dev, ADC_SWSTART); + adc_set_exttrig(dev, true); + + adc_enable(dev); + adc_calibrate(dev); + adc_set_sample_rate(dev, ADC_SMPR_55_5); +} + +static void timerDefaultConfig(timer_dev *dev) { + timer_adv_reg_map *regs = (dev->regs).adv; + const uint16 full_overflow = 0xFFFF; + const uint16 half_duty = 0x8FFF; + + timer_init(dev); + timer_pause(dev); + + regs->CR1 = TIMER_CR1_ARPE; + regs->PSC = 1; + regs->SR = 0; + regs->DIER = 0; + regs->EGR = TIMER_EGR_UG; + + switch (dev->type) { + case TIMER_ADVANCED: + regs->BDTR = TIMER_BDTR_MOE | TIMER_BDTR_LOCK_OFF; + // fall-through + case TIMER_GENERAL: + timer_set_reload(dev, full_overflow); + + for (int channel = 1; channel <= 4; channel++) { + timer_set_compare(dev, channel, half_duty); + timer_oc_set_mode(dev, channel, TIMER_OC_MODE_PWM_1, TIMER_OC_PE); + } + // fall-through + case TIMER_BASIC: + break; + } + + timer_resume(dev); +} diff --git a/wirish/boards.h b/wirish/boards.h index f8505ab..2515c00 100644 --- a/wirish/boards.h +++ b/wirish/boards.h @@ -3,29 +3,37 @@ * * Copyright (c) 2010 Bryan Newbold. * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. *****************************************************************************/ -/* This file contains board-specific pin mapping tables. To add a new board - * type, copy the "BOARD_maple" section below and edit it as needed, then - * update your build toolchain with a new "BOARD" type. This must match the - * separate MCU type (which determines the ../libmaple configuration). +/** + * @file boards.h + * @author Bryan Newbold <bnewbold@leaflabs.com>, + * Marti Bolivar <mbolivar@leaflabs.com> + * @brief Board-specific pin information. + * + * To add a new board type, add a new pair of files to + * /wirish/boards/, update the section below with a new "BOARD" type, + * and update /wirish/rules.mk to include your boards/your_board.cpp + * file in the top-level Makefile build. */ #ifndef _BOARDS_H_ @@ -33,14 +41,14 @@ #include "libmaple.h" #include "gpio.h" -#include "timers.h" -#include "exti.h" +#include "timer.h" -#ifdef __cplusplus -extern "C" { -#endif +#include "wirish_types.h" -/* Set of all possible digital pin names; not all boards have all these */ +/* Set of all possible pin names; not all boards have all these (note + * that we use the Dx convention since all of the Maple's pins are + * "digital" pins (e.g. can be used with digitalRead() and + * digitalWrite()), but not all of them are connected to ADCs. */ enum { D0, D1, D2, D3, D4, D5, D6, D7, D8, D9, D10, D11, D12, D13, D14, D15, D16, D17, D18, D19, D20, D21, D22, D23, D24, D25, D26, D27, D28, D29, D30, D31, @@ -51,444 +59,97 @@ enum { D92, D93, D94, D95, D96, D97, D98, D99, D100, D101, D102, D103, D104, D105, D106, D107, D108, D109, D110, D111, }; -/* Set of all possible analog pin names; not all boards have all these */ -enum { - ADC0, ADC1, ADC2, ADC3, ADC4, ADC5, ADC6, ADC7, ADC8, ADC9, ADC10, ADC11, - ADC12, ADC13, ADC14, ADC15, ADC16, ADC17, ADC18, ADC19, ADC20, }; - -#define ADC_INVALID 0xFFFFFFFF - -/* Types used for the tables below */ -typedef struct PinMapping { - GPIO_Port *port; - uint32 pin; - uint32 adc; - TimerCCR timer_ccr; - uint32 exti_port; - timer_dev_num timer_num; - uint32 timer_chan; -} PinMapping; - -/* LeafLabs Maple rev3, rev5 */ -#ifdef BOARD_maple +/** + * @brief Maps each Maple pin to a corresponding stm32_pin_info. + * @see stm32_pin_info + */ +extern const stm32_pin_info PIN_MAP[]; - #define CYCLES_PER_MICROSECOND 72 - #define SYSTICK_RELOAD_VAL 71999 /* takes a cycle to reload */ +/** + * @brief Pins capable of PWM output. + * + * Its length is BOARD_NR_PWM_PINS. + */ +extern const uint8 boardPWMPins[]; - #define BOARD_BUTTON_PIN 38 - #define BOARD_LED_PIN 13 +/** + * @brief Array of pins capable of analog input. + * + * Its length is BOARD_NR_ADC_PINS. + */ +extern const uint8 boardADCPins[]; - static __attribute__ ((unused)) PinMapping PIN_MAP[NR_GPIO_PINS] = { - /* D0/PA3 */ - {GPIOA_BASE, 3, ADC3, TIMER2_CH4_CCR, EXTI_CONFIG_PORTA, TIMER2, 4}, - /* D1/PA2 */ - {GPIOA_BASE, 2, ADC2, TIMER2_CH3_CCR, EXTI_CONFIG_PORTA, TIMER2, 3}, - /* D2/PA0 */ - {GPIOA_BASE, 0, ADC0, TIMER2_CH1_CCR, EXTI_CONFIG_PORTA, TIMER2, 1}, - /* D3/PA1 */ - {GPIOA_BASE, 1, ADC1, TIMER2_CH2_CCR, EXTI_CONFIG_PORTA, TIMER2, 2}, - /* D4/PB5 */ - {GPIOB_BASE, 5, ADC_INVALID, 0, EXTI_CONFIG_PORTB, TIMER_INVALID, TIMER_INVALID}, - /* D5/PB6 */ - {GPIOB_BASE, 6, ADC_INVALID, TIMER4_CH1_CCR, EXTI_CONFIG_PORTB, TIMER4, 1}, - /* D6/PA8 */ - {GPIOA_BASE, 8, ADC_INVALID, TIMER1_CH1_CCR, EXTI_CONFIG_PORTA, TIMER1, 1}, - /* D7/PA9 */ - {GPIOA_BASE, 9, ADC_INVALID, TIMER1_CH2_CCR, EXTI_CONFIG_PORTA, TIMER1, 2}, - /* D8/PA10 */ - {GPIOA_BASE, 10, ADC_INVALID, TIMER1_CH3_CCR, EXTI_CONFIG_PORTA, TIMER1, 3}, - /* D9/PB7 */ - {GPIOB_BASE, 7, ADC_INVALID, TIMER4_CH2_CCR, EXTI_CONFIG_PORTB, TIMER4, 2}, - /* D10/PA4 */ - {GPIOA_BASE, 4, ADC4, 0, EXTI_CONFIG_PORTA, TIMER_INVALID, TIMER_INVALID}, - /* D11/PA7 */ - {GPIOA_BASE, 7, ADC7, TIMER3_CH2_CCR, EXTI_CONFIG_PORTA, TIMER3, 2}, - /* D12/PA6 */ - {GPIOA_BASE, 6, ADC6, TIMER3_CH1_CCR, EXTI_CONFIG_PORTA, TIMER3, 1}, - /* D13/PA5 */ - {GPIOA_BASE, 5, ADC5, 0, EXTI_CONFIG_PORTA, TIMER_INVALID, TIMER_INVALID}, - /* D14/PB8 */ - {GPIOB_BASE, 8, ADC_INVALID, TIMER4_CH3_CCR, EXTI_CONFIG_PORTB, TIMER4, 3}, +/** + * @brief Pins which are connected to external hardware. + * + * For example, on Maple boards, it always at least includes + * BOARD_LED_PIN. Its length is BOARD_NR_USED_PINS. + */ +extern const uint8 boardUsedPins[]; - /* Little header */ +/** + * @brief Generic board initialization function. + * + * This function is called before main(). It ensures that the clocks + * and peripherals are configured properly for use with wirish, then + * calls boardInit(). + * + * @see boardInit() + */ +void init(void); - /* D15/PC0 */ - {GPIOC_BASE, 0, ADC10, 0, EXTI_CONFIG_PORTC, TIMER_INVALID, TIMER_INVALID}, - /* D16/PC1 */ - {GPIOC_BASE, 1, ADC11, 0, EXTI_CONFIG_PORTC, TIMER_INVALID, TIMER_INVALID}, - /* D17/PC2 */ - {GPIOC_BASE, 2, ADC12, 0, EXTI_CONFIG_PORTC, TIMER_INVALID, TIMER_INVALID}, - /* D18/PC3 */ - {GPIOC_BASE, 3, ADC13, 0, EXTI_CONFIG_PORTC, TIMER_INVALID, TIMER_INVALID}, - /* D19/PC4 */ - {GPIOC_BASE, 4, ADC14, 0, EXTI_CONFIG_PORTC, TIMER_INVALID, TIMER_INVALID}, - /* D20/PC5 */ - {GPIOC_BASE, 5, ADC15, 0, EXTI_CONFIG_PORTC, TIMER_INVALID, TIMER_INVALID}, +/** + * @brief Board-specific initialization function. + * + * This function is called from init() after all generic board + * initialization has been performed. Each board is required to + * define its own. + * + * @see init() + */ +extern void boardInit(void); - /* External header */ +/** + * @brief Test if a pin is used for a special purpose on your board. + * @param pin Pin to test + * @return true if the given pin is in boardUsedPins, and false otherwise. + * @see boardUsedPins + */ +bool boardUsesPin(uint8 pin); - /* D21/PC13 */ - {GPIOC_BASE, 13, ADC_INVALID, 0, EXTI_CONFIG_PORTC, TIMER_INVALID, TIMER_INVALID}, - /* D22/PC14 */ - {GPIOC_BASE, 14, ADC_INVALID, 0, EXTI_CONFIG_PORTC, TIMER_INVALID, TIMER_INVALID}, - /* D23/PC15 */ - {GPIOC_BASE, 15, ADC_INVALID, 0, EXTI_CONFIG_PORTC, TIMER_INVALID, TIMER_INVALID}, - /* D24/PB9 */ - {GPIOB_BASE, 9, ADC_INVALID, TIMER4_CH4_CCR, EXTI_CONFIG_PORTB, TIMER_INVALID, TIMER_INVALID}, - /* D25/PD2 */ - {GPIOD_BASE, 2, ADC_INVALID, 0, EXTI_CONFIG_PORTD, TIMER_INVALID, TIMER_INVALID}, - /* D26/PC10 */ - {GPIOC_BASE, 10, ADC_INVALID, 0, EXTI_CONFIG_PORTC, TIMER_INVALID, TIMER_INVALID}, - /* D27/PB0 */ - {GPIOB_BASE, 0, ADC8, TIMER3_CH3_CCR, EXTI_CONFIG_PORTB, TIMER3, 3}, - /* D28/PB1 */ - {GPIOB_BASE, 1, ADC9, TIMER3_CH4_CCR, EXTI_CONFIG_PORTB, TIMER3, 4}, - /* D29/PB10 */ - {GPIOB_BASE, 10, ADC_INVALID, 0, EXTI_CONFIG_PORTB, TIMER_INVALID, TIMER_INVALID}, - /* D30/PB11 */ - {GPIOB_BASE, 11, ADC_INVALID, 0, EXTI_CONFIG_PORTB, TIMER_INVALID, TIMER_INVALID}, - /* D31/PB12 */ - {GPIOB_BASE, 12, ADC_INVALID, 0, EXTI_CONFIG_PORTB, TIMER_INVALID, TIMER_INVALID}, - /* D32/PB13 */ - {GPIOB_BASE, 13, ADC_INVALID, 0, EXTI_CONFIG_PORTB, TIMER_INVALID, TIMER_INVALID}, - /* D33/PB14 */ - {GPIOB_BASE, 14, ADC_INVALID, 0, EXTI_CONFIG_PORTB, TIMER_INVALID, TIMER_INVALID}, - /* D34/PB15 */ - {GPIOB_BASE, 15, ADC_INVALID, 0, EXTI_CONFIG_PORTB, TIMER_INVALID, TIMER_INVALID}, - /* D35/PC6 */ - {GPIOC_BASE, 6, ADC_INVALID, 0, EXTI_CONFIG_PORTC, TIMER_INVALID, TIMER_INVALID}, - /* D36/PC7 */ - {GPIOC_BASE, 7, ADC_INVALID, 0, EXTI_CONFIG_PORTC, TIMER_INVALID, TIMER_INVALID}, - /* D37/PC8 */ - {GPIOC_BASE, 8, ADC_INVALID, 0, EXTI_CONFIG_PORTC, TIMER_INVALID, TIMER_INVALID}, - /* D38/PC9 (BUT) */ - {GPIOC_BASE, 9, ADC_INVALID, 0, EXTI_CONFIG_PORTC, TIMER_INVALID, TIMER_INVALID} - }; +/* Include the appropriate private header from boards/: */ - #define BOARD_INIT do { \ - } while(0) +/* FIXME HACK put boards/ before these paths once IDE uses make. */ +#ifdef BOARD_maple +#include "maple.h" #elif defined(BOARD_maple_native) - - /* LeafLabs Maple Native (prototype) */ - - #define CYCLES_PER_MICROSECOND 72 - #define SYSTICK_RELOAD_VAL 71999 /* takes a cycle to reload */ - - #define BOARD_LED_PIN D21 - #define BOARD_BUTTON_PIN D18 - - static __attribute__ ((unused)) PinMapping PIN_MAP[NR_GPIO_PINS] = { - /* Top header */ - - /* D0/PB10 */ - {GPIOB_BASE, 10, ADC_INVALID, 0, EXTI_CONFIG_PORTB, TIMER_INVALID, TIMER_INVALID}, - /* D1/PB2 */ - {GPIOB_BASE, 2, ADC_INVALID, 0, EXTI_CONFIG_PORTB, TIMER_INVALID, TIMER_INVALID}, - /* D2/PB12 */ - {GPIOB_BASE, 12, ADC_INVALID, 0, EXTI_CONFIG_PORTB, TIMER_INVALID, TIMER_INVALID}, - /* D3/PB13 */ - {GPIOB_BASE, 13, ADC_INVALID, 0, EXTI_CONFIG_PORTB, TIMER_INVALID, TIMER_INVALID}, - /* D4/PB14 */ - {GPIOB_BASE, 14, ADC_INVALID, 0, EXTI_CONFIG_PORTB, TIMER_INVALID, TIMER_INVALID}, - /* D5/PB15 */ - {GPIOB_BASE, 15, ADC_INVALID, 0, EXTI_CONFIG_PORTB, TIMER_INVALID, TIMER_INVALID}, - /* D6/PC0 */ - {GPIOC_BASE, 0, ADC10, 0, EXTI_CONFIG_PORTC, TIMER_INVALID, TIMER_INVALID}, - /* D7/PC1 */ - {GPIOC_BASE, 1, ADC11, 0, EXTI_CONFIG_PORTC, TIMER_INVALID, TIMER_INVALID}, - /* D8/PC2 */ - {GPIOC_BASE, 2, ADC12, 0, EXTI_CONFIG_PORTC, TIMER_INVALID, TIMER_INVALID}, - /* D9/PC3 */ - {GPIOC_BASE, 3, ADC13, 0, EXTI_CONFIG_PORTC, TIMER_INVALID, TIMER_INVALID}, - /* D10/PC4 */ - {GPIOC_BASE, 4, ADC14, 0, EXTI_CONFIG_PORTC, TIMER_INVALID, TIMER_INVALID}, - /* D11/PC5 */ - {GPIOC_BASE, 5, ADC15, 0, EXTI_CONFIG_PORTC, TIMER_INVALID, TIMER_INVALID}, - /* D12/PC6 */ - {GPIOC_BASE, 6, ADC_INVALID, TIMER8_CH1_CCR, EXTI_CONFIG_PORTC, TIMER8, 1}, - /* D13/PC7 */ - {GPIOC_BASE, 7, ADC_INVALID, TIMER8_CH2_CCR, EXTI_CONFIG_PORTC, TIMER8, 2}, - /* D14/PC8 */ - {GPIOC_BASE, 8, ADC_INVALID, TIMER8_CH3_CCR, EXTI_CONFIG_PORTC, TIMER8, 3}, - /* D15/PC9 */ - {GPIOC_BASE, 9, ADC_INVALID, TIMER8_CH4_CCR, EXTI_CONFIG_PORTC, TIMER8, 4}, - /* D16/PC10 */ - {GPIOC_BASE, 10, ADC_INVALID, 0, EXTI_CONFIG_PORTC, TIMER_INVALID, TIMER_INVALID}, - /* D17/PC11 */ - {GPIOC_BASE, 11, ADC_INVALID, 0, EXTI_CONFIG_PORTC, TIMER_INVALID, TIMER_INVALID}, - /* D18/PC12 */ - {GPIOC_BASE, 12, ADC_INVALID, 0, EXTI_CONFIG_PORTC, TIMER_INVALID, TIMER_INVALID}, - /* D19/PC13 */ - {GPIOC_BASE, 13, ADC_INVALID, 0, EXTI_CONFIG_PORTC, TIMER_INVALID, TIMER_INVALID}, - /* D20/PC14 */ - {GPIOC_BASE, 14, ADC_INVALID, 0, EXTI_CONFIG_PORTC, TIMER_INVALID, TIMER_INVALID}, - /* D21/PC15 */ - {GPIOC_BASE, 15, ADC_INVALID, 0, EXTI_CONFIG_PORTC, TIMER_INVALID, TIMER_INVALID}, - /* D22/PA8 */ - {GPIOA_BASE, 8, ADC_INVALID, TIMER1_CH1_CCR, EXTI_CONFIG_PORTA, TIMER1, 1}, - /* D23/PA9 */ - {GPIOA_BASE, 9, ADC_INVALID, TIMER1_CH2_CCR, EXTI_CONFIG_PORTA, TIMER1, 2}, - /* D24/PA10 */ - {GPIOA_BASE, 10, ADC_INVALID, TIMER1_CH3_CCR, EXTI_CONFIG_PORTA, TIMER1, 3}, - /* D25/PB9 */ - {GPIOB_BASE, 9, ADC_INVALID, TIMER4_CH4_CCR, EXTI_CONFIG_PORTB, TIMER4, 4}, - - /* Bottom header */ - - /* D26/PD2 */ - {GPIOD_BASE, 2, ADC_INVALID, 0, EXTI_CONFIG_PORTD, TIMER_INVALID, TIMER_INVALID}, - /* D27/PD3 */ - {GPIOD_BASE, 3, ADC_INVALID, 0, EXTI_CONFIG_PORTD, TIMER_INVALID, TIMER_INVALID}, - /* D28/PD6 */ - {GPIOD_BASE, 6, ADC_INVALID, 0, EXTI_CONFIG_PORTD, TIMER_INVALID, TIMER_INVALID}, - /* D29/PG11 */ - {GPIOG_BASE, 11, ADC_INVALID, 0, EXTI_CONFIG_PORTG, TIMER_INVALID, TIMER_INVALID}, - /* D30/PG12 */ - {GPIOG_BASE, 12, ADC_INVALID, 0, EXTI_CONFIG_PORTG, TIMER_INVALID, TIMER_INVALID}, - /* D31/PG13 */ - {GPIOG_BASE, 13, ADC_INVALID, 0, EXTI_CONFIG_PORTG, TIMER_INVALID, TIMER_INVALID}, - /* D32/PG14 */ - {GPIOG_BASE, 14, ADC_INVALID, 0, EXTI_CONFIG_PORTG, TIMER_INVALID, TIMER_INVALID}, - /* D33/PG8 */ - {GPIOG_BASE, 8, ADC_INVALID, 0, EXTI_CONFIG_PORTG, TIMER_INVALID, TIMER_INVALID}, - /* D34/PG7 */ - {GPIOG_BASE, 7, ADC_INVALID, 0, EXTI_CONFIG_PORTG, TIMER_INVALID, TIMER_INVALID}, - /* D35/PG6 */ - {GPIOG_BASE, 6, ADC_INVALID, 0, EXTI_CONFIG_PORTG, TIMER_INVALID, TIMER_INVALID}, - /* D36/PB5 */ - {GPIOB_BASE, 5, ADC_INVALID, 0, EXTI_CONFIG_PORTB, TIMER_INVALID, TIMER_INVALID}, - /* D37/PB6 */ - {GPIOB_BASE, 6, ADC_INVALID, TIMER4_CH1_CCR, EXTI_CONFIG_PORTB, TIMER4, 1}, - /* D38/PB7 */ - {GPIOB_BASE, 7, ADC_INVALID, TIMER4_CH2_CCR, EXTI_CONFIG_PORTB, TIMER4, 2}, - /* D39/PF6 */ - {GPIOF_BASE, 6, ADC4, 0, EXTI_CONFIG_PORTF, TIMER_INVALID, TIMER_INVALID}, - /* D40/PF7 */ - {GPIOF_BASE, 7, ADC5, 0, EXTI_CONFIG_PORTF, TIMER_INVALID, TIMER_INVALID}, - /* D41/PF8 */ - {GPIOF_BASE, 8, ADC6, 0, EXTI_CONFIG_PORTF, TIMER_INVALID, TIMER_INVALID}, - /* D42/PF9 */ - {GPIOF_BASE, 9, ADC7, 0, EXTI_CONFIG_PORTF, TIMER_INVALID, TIMER_INVALID}, - /* D43/PF10 */ - {GPIOF_BASE, 10, ADC8, 0, EXTI_CONFIG_PORTF, TIMER_INVALID, TIMER_INVALID}, - /* D44/PF11 */ - {GPIOF_BASE, 11, ADC_INVALID, 0, EXTI_CONFIG_PORTF, TIMER_INVALID, TIMER_INVALID}, - /* D45/PB1 */ - {GPIOB_BASE, 1, ADC9, TIMER3_CH4_CCR, EXTI_CONFIG_PORTB, TIMER3, 4}, - /* D46/PB0 */ - {GPIOB_BASE, 0, ADC8, TIMER3_CH3_CCR, EXTI_CONFIG_PORTB, TIMER3, 3}, - /* D47/PA0 */ - {GPIOA_BASE, 0, ADC0, TIMER5_CH1_CCR, EXTI_CONFIG_PORTA, TIMER5, 1}, - /* D48/PA1 */ - {GPIOA_BASE, 1, ADC1, TIMER5_CH2_CCR, EXTI_CONFIG_PORTA, TIMER5, 2}, /* FIXME (?) what to do about D48--D50 - also being TIMER2_CH[2,3,4]? */ - /* D49/PA2 */ - {GPIOA_BASE, 2, ADC2, TIMER5_CH3_CCR, EXTI_CONFIG_PORTA, TIMER5, 3}, - /* D50/PA3 */ - {GPIOA_BASE, 3, ADC3, TIMER5_CH4_CCR, EXTI_CONFIG_PORTA, TIMER5, 4}, - /* D51/PA4 */ - {GPIOA_BASE, 4, ADC4, 0, EXTI_CONFIG_PORTA, TIMER_INVALID, TIMER_INVALID}, - /* D52/PA5 */ - {GPIOA_BASE, 5, ADC5, 0, EXTI_CONFIG_PORTA, TIMER_INVALID, TIMER_INVALID}, - /* D53/PA6 */ - {GPIOA_BASE, 6, ADC6, TIMER3_CH1_CCR, EXTI_CONFIG_PORTA, TIMER3, 1}, - /* D54/PA7 */ - {GPIOA_BASE, 7, ADC7, TIMER3_CH2_CCR, EXTI_CONFIG_PORTA, TIMER3, 2}, - - /* Right (triple) header */ - - /* D55/PF0 */ - {GPIOF_BASE, 0, ADC_INVALID, 0, EXTI_CONFIG_PORTF, TIMER_INVALID, TIMER_INVALID}, - /* D56/PD11 */ - {GPIOD_BASE, 11, ADC_INVALID, 0, EXTI_CONFIG_PORTD, TIMER_INVALID, TIMER_INVALID}, - /* D57/PD14 */ - {GPIOD_BASE, 14, ADC_INVALID, 0, EXTI_CONFIG_PORTD, TIMER_INVALID, TIMER_INVALID}, - /* D58/PF1 */ - {GPIOF_BASE, 1, ADC_INVALID, 0, EXTI_CONFIG_PORTF, TIMER_INVALID, TIMER_INVALID}, - /* D59/PD12 */ - {GPIOD_BASE, 12, ADC_INVALID, 0, EXTI_CONFIG_PORTD, TIMER_INVALID, TIMER_INVALID}, - /* D60/PD15 */ - {GPIOD_BASE, 15, ADC_INVALID, 0, EXTI_CONFIG_PORTD, TIMER_INVALID, TIMER_INVALID}, - /* D61/PF2 */ - {GPIOF_BASE, 2, ADC_INVALID, 0, EXTI_CONFIG_PORTF, TIMER_INVALID, TIMER_INVALID}, - /* D62/PD13 */ - {GPIOD_BASE, 13, ADC_INVALID, 0, EXTI_CONFIG_PORTD, TIMER_INVALID, TIMER_INVALID}, - /* D63/PD0 */ - {GPIOD_BASE, 0, ADC_INVALID, 0, EXTI_CONFIG_PORTD, TIMER_INVALID, TIMER_INVALID}, - /* D64/PF3 */ - {GPIOF_BASE, 3, ADC_INVALID, 0, EXTI_CONFIG_PORTF, TIMER_INVALID, TIMER_INVALID}, - /* D65/PE3 */ - {GPIOE_BASE, 3, ADC_INVALID, 0, EXTI_CONFIG_PORTE, TIMER_INVALID, TIMER_INVALID}, - /* D66/PD1 */ - {GPIOD_BASE, 1, ADC_INVALID, 0, EXTI_CONFIG_PORTD, TIMER_INVALID, TIMER_INVALID}, - /* D67/PF4 */ - {GPIOF_BASE, 4, ADC_INVALID, 0, EXTI_CONFIG_PORTF, TIMER_INVALID, TIMER_INVALID}, - /* D68/PE4 */ - {GPIOE_BASE, 4, ADC_INVALID, 0, EXTI_CONFIG_PORTE, TIMER_INVALID, TIMER_INVALID}, - /* D69/PE7 */ - {GPIOE_BASE, 7, ADC_INVALID, 0, EXTI_CONFIG_PORTE, TIMER_INVALID, TIMER_INVALID}, - /* D70/PF5 */ - {GPIOF_BASE, 5, ADC_INVALID, 0, EXTI_CONFIG_PORTF, TIMER_INVALID, TIMER_INVALID}, - /* D71/PE5 */ - {GPIOE_BASE, 5, ADC_INVALID, 0, EXTI_CONFIG_PORTE, TIMER_INVALID, TIMER_INVALID}, - /* D72/PE8 */ - {GPIOE_BASE, 8, ADC_INVALID, 0, EXTI_CONFIG_PORTE, TIMER_INVALID, TIMER_INVALID}, - /* D73/PF12 */ - {GPIOF_BASE, 12, ADC_INVALID, 0, EXTI_CONFIG_PORTF, TIMER_INVALID, TIMER_INVALID}, - /* D74/PE6 */ - {GPIOE_BASE, 6, ADC_INVALID, 0, EXTI_CONFIG_PORTE, TIMER_INVALID, TIMER_INVALID}, - /* D75/PE9 */ - {GPIOE_BASE, 9, ADC_INVALID, 0, EXTI_CONFIG_PORTE, TIMER_INVALID, TIMER_INVALID}, - /* D76/PF13 */ - {GPIOF_BASE, 13, ADC_INVALID, 0, EXTI_CONFIG_PORTF, TIMER_INVALID, TIMER_INVALID}, - /* D77/PE10 */ - {GPIOE_BASE, 10, ADC_INVALID, 0, EXTI_CONFIG_PORTE, TIMER_INVALID, TIMER_INVALID}, - /* D78/PF14 */ - {GPIOF_BASE, 14, ADC_INVALID, 0, EXTI_CONFIG_PORTF, TIMER_INVALID, TIMER_INVALID}, - /* D79/PG9 */ - {GPIOG_BASE, 9, ADC_INVALID, 0, EXTI_CONFIG_PORTG, TIMER_INVALID, TIMER_INVALID}, - /* D80/PE11 */ - {GPIOE_BASE, 11, ADC_INVALID, 0, EXTI_CONFIG_PORTE, TIMER_INVALID, TIMER_INVALID}, - /* D81/PF15 */ - {GPIOF_BASE, 15, ADC_INVALID, 0, EXTI_CONFIG_PORTF, TIMER_INVALID, TIMER_INVALID}, - /* D82/PG10 */ - {GPIOG_BASE, 10, ADC_INVALID, 0, EXTI_CONFIG_PORTG, TIMER_INVALID, TIMER_INVALID}, - /* D83/PE12 */ - {GPIOE_BASE, 12, ADC_INVALID, 0, EXTI_CONFIG_PORTE, TIMER_INVALID, TIMER_INVALID}, - /* D84/PG0 */ - {GPIOG_BASE, 0, ADC_INVALID, 0, EXTI_CONFIG_PORTG, TIMER_INVALID, TIMER_INVALID}, - /* D85/PD5 */ - {GPIOD_BASE, 5, ADC_INVALID, 0, EXTI_CONFIG_PORTD, TIMER_INVALID, TIMER_INVALID}, - /* D86/PE13 */ - {GPIOE_BASE, 13, ADC_INVALID, 0, EXTI_CONFIG_PORTE, TIMER_INVALID, TIMER_INVALID}, - /* D87/PG1 */ - {GPIOG_BASE, 1, ADC_INVALID, 0, EXTI_CONFIG_PORTG, TIMER_INVALID, TIMER_INVALID}, - /* D88/PD4 */ - {GPIOD_BASE, 4, ADC_INVALID, 0, EXTI_CONFIG_PORTD, TIMER_INVALID, TIMER_INVALID}, - /* D89/PE14 */ - {GPIOE_BASE, 14, ADC_INVALID, 0, EXTI_CONFIG_PORTE, TIMER_INVALID, TIMER_INVALID}, - /* D90/PG2 */ - {GPIOG_BASE, 2, ADC_INVALID, 0, EXTI_CONFIG_PORTG, TIMER_INVALID, TIMER_INVALID}, - /* D91/PE1 */ - {GPIOE_BASE, 1, ADC_INVALID, 0, EXTI_CONFIG_PORTE, TIMER_INVALID, TIMER_INVALID}, - /* D92/PE15 */ - {GPIOE_BASE, 15, ADC_INVALID, 0, EXTI_CONFIG_PORTE, TIMER_INVALID, TIMER_INVALID}, - /* D93/PG3 */ - {GPIOG_BASE, 3, ADC_INVALID, 0, EXTI_CONFIG_PORTG, TIMER_INVALID, TIMER_INVALID}, - /* D94/PE0 */ - {GPIOE_BASE, 0, ADC_INVALID, 0, EXTI_CONFIG_PORTE, TIMER_INVALID, TIMER_INVALID}, - /* D95/PD8 */ - {GPIOD_BASE, 8, ADC_INVALID, 0, EXTI_CONFIG_PORTD, TIMER_INVALID, TIMER_INVALID}, - /* D96/PG4 */ - {GPIOG_BASE, 4, ADC_INVALID, 0, EXTI_CONFIG_PORTG, TIMER_INVALID, TIMER_INVALID}, - /* D97/PD9 */ - {GPIOD_BASE, 9, ADC_INVALID, 0, EXTI_CONFIG_PORTD, TIMER_INVALID, TIMER_INVALID}, - /* D98/PG5 */ - {GPIOG_BASE, 5, ADC_INVALID, 0, EXTI_CONFIG_PORTG, TIMER_INVALID, TIMER_INVALID}, - /* D99/PD10 */ - {GPIOD_BASE, 10, ADC_INVALID, 0, EXTI_CONFIG_PORTD, TIMER_INVALID, TIMER_INVALID} - }; - - #define BOARD_INIT do { \ - } while(0) - +#include "maple_native.h" #elif defined(BOARD_maple_mini) - - #define CYCLES_PER_MICROSECOND 72 - #define SYSTICK_RELOAD_VAL 71999 /* takes a cycle to reload */ - - #define BOARD_BUTTON_PIN 32 - #define BOARD_LED_PIN 33 - - static __attribute__ ((unused)) PinMapping PIN_MAP[NR_GPIO_PINS] = { - /* D0/PB11 */ - {GPIOB_BASE, 11, ADC_INVALID, 0, EXTI_CONFIG_PORTB, TIMER_INVALID, TIMER_INVALID}, - /* D1/PB10 */ - {GPIOB_BASE, 10, ADC_INVALID, 0, EXTI_CONFIG_PORTB, TIMER_INVALID, TIMER_INVALID}, - /* D2/PB2 */ - {GPIOB_BASE, 2, ADC_INVALID, 0, EXTI_CONFIG_PORTB, TIMER_INVALID, TIMER_INVALID}, - /* D3/PB0 */ - {GPIOB_BASE, 0, ADC8, TIMER3_CH3_CCR, EXTI_CONFIG_PORTB, TIMER3, 3}, - /* D4/PA7 */ - {GPIOA_BASE, 7, ADC7, TIMER3_CH2_CCR, EXTI_CONFIG_PORTA, TIMER3, 2}, - /* D5/PA6 */ - {GPIOA_BASE, 6, ADC6, TIMER3_CH1_CCR, EXTI_CONFIG_PORTA, TIMER3, 1}, - /* D6/PA5 */ - {GPIOA_BASE, 5, ADC5, 0, EXTI_CONFIG_PORTA, TIMER_INVALID, TIMER_INVALID}, - /* D7/PA4 */ - {GPIOA_BASE, 4, ADC4, 0, EXTI_CONFIG_PORTA, TIMER_INVALID, TIMER_INVALID}, - /* D8/PA3 */ - {GPIOA_BASE, 3, ADC3, TIMER2_CH4_CCR, EXTI_CONFIG_PORTA, TIMER2, 4}, - /* D9/PA2 */ - {GPIOA_BASE, 2, ADC2, TIMER2_CH3_CCR, EXTI_CONFIG_PORTA, TIMER2, 3}, - /* D10/PA1 */ - {GPIOA_BASE, 1, ADC1, TIMER2_CH2_CCR, EXTI_CONFIG_PORTA, TIMER2, 2}, - /* D11/PA0 */ - {GPIOA_BASE, 0, ADC0, TIMER2_CH1_CCR, EXTI_CONFIG_PORTA, TIMER2, 1}, - /* D12/PC15 */ - {GPIOC_BASE, 15, ADC_INVALID, 0, EXTI_CONFIG_PORTC, TIMER_INVALID, TIMER_INVALID}, - /* D13/PC14 */ - {GPIOC_BASE, 14, ADC_INVALID, 0, EXTI_CONFIG_PORTC, TIMER_INVALID, TIMER_INVALID}, - /* D14/PC13 */ - {GPIOC_BASE, 13, ADC_INVALID, 0, EXTI_CONFIG_PORTC, TIMER_INVALID, TIMER_INVALID}, - /* D15/PB7 */ - {GPIOB_BASE, 7, ADC_INVALID, TIMER4_CH2_CCR, EXTI_CONFIG_PORTB, TIMER4, 1}, - /* D16/PB6 */ - {GPIOB_BASE, 6, ADC_INVALID, TIMER4_CH1_CCR, EXTI_CONFIG_PORTB, TIMER4, 1}, - /* D17/PB5 */ - {GPIOB_BASE, 5, ADC_INVALID, 0, EXTI_CONFIG_PORTB, TIMER_INVALID, TIMER_INVALID}, - /* D18/PB4 */ - {GPIOB_BASE, 4, ADC_INVALID, 0, EXTI_CONFIG_PORTB, TIMER_INVALID, TIMER_INVALID}, - /* D19/PB3 */ - {GPIOB_BASE, 3, ADC_INVALID, 0, EXTI_CONFIG_PORTB, TIMER_INVALID, TIMER_INVALID}, - /* D20/PA15 */ - {GPIOA_BASE, 15, ADC_INVALID, 0, EXTI_CONFIG_PORTA, TIMER_INVALID, TIMER_INVALID}, - /* D21/PA14 */ - {GPIOA_BASE, 14, ADC_INVALID, 0, EXTI_CONFIG_PORTA, TIMER_INVALID, TIMER_INVALID}, - /* D22/PA13 */ - {GPIOA_BASE, 13, ADC_INVALID, 0, EXTI_CONFIG_PORTA, TIMER_INVALID, TIMER_INVALID}, - /* D23/PA12 */ - {GPIOA_BASE, 12, ADC_INVALID, 0, EXTI_CONFIG_PORTA, TIMER_INVALID, TIMER_INVALID}, - /* D24/PA11 */ - {GPIOA_BASE, 11, ADC_INVALID, TIMER1_CH4_CCR, EXTI_CONFIG_PORTA, TIMER1, 4}, - /* D25/PA10 */ - {GPIOA_BASE, 10, ADC_INVALID, TIMER1_CH3_CCR, EXTI_CONFIG_PORTA, TIMER1, 3}, - /* D26/PA9 */ - {GPIOA_BASE, 9, ADC_INVALID, TIMER1_CH2_CCR, EXTI_CONFIG_PORTA, TIMER2, 2}, - /* D27/PA8 */ - {GPIOA_BASE, 8, ADC_INVALID, TIMER1_CH1_CCR, EXTI_CONFIG_PORTB, TIMER1, 1}, - /* D28/PB15 */ - {GPIOB_BASE, 15, ADC_INVALID, 0, EXTI_CONFIG_PORTB, TIMER_INVALID, TIMER_INVALID}, - /* D29/PB14 */ - {GPIOB_BASE, 14, ADC_INVALID, 0, EXTI_CONFIG_PORTB, TIMER_INVALID, TIMER_INVALID}, - /* D30/PB13 */ - {GPIOB_BASE, 13, ADC_INVALID, 0, EXTI_CONFIG_PORTB, TIMER_INVALID, TIMER_INVALID}, - /* D31/PB12 */ - {GPIOB_BASE, 12, ADC_INVALID, 0, EXTI_CONFIG_PORTB, TIMER_INVALID, TIMER_INVALID}, - /* D32/PB8 */ - {GPIOB_BASE, 8, ADC_INVALID, TIMER4_CH3_CCR, EXTI_CONFIG_PORTB, TIMER4, 3}, - /* D33/PB1 */ - {GPIOB_BASE, 1, ADC9, TIMER3_CH4_CCR, EXTI_CONFIG_PORTB, TIMER3, 4}, - }; - - /* since we want the Serial Wire/JTAG pins as GPIOs, disable both - SW and JTAG debug support */ - /* don't use __clear_bits here! */ - #define BOARD_INIT \ - do { \ - *AFIO_MAPR = (*AFIO_MAPR | BIT(26)) & ~(BIT(25) | BIT(24)); \ - } while (0) - +#include "maple_mini.h" +#elif defined(BOARD_maple_RET6) +/* + * **NOT** MAPLE REV6. This the **Maple RET6 EDITION**, which is a + * Maple with an STM32F103RET6 (...RET6) instead of an STM32F103RBT6 + * (...RBT6) on it. Maple Rev6 (as of March 2011) DOES NOT EXIST. + */ +#include "maple_RET6.h" #else - +/* + * TODO turn this into a warning so people can: + * + * #include "my_board_config.h" + * #include "wirish.h" + * + * This will enable third-party board support without requiring that + * anybody hack around in libmaple itself. + */ #error "Board type has not been selected correctly." - #endif -#ifdef __cplusplus -} // extern "C" -#endif +/* Set derived definitions */ -#endif +#define CLOCK_SPEED_MHZ CYCLES_PER_MICROSECOND +#define CLOCK_SPEED_HZ (CLOCK_SPEED_MHZ * 1000000UL) +#endif diff --git a/wirish/boards/maple.cpp b/wirish/boards/maple.cpp new file mode 100644 index 0000000..2d35e7b --- /dev/null +++ b/wirish/boards/maple.cpp @@ -0,0 +1,115 @@ +/****************************************************************************** + * The MIT License + * + * Copyright (c) 2011 LeafLabs, LLC. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + *****************************************************************************/ + +/** + * @file maple.cpp + * @author Marti Bolivar <mbolivar@leaflabs.com> + * @brief Maple PIN_MAP and boardInit(). + */ + +#include "gpio.h" +#include "timer.h" + +#include "maple.h" + +#ifdef BOARD_maple + +void boardInit(void) { +} + +extern const stm32_pin_info PIN_MAP[BOARD_NR_GPIO_PINS] = { + + /* Top header */ + + {GPIOA, TIMER2, ADC1, 3, 4, 3}, /* D0/PA3 */ + {GPIOA, TIMER2, ADC1, 2, 3, 2}, /* D1/PA2 */ + {GPIOA, TIMER2, ADC1, 0, 1, 0}, /* D2/PA0 */ + {GPIOA, TIMER2, ADC1, 1, 2, 1}, /* D3/PA1 */ + {GPIOB, NULL, NULL, 5, 0, ADCx}, /* D4/PB5 */ + {GPIOB, TIMER4, NULL, 6, 1, ADCx}, /* D5/PB6 */ + {GPIOA, TIMER1, NULL, 8, 1, ADCx}, /* D6/PA8 */ + {GPIOA, TIMER1, NULL, 9, 2, ADCx}, /* D7/PA9 */ + {GPIOA, TIMER1, NULL, 10, 3, ADCx}, /* D8/PA10 */ + {GPIOB, TIMER4, NULL, 7, 2, ADCx}, /* D9/PB7 */ + {GPIOA, NULL, ADC1, 4, 0, 4}, /* D10/PA4 */ + {GPIOA, TIMER3, ADC1, 7, 2, 7}, /* D11/PA7 */ + {GPIOA, TIMER3, ADC1, 6, 1, 6}, /* D12/PA6 */ + {GPIOA, NULL, ADC1, 5, 0, 5}, /* D13/PA5 (LED) */ + {GPIOB, TIMER4, NULL, 8, 3, ADCx}, /* D14/PB8 */ + + /* Little header */ + + {GPIOC, NULL, ADC1, 0, 0, 10}, /* D15/PC0 */ + {GPIOC, NULL, ADC1, 1, 0, 11}, /* D16/PC1 */ + {GPIOC, NULL, ADC1, 2, 0, 12}, /* D17/PC2 */ + {GPIOC, NULL, ADC1, 3, 0, 13}, /* D18/PC3 */ + {GPIOC, NULL, ADC1, 4, 0, 14}, /* D19/PC4 */ + {GPIOC, NULL, ADC1, 5, 0, 15}, /* D20/PC5 */ + + /* External header */ + + {GPIOC, NULL, NULL, 13, 0, ADCx}, /* D21/PC13 */ + {GPIOC, NULL, NULL, 14, 0, ADCx}, /* D22/PC14 */ + {GPIOC, NULL, NULL, 15, 0, ADCx}, /* D23/PC15 */ + {GPIOB, TIMER4, NULL, 9, 4, ADCx}, /* D24/PB9 */ + {GPIOD, NULL, NULL, 2, 0, ADCx}, /* D25/PD2 */ + {GPIOC, NULL, NULL, 10, 0, ADCx}, /* D26/PC10 */ + {GPIOB, TIMER3, ADC1, 0, 3, 8}, /* D27/PB0 */ + {GPIOB, TIMER3, ADC1, 1, 4, 9}, /* D28/PB1 */ + {GPIOB, NULL, NULL, 10, 0, ADCx}, /* D29/PB10 */ + {GPIOB, NULL, NULL, 11, 0, ADCx}, /* D30/PB11 */ + {GPIOB, NULL, NULL, 12, 0, ADCx}, /* D31/PB12 */ + {GPIOB, NULL, NULL, 13, 0, ADCx}, /* D32/PB13 */ + {GPIOB, NULL, NULL, 14, 0, ADCx}, /* D33/PB14 */ + {GPIOB, NULL, NULL, 15, 0, ADCx}, /* D34/PB15 */ + {GPIOC, NULL, NULL, 6, 0, ADCx}, /* D35/PC6 */ + {GPIOC, NULL, NULL, 7, 0, ADCx}, /* D36/PC7 */ + {GPIOC, NULL, NULL, 8, 0, ADCx}, /* D37/PC8 */ + {GPIOC, NULL, NULL, 9, 0, ADCx}, /* D38/PC9 (BUT) */ + + /* JTAG header */ + + {GPIOA, NULL, NULL, 13, 0, ADCx}, /* D39/PA13 */ + {GPIOA, NULL, NULL, 14, 0, ADCx}, /* D40/PA14 */ + {GPIOA, NULL, NULL, 15, 0, ADCx}, /* D41/PA15 */ + {GPIOB, NULL, NULL, 3, 0, ADCx}, /* D42/PB3 */ + {GPIOB, NULL, NULL, 4, 0, ADCx}, /* D43/PB4 */ +}; + +extern const uint8 boardPWMPins[] __FLASH__ = { + 0, 1, 2, 3, 5, 6, 7, 8, 9, 11, 12, 14, 24, 25, 27, 28 +}; + +extern const uint8 boardADCPins[] __FLASH__ = { + 0, 1, 2, 10, 11, 12, 13, 15, 16, 17, 18, 19, 20, 27, 28 +}; + +extern const uint8 boardUsedPins[] __FLASH__ = { + BOARD_LED_PIN, BOARD_BUTTON_PIN, BOARD_JTMS_SWDIO_PIN, + BOARD_JTCK_SWCLK_PIN, BOARD_JTDI_PIN, BOARD_JTDO_PIN, BOARD_NJTRST_PIN +}; + +#endif diff --git a/wirish/boards/maple.h b/wirish/boards/maple.h new file mode 100644 index 0000000..4b55f7a --- /dev/null +++ b/wirish/boards/maple.h @@ -0,0 +1,91 @@ +/****************************************************************************** + * The MIT License + * + * Copyright (c) 2011 LeafLabs, LLC. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + *****************************************************************************/ + +/** + * @file maple.h + * @author Marti Bolivar <mbolivar@leaflabs.com> + * @brief Private include file for Maple in boards.h + */ + +#include "wirish_types.h" + +#ifndef _BOARD_MAPLE_H_ +#define _BOARD_MAPLE_H_ + +#define CYCLES_PER_MICROSECOND 72 +#define SYSTICK_RELOAD_VAL 71999 /* takes a cycle to reload */ + +#define BOARD_BUTTON_PIN 38 +#define BOARD_LED_PIN 13 + +/* Number of USARTs/UARTs whose pins are broken out to headers */ +#define BOARD_NR_USARTS 3 + +/* Default USART pin numbers (not considering AFIO remap) */ +#define BOARD_USART1_TX_PIN 7 +#define BOARD_USART1_RX_PIN 8 +#define BOARD_USART2_TX_PIN 1 +#define BOARD_USART2_RX_PIN 0 +#define BOARD_USART3_TX_PIN 29 +#define BOARD_USART3_RX_PIN 30 + +/* Number of SPI ports */ +#define BOARD_NR_SPI 2 + +/* Default SPI pin numbers (not considering AFIO remap) */ +#define BOARD_SPI1_NSS_PIN 10 +#define BOARD_SPI1_MOSI_PIN 11 +#define BOARD_SPI1_MISO_PIN 12 +#define BOARD_SPI1_SCK_PIN 13 +#define BOARD_SPI2_NSS_PIN 31 +#define BOARD_SPI2_MOSI_PIN 34 +#define BOARD_SPI2_MISO_PIN 33 +#define BOARD_SPI2_SCK_PIN 32 + +/* Total number of GPIO pins that are broken out to headers and + * intended for general use. */ +#define BOARD_NR_GPIO_PINS 44 + +/* Number of pins capable of PWM output */ +#define BOARD_NR_PWM_PINS 16 + +/* Number of pins capable of ADC conversion */ +#define BOARD_NR_ADC_PINS 15 + +/* Number of pins already connected to external hardware. For Maple, + * these are just BOARD_LED_PIN and BOARD_BUTTON_PIN. */ +#define BOARD_NR_USED_PINS 7 + +/* + * Debug port pins + */ +#define BOARD_JTMS_SWDIO_PIN 39 +#define BOARD_JTCK_SWCLK_PIN 40 +#define BOARD_JTDI_PIN 41 +#define BOARD_JTDO_PIN 42 +#define BOARD_NJTRST_PIN 43 + +#endif diff --git a/wirish/boards/maple_RET6.cpp b/wirish/boards/maple_RET6.cpp new file mode 100644 index 0000000..d5d3d32 --- /dev/null +++ b/wirish/boards/maple_RET6.cpp @@ -0,0 +1,112 @@ +/****************************************************************************** + * The MIT License + * + * Copyright (c) 2011 LeafLabs, LLC. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + *****************************************************************************/ + +/** + * @file maple_RET6.cpp + * @author Marti Bolivar <mbolivar@leaflabs.com> + * @brief Maple RET6 Edition PIN_MAP and boardInit() + */ + +#include "maple_RET6.h" + +#ifdef BOARD_maple_RET6 + +void boardInit(void) { +} + +extern const stm32_pin_info PIN_MAP[BOARD_NR_GPIO_PINS] = { + + /* Top header */ + + {GPIOA, TIMER2, ADC1, 3, 4, 3}, /* D0/PA3 */ + {GPIOA, TIMER2, ADC1, 2, 3, 2}, /* D1/PA2 */ + {GPIOA, TIMER2, ADC1, 0, 1, 0}, /* D2/PA0 */ + {GPIOA, TIMER2, ADC1, 1, 2, 1}, /* D3/PA1 */ + {GPIOB, NULL, NULL, 5, 0, ADCx}, /* D4/PB5 */ + {GPIOB, TIMER4, NULL, 6, 1, ADCx}, /* D5/PB6 */ + {GPIOA, TIMER1, NULL, 8, 1, ADCx}, /* D6/PA8 */ + {GPIOA, TIMER1, NULL, 9, 2, ADCx}, /* D7/PA9 */ + {GPIOA, TIMER1, NULL, 10, 3, ADCx}, /* D8/PA10 */ + {GPIOB, TIMER4, NULL, 7, 2, ADCx}, /* D9/PB7 */ + {GPIOA, NULL, ADC1, 4, 0, 4}, /* D10/PA4 */ + {GPIOA, TIMER3, ADC1, 7, 2, 7}, /* D11/PA7 */ + {GPIOA, TIMER3, ADC1, 6, 1, 6}, /* D12/PA6 */ + {GPIOA, NULL, ADC1, 5, 0, 5}, /* D13/PA5 (LED) */ + {GPIOB, TIMER4, NULL, 8, 3, ADCx}, /* D14/PB8 */ + + /* Little header */ + + {GPIOC, NULL, ADC1, 0, 0, 10}, /* D15/PC0 */ + {GPIOC, NULL, ADC1, 1, 0, 11}, /* D16/PC1 */ + {GPIOC, NULL, ADC1, 2, 0, 12}, /* D17/PC2 */ + {GPIOC, NULL, ADC1, 3, 0, 13}, /* D18/PC3 */ + {GPIOC, NULL, ADC1, 4, 0, 14}, /* D19/PC4 */ + {GPIOC, NULL, ADC1, 5, 0, 15}, /* D20/PC5 */ + + /* External header */ + + {GPIOC, NULL, NULL, 13, 0, ADCx}, /* D21/PC13 */ + {GPIOC, NULL, NULL, 14, 0, ADCx}, /* D22/PC14 */ + {GPIOC, NULL, NULL, 15, 0, ADCx}, /* D23/PC15 */ + {GPIOB, TIMER4, NULL, 9, 4, ADCx}, /* D24/PB9 */ + {GPIOD, NULL, NULL, 2, 0, ADCx}, /* D25/PD2 */ + {GPIOC, NULL, NULL, 10, 0, ADCx}, /* D26/PC10 */ + {GPIOB, TIMER3, ADC1, 0, 3, 8}, /* D27/PB0 */ + {GPIOB, TIMER3, ADC1, 1, 4, 9}, /* D28/PB1 */ + {GPIOB, NULL, NULL, 10, 0, ADCx}, /* D29/PB10 */ + {GPIOB, NULL, NULL, 11, 0, ADCx}, /* D30/PB11 */ + {GPIOB, NULL, NULL, 12, 0, ADCx}, /* D31/PB12 */ + {GPIOB, NULL, NULL, 13, 0, ADCx}, /* D32/PB13 */ + {GPIOB, NULL, NULL, 14, 0, ADCx}, /* D33/PB14 */ + {GPIOB, NULL, NULL, 15, 0, ADCx}, /* D34/PB15 */ + {GPIOC, TIMER8, NULL, 6, 1, ADCx}, /* D35/PC6 */ + {GPIOC, TIMER8, NULL, 7, 2, ADCx}, /* D36/PC7 */ + {GPIOC, TIMER8, NULL, 8, 3, ADCx}, /* D37/PC8 */ + {GPIOC, TIMER8, NULL, 9, 4, ADCx}, /* D38/PC9 (BUT) */ + + /* JTAG header */ + + {GPIOA, NULL, NULL, 13, 0, ADCx}, /* D39/PA13 */ + {GPIOA, NULL, NULL, 14, 0, ADCx}, /* D40/PA14 */ + {GPIOA, NULL, NULL, 15, 0, ADCx}, /* D41/PA15 */ + {GPIOB, NULL, NULL, 3, 0, ADCx}, /* D42/PB3 */ + {GPIOB, NULL, NULL, 4, 0, ADCx}, /* D43/PB4 */ +}; + +extern const uint8 boardPWMPins[BOARD_NR_PWM_PINS] __FLASH__ = { + 0, 1, 2, 3, 5, 6, 7, 8, 9, 11, 12, 14, 24, 25, 27, 28 +}; + +extern const uint8 boardADCPins[BOARD_NR_ADC_PINS] __FLASH__ = { + 0, 1, 2, 10, 11, 12, 13, 15, 16, 17, 18, 19, 20, 27, 28 +}; + +extern const uint8 boardUsedPins[BOARD_NR_USED_PINS] __FLASH__ = { + BOARD_LED_PIN, BOARD_BUTTON_PIN, BOARD_JTMS_SWDIO_PIN, + BOARD_JTCK_SWCLK_PIN, BOARD_JTDI_PIN, BOARD_JTDO_PIN, BOARD_NJTRST_PIN +}; + +#endif diff --git a/wirish/boards/maple_RET6.h b/wirish/boards/maple_RET6.h new file mode 100644 index 0000000..4195857 --- /dev/null +++ b/wirish/boards/maple_RET6.h @@ -0,0 +1,79 @@ +/****************************************************************************** + * The MIT License + * + * Copyright (c) 2011 LeafLabs, LLC. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + *****************************************************************************/ + +/** + * @file maple_RET6.h + * @author Marti Bolivar <mbolivar@leaflabs.com> + * @brief Private include file for Maple RET6 Edition in boards.h + * + * See maple.h for more information on these definitions. + */ + +#include "gpio.h" +#include "timer.h" + +#include "wirish_types.h" + +#ifndef _BOARDS_MAPLE_RET6_H_ +#define _BOARDS_MAPLE_RET6_H_ + +#define CYCLES_PER_MICROSECOND 72 +#define SYSTICK_RELOAD_VAL 71999 /* takes a cycle to reload */ + +#define BOARD_BUTTON_PIN 38 +#define BOARD_LED_PIN 13 + +/* Note: UART4 and UART5 have pins which aren't broken out :( */ +#define BOARD_NR_USARTS 3 +#define BOARD_USART1_TX_PIN 7 +#define BOARD_USART1_RX_PIN 8 +#define BOARD_USART2_TX_PIN 1 +#define BOARD_USART2_RX_PIN 0 +#define BOARD_USART3_TX_PIN 29 +#define BOARD_USART3_RX_PIN 30 + +#define BOARD_NR_SPI 2 +#define BOARD_SPI1_NSS_PIN 10 +#define BOARD_SPI1_MOSI_PIN 11 +#define BOARD_SPI1_MISO_PIN 12 +#define BOARD_SPI1_SCK_PIN 13 +#define BOARD_SPI2_NSS_PIN 31 +#define BOARD_SPI2_MOSI_PIN 34 +#define BOARD_SPI2_MISO_PIN 33 +#define BOARD_SPI2_SCK_PIN 32 + +#define BOARD_NR_GPIO_PINS 44 +#define BOARD_NR_PWM_PINS 16 +#define BOARD_NR_ADC_PINS 15 +#define BOARD_NR_USED_PINS 7 + +#define BOARD_JTMS_SWDIO_PIN 39 +#define BOARD_JTCK_SWCLK_PIN 40 +#define BOARD_JTDI_PIN 41 +#define BOARD_JTDO_PIN 42 +#define BOARD_NJTRST_PIN 43 + +#endif diff --git a/wirish/boards/maple_mini.cpp b/wirish/boards/maple_mini.cpp new file mode 100644 index 0000000..3bc250b --- /dev/null +++ b/wirish/boards/maple_mini.cpp @@ -0,0 +1,103 @@ +/****************************************************************************** + * The MIT License + * + * Copyright (c) 2011 LeafLabs, LLC. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + *****************************************************************************/ + +/** + * @file maple_mini.cpp + * @author Marti Bolivar <mbolivar@leaflabs.com> + * @brief Maple Mini PIN_MAP and boardInit(). + */ + +#include "maple_mini.h" +#include "gpio.h" +#include "wirish_debug.h" + +#ifdef BOARD_maple_mini + +/* Since we want the Serial Wire/JTAG pins as GPIOs, disable both SW + * and JTAG debug support */ +void boardInit(void) { + disableDebugPorts(); +} + +extern const stm32_pin_info PIN_MAP[BOARD_NR_GPIO_PINS] = { + + /* Top header */ + + {GPIOB, NULL, NULL, 11, 0, ADCx}, /* D0/PB11 */ + {GPIOB, NULL, NULL, 10, 0, ADCx}, /* D1/PB10 */ + {GPIOB, NULL, NULL, 2, 0, ADCx}, /* D2/PB2 */ + {GPIOB, TIMER3, ADC1, 0, 3, 8}, /* D3/PB0 */ + {GPIOA, TIMER3, ADC1, 7, 2, 7}, /* D4/PA7 */ + {GPIOA, TIMER3, ADC1, 6, 1, 6}, /* D5/PA6 */ + {GPIOA, NULL, ADC1, 5, 0, 5}, /* D6/PA5 */ + {GPIOA, NULL, ADC1, 4, 0, 4}, /* D7/PA4 */ + {GPIOA, TIMER2, ADC1, 3, 4, 3}, /* D8/PA3 */ + {GPIOA, TIMER2, ADC1, 2, 3, 2}, /* D9/PA2 */ + {GPIOA, TIMER2, ADC1, 1, 2, 1}, /* D10/PA1 */ + {GPIOA, TIMER2, ADC1, 0, 1, 0}, /* D11/PA0 */ + {GPIOC, NULL, NULL, 15, 0, ADCx}, /* D12/PC15 */ + {GPIOC, NULL, NULL, 14, 0, ADCx}, /* D13/PC14 */ + {GPIOC, NULL, NULL, 13, 0, ADCx}, /* D14/PC13 */ + + /* Bottom header */ + + {GPIOB, TIMER4, NULL, 7, 2, ADCx}, /* D15/PB7 */ + {GPIOB, TIMER4, NULL, 6, 1, ADCx}, /* D16/PB6 */ + {GPIOB, NULL, NULL, 5, 0, ADCx}, /* D17/PB5 */ + {GPIOB, NULL, NULL, 4, 0, ADCx}, /* D18/PB4 */ + {GPIOB, NULL, NULL, 3, 0, ADCx}, /* D19/PB3 */ + {GPIOA, NULL, NULL, 15, 0, ADCx}, /* D20/PA15 */ + {GPIOA, NULL, NULL, 14, 0, ADCx}, /* D21/PA14 */ + {GPIOA, NULL, NULL, 13, 0, ADCx}, /* D22/PA13 */ + {GPIOA, NULL, NULL, 12, 0, ADCx}, /* D23/PA12 */ + {GPIOA, TIMER1, NULL, 11, 4, ADCx}, /* D24/PA11 */ + {GPIOA, TIMER1, NULL, 10, 3, ADCx}, /* D25/PA10 */ + {GPIOA, TIMER1, NULL, 9, 2, ADCx}, /* D26/PA9 */ + {GPIOA, TIMER1, NULL, 8, 1, ADCx}, /* D27/PA8 */ + {GPIOB, NULL, NULL, 15, 0, ADCx}, /* D28/PB15 */ + {GPIOB, NULL, NULL, 14, 0, ADCx}, /* D29/PB14 */ + {GPIOB, NULL, NULL, 13, 0, ADCx}, /* D30/PB13 */ + {GPIOB, NULL, NULL, 12, 0, ADCx}, /* D31/PB12 */ + {GPIOB, TIMER4, NULL, 8, 3, ADCx}, /* D32/PB8 */ + {GPIOB, TIMER3, ADC1, 1, 4, 9}, /* D33/PB1 */ +}; + +extern const uint8 boardPWMPins[BOARD_NR_PWM_PINS] __FLASH__ = { + 3, 4, 5, 8, 9, 10, 11, 15, 16, 25, 26, 27 +}; + +extern const uint8 boardADCPins[BOARD_NR_ADC_PINS] __FLASH__ = { + 3, 4, 5, 6, 7, 8, 9, 10, 11, 33 // NB: 33 is BOARD_LED_PIN +}; + +#define USB_DP 23 +#define USB_DM 24 + +extern const uint8 boardUsedPins[BOARD_NR_USED_PINS] __FLASH__ = { + BOARD_LED_PIN, BOARD_BUTTON_PIN, USB_DP, USB_DM +}; + +#endif diff --git a/wirish/boards/maple_mini.h b/wirish/boards/maple_mini.h new file mode 100644 index 0000000..fde7f98 --- /dev/null +++ b/wirish/boards/maple_mini.h @@ -0,0 +1,78 @@ +/****************************************************************************** + * The MIT License + * + * Copyright (c) 2011 LeafLabs, LLC. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + *****************************************************************************/ + +/** + * @file maple_mini.h + * @author Marti Bolivar <mbolivar@leaflabs.com> + * @brief Private include file for Maple Mini in boards.h + * + * See maple.h for more information on these definitions. + */ + +#include "gpio.h" +#include "timer.h" + +#include "wirish_types.h" + +#ifndef _BOARD_MAPLE_MINI_H_ +#define _BOARD_MAPLE_MINI_H_ + +#define CYCLES_PER_MICROSECOND 72 +#define SYSTICK_RELOAD_VAL 71999 /* takes a cycle to reload */ + +#define BOARD_BUTTON_PIN 32 +#define BOARD_LED_PIN 33 + +#define BOARD_NR_USARTS 3 +#define BOARD_USART1_TX_PIN 26 +#define BOARD_USART1_RX_PIN 25 +#define BOARD_USART2_TX_PIN 9 +#define BOARD_USART2_RX_PIN 8 +#define BOARD_USART3_TX_PIN 1 +#define BOARD_USART3_RX_PIN 0 + +#define BOARD_NR_SPI 2 +#define BOARD_SPI1_NSS_PIN 7 +#define BOARD_SPI1_MOSI_PIN 4 +#define BOARD_SPI1_MISO_PIN 5 +#define BOARD_SPI1_SCK_PIN 6 +#define BOARD_SPI2_NSS_PIN 31 +#define BOARD_SPI2_MOSI_PIN 28 +#define BOARD_SPI2_MISO_PIN 29 +#define BOARD_SPI2_SCK_PIN 30 + +#define BOARD_NR_GPIO_PINS 34 +#define BOARD_NR_PWM_PINS 12 +#define BOARD_NR_ADC_PINS 10 +#define BOARD_NR_USED_PINS 4 + +#define BOARD_JTMS_SWDIO_PIN 22 +#define BOARD_JTCK_SWCLK_PIN 21 +#define BOARD_JTDI_PIN 20 +#define BOARD_JTDO_PIN 19 +#define BOARD_NJTRST_PIN 18 + +#endif diff --git a/wirish/boards/maple_native.cpp b/wirish/boards/maple_native.cpp new file mode 100644 index 0000000..fa36240 --- /dev/null +++ b/wirish/boards/maple_native.cpp @@ -0,0 +1,199 @@ +/****************************************************************************** + * The MIT License + * + * Copyright (c) 2011 LeafLabs, LLC. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + *****************************************************************************/ + +/** + * @file maple_native.cpp + * @author Marti Bolivar <mbolivar@leaflabs.com> + * @brief Maple Native PIN_MAP and boardInit(). + */ + +#include "maple_native.h" + +#include "fsmc.h" +#include "gpio.h" +#include "rcc.h" +#include "timer.h" + +#include "wirish_types.h" + +#ifdef BOARD_maple_native + +void initSRAMChip(void); + +void boardInit(void) { + initSRAMChip(); +} + +extern const stm32_pin_info PIN_MAP[BOARD_NR_GPIO_PINS] = { + + /* Top header */ + + {GPIOB, NULL, NULL, 10, 0, ADCx}, /* D0/PB10 */ + {GPIOB, NULL, NULL, 11, 0, ADCx}, /* D1/PB11 */ + {GPIOB, NULL, NULL, 12, 0, ADCx}, /* D2/PB12 */ + {GPIOB, NULL, NULL, 13, 0, ADCx}, /* D3/PB13 */ + {GPIOB, NULL, NULL, 14, 0, ADCx}, /* D4/PB14 */ + {GPIOB, NULL, NULL, 15, 0, ADCx}, /* D5/PB15 */ + {GPIOG, NULL, NULL, 15, 0, ADCx}, /* D6/PG15 (BUT) */ + {GPIOC, NULL, ADC1, 0, 0, 10}, /* D7/PC0 */ + {GPIOC, NULL, ADC1, 1, 0, 11}, /* D8/PC1 */ + {GPIOC, NULL, ADC1, 2, 0, 12}, /* D9/PC2 */ + {GPIOC, NULL, ADC1, 3, 0, 13}, /* D10/PC3 */ + {GPIOC, NULL, ADC1, 4, 0, 14}, /* D11/PC4 */ + {GPIOC, NULL, ADC1, 5, 0, 15}, /* D12/PC5 */ + {GPIOC, TIMER8, NULL, 6, 1, ADCx}, /* D13/PC6 */ + {GPIOC, TIMER8, NULL, 7, 2, ADCx}, /* D14/PC7 */ + {GPIOC, TIMER8, NULL, 8, 3, ADCx}, /* D15/PC8 */ + {GPIOC, TIMER8, NULL, 9, 4, ADCx}, /* D16/PC9 */ + {GPIOC, NULL, NULL, 10, 0, ADCx}, /* D17/PC10 */ + {GPIOC, NULL, NULL, 11, 0, ADCx}, /* D18/PC11 */ + {GPIOC, NULL, NULL, 12, 0, ADCx}, /* D19/PC12 */ + {GPIOC, NULL, NULL, 13, 0, ADCx}, /* D20/PC13 */ + {GPIOC, NULL, NULL, 14, 0, ADCx}, /* D21/PC14 */ + {GPIOC, NULL, NULL, 15, 0, ADCx}, /* D22/PC15 (LED) */ + {GPIOA, TIMER1, NULL, 8, 1, ADCx}, /* D23/PA8 */ + {GPIOA, TIMER1, NULL, 9, 2, ADCx}, /* D24/PA9 */ + {GPIOA, TIMER1, NULL, 10, 3, ADCx}, /* D25/PA10 */ + {GPIOB, TIMER4, NULL, 9, 4, ADCx}, /* D26/PB9 */ + + /* Bottom header */ + /* Note: D{48, 49, 50, 51} are also TIMER2_CH{1, 2, 3, 4}, respectively. */ + /* TODO remap timer 2 in boardInit(); make the appropriate changes here */ + + {GPIOD, NULL, NULL, 2, 0, ADCx}, /* D27/PD2 */ + {GPIOD, NULL, NULL, 3, 0, ADCx}, /* D28/PD3 */ + {GPIOD, NULL, NULL, 6, 0, ADCx}, /* D29/PD6 */ + {GPIOG, NULL, NULL, 11, 0, ADCx}, /* D30/PG11 */ + {GPIOG, NULL, NULL, 12, 0, ADCx}, /* D31/PG12 */ + {GPIOG, NULL, NULL, 13, 0, ADCx}, /* D32/PG13 */ + {GPIOG, NULL, NULL, 14, 0, ADCx}, /* D33/PG14 */ + {GPIOG, NULL, NULL, 8, 0, ADCx}, /* D34/PG8 */ + {GPIOG, NULL, NULL, 7, 0, ADCx}, /* D35/PG7 */ + {GPIOG, NULL, NULL, 6, 0, ADCx}, /* D36/PG6 */ + {GPIOB, NULL, NULL, 5, 0, ADCx}, /* D37/PB5 */ + {GPIOB, TIMER4, NULL, 6, 1, ADCx}, /* D38/PB6 */ + {GPIOB, TIMER4, NULL, 7, 2, ADCx}, /* D39/PB7 */ + {GPIOF, NULL, NULL, 11, 0, ADCx}, /* D40/PF11 */ + {GPIOF, NULL, ADC3, 6, 0, 4}, /* D41/PF6 */ + {GPIOF, NULL, ADC3, 7, 0, 5}, /* D42/PF7 */ + {GPIOF, NULL, ADC3, 8, 0, 6}, /* D43/PF8 */ + {GPIOF, NULL, ADC3, 9, 0, 7}, /* D44/PF9 */ + {GPIOF, NULL, ADC3, 10, 0, 8}, /* D45/PF10 */ + {GPIOB, TIMER3, ADC1, 1, 4, 9}, /* D46/PB1 */ + {GPIOB, TIMER3, ADC1, 0, 3, 8}, /* D47/PB0 */ + {GPIOA, TIMER5, ADC1, 0, 1, 0}, /* D48/PA0 */ + {GPIOA, TIMER5, ADC1, 1, 2, 1}, /* D49/PA1 */ + {GPIOA, TIMER5, ADC1, 2, 3, 2}, /* D50/PA2 */ + {GPIOA, TIMER5, ADC1, 3, 4, 3}, /* D51/PA3 */ + {GPIOA, NULL, ADC1, 4, 0, 4}, /* D52/PA4 */ + {GPIOA, NULL, ADC1, 5, 0, 5}, /* D53/PA5 */ + {GPIOA, TIMER3, ADC1, 6, 1, 6}, /* D54/PA6 */ + {GPIOA, TIMER3, ADC1, 7, 2, 7}, /* D55/PA7 */ + + /* Right (triple) header */ + + {GPIOF, NULL, NULL, 0, 0, ADCx}, /* D56/PF0 */ + {GPIOD, NULL, NULL, 11, 0, ADCx}, /* D57/PD11 */ + {GPIOD, NULL, NULL, 14, 0, ADCx}, /* D58/PD14 */ + {GPIOF, NULL, NULL, 1, 0, ADCx}, /* D59/PF1 */ + {GPIOD, NULL, NULL, 12, 0, ADCx}, /* D60/PD12 */ + {GPIOD, NULL, NULL, 15, 0, ADCx}, /* D61/PD15 */ + {GPIOF, NULL, NULL, 2, 0, ADCx}, /* D62/PF2 */ + {GPIOD, NULL, NULL, 13, 0, ADCx}, /* D63/PD13 */ + {GPIOD, NULL, NULL, 0, 0, ADCx}, /* D64/PD0 */ + {GPIOF, NULL, NULL, 3, 0, ADCx}, /* D65/PF3 */ + {GPIOE, NULL, NULL, 3, 0, ADCx}, /* D66/PE3 */ + {GPIOD, NULL, NULL, 1, 0, ADCx}, /* D67/PD1 */ + {GPIOF, NULL, NULL, 4, 0, ADCx}, /* D68/PF4 */ + {GPIOE, NULL, NULL, 4, 0, ADCx}, /* D69/PE4 */ + {GPIOE, NULL, NULL, 7, 0, ADCx}, /* D70/PE7 */ + {GPIOF, NULL, NULL, 5, 0, ADCx}, /* D71/PF5 */ + {GPIOE, NULL, NULL, 5, 0, ADCx}, /* D72/PE5 */ + {GPIOE, NULL, NULL, 8, 0, ADCx}, /* D73/PE8 */ + {GPIOF, NULL, NULL, 12, 0, ADCx}, /* D74/PF12 */ + {GPIOE, NULL, NULL, 6, 0, ADCx}, /* D75/PE6 */ + {GPIOE, NULL, NULL, 9, 0, ADCx}, /* D76/PE9 */ + {GPIOF, NULL, NULL, 13, 0, ADCx}, /* D77/PF13 */ + {GPIOE, NULL, NULL, 10, 0, ADCx}, /* D78/PE10 */ + {GPIOF, NULL, NULL, 14, 0, ADCx}, /* D79/PF14 */ + {GPIOG, NULL, NULL, 9, 0, ADCx}, /* D80/PG9 */ + {GPIOE, NULL, NULL, 11, 0, ADCx}, /* D81/PE11 */ + {GPIOF, NULL, NULL, 15, 0, ADCx}, /* D82/PF15 */ + {GPIOG, NULL, NULL, 10, 0, ADCx}, /* D83/PG10 */ + {GPIOE, NULL, NULL, 12, 0, ADCx}, /* D84/PE12 */ + {GPIOG, NULL, NULL, 0, 0, ADCx}, /* D85/PG0 */ + {GPIOD, NULL, NULL, 5, 0, ADCx}, /* D86/PD5 */ + {GPIOE, NULL, NULL, 13, 0, ADCx}, /* D87/PE13 */ + {GPIOG, NULL, NULL, 1, 0, ADCx}, /* D88/PG1 */ + {GPIOD, NULL, NULL, 4, 0, ADCx}, /* D89/PD4 */ + {GPIOE, NULL, NULL, 14, 0, ADCx}, /* D90/PE14 */ + {GPIOG, NULL, NULL, 2, 0, ADCx}, /* D91/PG2 */ + {GPIOE, NULL, NULL, 1, 0, ADCx}, /* D92/PE1 */ + {GPIOE, NULL, NULL, 15, 0, ADCx}, /* D93/PE15 */ + {GPIOG, NULL, NULL, 3, 0, ADCx}, /* D94/PG3 */ + {GPIOE, NULL, NULL, 0, 0, ADCx}, /* D95/PE0 */ + {GPIOD, NULL, NULL, 8, 0, ADCx}, /* D96/PD8 */ + {GPIOG, NULL, NULL, 4, 0, ADCx}, /* D97/PG4 */ + {GPIOD, NULL, NULL, 9, 0, ADCx}, /* D98/PD9 */ + {GPIOG, NULL, NULL, 5, 0, ADCx}, /* D99/PG5 */ + {GPIOD, NULL, NULL, 10, 0, ADCx}, /* D100/PD10 */ + + /* JTAG header */ + + {GPIOA, NULL, NULL, 13, 0, ADCx}, /* D101/PA13 */ + {GPIOA, NULL, NULL, 14, 0, ADCx}, /* D102/PA14 */ + {GPIOA, NULL, NULL, 15, 0, ADCx}, /* D103/PA15 */ + {GPIOB, NULL, NULL, 3, 0, ADCx}, /* D104/PB3 */ + {GPIOB, NULL, NULL, 4, 0, ADCx} /* D105/PB4 */ +}; + +extern const uint8 boardPWMPins[BOARD_NR_PWM_PINS] __FLASH__ = { + 13, 14, 15, 16, 23, 24, 25, 26, 38, 39, 46, 47, 48, 49, 50, 51, 54, 55 +}; + +extern const uint8 boardADCPins[BOARD_NR_ADC_PINS] __FLASH__ = { + 7, 8, 9, 10, 11, 12, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, + 54, 55 +}; + +/* FIXME [0.0.10] see comment by BOARD_NR_USED_PINS in maple_native.h */ +extern const uint8 boardUsedPins[BOARD_NR_USED_PINS] __FLASH__ = { + BOARD_LED_PIN, BOARD_BUTTON_PIN, BOARD_JTMS_SWDIO_PIN, + BOARD_JTCK_SWCLK_PIN, BOARD_JTDI_PIN, BOARD_JTDO_PIN, BOARD_NJTRST_PIN +}; + +void initSRAMChip(void) { + fsmc_nor_psram_reg_map *regs = FSMC_NOR_PSRAM1_BASE; + + fsmc_sram_init_gpios(); + rcc_clk_enable(RCC_FSMC); + + regs->BCR = FSMC_BCR_WREN | FSMC_BCR_MWID_16BITS | FSMC_BCR_MBKEN; + fsmc_nor_psram_set_addset(regs, 0); + fsmc_nor_psram_set_datast(regs, 3); +} + +#endif diff --git a/wirish/boards/maple_native.h b/wirish/boards/maple_native.h new file mode 100644 index 0000000..b573d72 --- /dev/null +++ b/wirish/boards/maple_native.h @@ -0,0 +1,85 @@ +/****************************************************************************** + * The MIT License + * + * Copyright (c) 2011 LeafLabs, LLC. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + *****************************************************************************/ + +/** + * @file maple_native.h + * @author Marti Bolivar <mbolivar@leaflabs.com> + * @brief Private include file for Maple Native in boards.h + * + * See maple.h for more information on these definitions. + */ + +#ifndef _BOARD_MAPLE_NATIVE_H_ +#define _BOARD_MAPLE_NATIVE_H_ + +#define CYCLES_PER_MICROSECOND 72 +#define SYSTICK_RELOAD_VAL 71999 + +#define BOARD_LED_PIN 22 +#define BOARD_BUTTON_PIN 6 + +#define BOARD_NR_USARTS 5 +#define BOARD_USART1_TX_PIN 26 +#define BOARD_USART1_RX_PIN 27 +#define BOARD_USART2_TX_PIN 52 +#define BOARD_USART2_RX_PIN 53 +#define BOARD_USART3_TX_PIN 0 +#define BOARD_USART3_RX_PIN 1 +#define BOARD_UART4_TX_PIN 19 +#define BOARD_UART4_RX_PIN 20 +#define BOARD_UART5_TX_PIN 21 +#define BOARD_UART5_RX_PIN 29 + +#define BOARD_NR_SPI 3 +#define BOARD_SPI1_NSS_PIN 52 +#define BOARD_SPI1_MOSI_PIN 55 +#define BOARD_SPI1_MISO_PIN 54 +#define BOARD_SPI1_SCK_PIN 53 +#define BOARD_SPI2_NSS_PIN 2 +#define BOARD_SPI2_MOSI_PIN 5 +#define BOARD_SPI2_MISO_PIN 4 +#define BOARD_SPI2_SCK_PIN 3 +#define BOARD_SPI3_NSS_PIN 103 +#define BOARD_SPI3_MOSI_PIN 37 +#define BOARD_SPI3_MISO_PIN 105 +#define BOARD_SPI3_SCK_PIN 104 + +#define BOARD_NR_GPIO_PINS 106 +#define BOARD_NR_PWM_PINS 18 +#define BOARD_NR_ADC_PINS 21 +/* FIXME [0.0.10] this isn't true at all; almost all of the triple + * header pins are used by the FSMC by default. Fix this (and the + * corresponding boardUsedPins definition in maple_native.cpp) by QA + * time. */ +#define BOARD_NR_USED_PINS 7 + +#define BOARD_JTMS_SWDIO_PIN 101 +#define BOARD_JTCK_SWCLK_PIN 102 +#define BOARD_JTDI_PIN 103 +#define BOARD_JTDO_PIN 104 +#define BOARD_NJTRST_PIN 105 + +#endif diff --git a/wirish/comm/HardwareSPI.cpp b/wirish/comm/HardwareSPI.cpp index 20090f5..54b7ab3 100644 --- a/wirish/comm/HardwareSPI.cpp +++ b/wirish/comm/HardwareSPI.cpp @@ -23,118 +23,268 @@ *****************************************************************************/ /** - * @brief HardwareSPI "wiring-like" api for SPI + * @author Marti Bolivar <mbolivar@leaflabs.com> + * @brief Wirish SPI implementation. */ -/* NOTES: - * - * Speeds: - * ----------------------------------- - * Interface num: SPI1 SPI2 - * Bus APB2 APB1 - * ----------------------------------- - * Prescaler Frequencies - * ----------------------------------- - * 2: N/A 18 000 000 - * 4: 18 000 000 9 000 000 - * 8: 9 000 000 4 500 000 - * 16: 4 500 000 2 250 000 - * 32: 2 250 000 1 125 000 - * 64: 1 125 000 562 500 - * 128: 562 500 281 250 - * 256: 281 250 140 625 - * - * TODO: Do the complementary PWM outputs mess up SPI2? - * */ +#include "HardwareSPI.h" + +#include "timer.h" +#include "util.h" #include "wirish.h" -#include "spi.h" -#include "HardwareSPI.h" +#include "boards.h" -static const uint32 prescaleFactors[MAX_SPI_FREQS] = { - SPI_PRESCALE_2, // SPI_18MHZ - SPI_PRESCALE_4, // SPI_9MHZ - SPI_PRESCALE_8, // SPI_4_5MHZ - SPI_PRESCALE_16, // SPI_2_25MHZ - SPI_PRESCALE_32, // SPI_1_125MHZ - SPI_PRESCALE_64, // SPI_562_500KHZ - SPI_PRESCALE_128, // SPI_281_250KHZ - SPI_PRESCALE_256, // SPI_140_625KHZ -}; +static void enable_device(spi_dev *dev, + bool as_master, + SPIFrequency frequency, + spi_cfg_flag endianness, + spi_mode mode); -/** - * @brief Initialize a SPI peripheral - * @param freq frequency to run at, must one of the following values: - * - SPI_18MHZ - * - SPI_9MHZ - * - SPI_4_5MHZ - * - SPI_2_25MHZ - * - SPI_1_125MHZ - * - SPI_562_500KHZ - * - SPI_281_250KHZ - * - SPI_140_625KHZ - * @param endianness endianness of the data frame, must be either LSBFIRST - * or MSBFIRST - * @param mode SPI standard CPOL and CPHA levels +/* + * Constructor, public methods */ -void HardwareSPI::begin(SPIFrequency freq, uint32 endianness, uint32 mode) { - uint32 spi_num = this->spi_num; - uint32 prescale; - - if ((freq >= MAX_SPI_FREQS) || - !((endianness == LSBFIRST) || - (endianness == MSBFIRST)) || - (mode >= 4)) { + +HardwareSPI::HardwareSPI(uint32 spi_num) { + switch (spi_num) { + case 1: + this->spi_d = SPI1; + break; + case 2: + this->spi_d = SPI2; + break; +#ifdef STM32_HIGH_DENSITY + case 3: + this->spi_d = SPI3; + break; +#endif + default: + ASSERT(0); + } +} + +void HardwareSPI::begin(SPIFrequency frequency, uint32 bitOrder, uint32 mode) { + if (mode >= 4) { + ASSERT(0); return; } + spi_cfg_flag end = bitOrder == MSBFIRST ? SPI_FRAME_MSB : SPI_FRAME_LSB; + spi_mode m = (spi_mode)mode; + enable_device(this->spi_d, true, frequency, end, m); +} - if (spi_num == 1) { - /* SPI1 is too fast for 140625 */ - if (freq == SPI_140_625KHZ) { - return; - } +void HardwareSPI::begin(void) { + this->begin(SPI_1_125MHZ, MSBFIRST, 0); +} - /* Turn off PWM on shared pins */ - timer_set_mode(TIMER3, 2, TIMER_DISABLED); - timer_set_mode(TIMER3, 1, TIMER_DISABLED); +void HardwareSPI::beginSlave(uint32 bitOrder, uint32 mode) { + if (mode >= 4) { + ASSERT(0); + return; } + spi_cfg_flag end = bitOrder == MSBFIRST ? SPI_FRAME_MSB : SPI_FRAME_LSB; + spi_mode m = (spi_mode)mode; + enable_device(this->spi_d, false, (SPIFrequency)0, end, m); +} - endianness = (endianness == LSBFIRST) ? SPI_LSBFIRST : SPI_MSBFIRST; - prescale = (spi_num == 1) ? - prescaleFactors[freq + 1] : - prescaleFactors[freq]; +void HardwareSPI::beginSlave(void) { + this->beginSlave(MSBFIRST, 0); +} + +void HardwareSPI::end(void) { + if (!spi_is_enabled(this->spi_d)) { + return; + } - spi_init(spi_num, prescale, endianness, mode); + // Follows RM0008's sequence for disabling a SPI in master/slave + // full duplex mode. + while (spi_is_rx_nonempty(this->spi_d)) { + // FIXME [0.1.0] remove this once you have an interrupt based driver + volatile uint16 rx __attribute__((unused)) = spi_rx_reg(this->spi_d); + } + while (!spi_is_tx_empty(this->spi_d)) + ; + while (spi_is_busy(this->spi_d)) + ; + spi_peripheral_disable(this->spi_d); } -/** - * @brief Initialize a SPI peripheral with a default speed of 1.125 - * MHZ, MSBFIRST, mode 0 - */ -void HardwareSPI::begin(void) { - begin(SPI_1_125MHZ, MSBFIRST, 0); +uint8 HardwareSPI::read(void) { + uint8 buf[1]; + this->read(buf, 1); + return buf[0]; } -/** - * @brief send a byte out the spi peripheral - * @param data byte to send +void HardwareSPI::read(uint8 *buf, uint32 len) { + uint32 rxed = 0; + while (rxed < len) { + while (!spi_is_rx_nonempty(this->spi_d)) + ; + buf[rxed++] = (uint8)spi_rx_reg(this->spi_d); + } +} + +void HardwareSPI::write(uint8 byte) { + this->write(&byte, 1); +} + +void HardwareSPI::write(const uint8 *data, uint32 length) { + uint32 txed = 0; + while (txed < length) { + txed += spi_tx(this->spi_d, data + txed, length - txed); + } +} + +uint8 HardwareSPI::transfer(uint8 byte) { + this->write(byte); + return this->read(); +} + +/* + * Deprecated functions */ + uint8 HardwareSPI::send(uint8 data) { - return spi_tx_byte(this->spi_num, data); + uint8 buf[] = {data}; + return this->send(buf, 1); } uint8 HardwareSPI::send(uint8 *buf, uint32 len) { - return spi_tx(this->spi_num, buf, len); + if (len == 0) { + return 0; + } + uint32 txed = 0; + uint8 ret = 0; // shut up, GCC + while (txed < len) { + this->write(buf[txed++]); + ret = this->read(); + } + return ret; } -/** - * @brief read a byte from the spi peripheral - * @return byte in the buffer - */ uint8 HardwareSPI::recv(void) { - return spi_rx(this->spi_num); + return this->read(); } -HardwareSPI::HardwareSPI(uint32 spi_num) { - this->spi_num = spi_num; +/* + * Auxiliary functions + */ + +static void configure_gpios(spi_dev *dev, bool as_master); +static spi_baud_rate determine_baud_rate(spi_dev *dev, SPIFrequency freq); + +/* Enables the device in master or slave full duplex mode. If you + * change this code, you must ensure that appropriate changes are made + * to HardwareSPI::end(). */ +static void enable_device(spi_dev *dev, + bool as_master, + SPIFrequency freq, + spi_cfg_flag endianness, + spi_mode mode) { + spi_baud_rate baud = determine_baud_rate(dev, freq); + uint32 cfg_flags = (endianness | SPI_DFF_8_BIT | SPI_SW_SLAVE | + (as_master ? SPI_SOFT_SS : 0)); + + spi_init(dev); + configure_gpios(dev, as_master); + if (as_master) { + spi_master_enable(dev, baud, mode, cfg_flags); + } else { + spi_slave_enable(dev, mode, cfg_flags); + } +} + +static void disable_pwm(const stm32_pin_info *i) { + if (i->timer_device) { + timer_set_mode(i->timer_device, i->timer_channel, TIMER_DISABLED); + } +} + +typedef struct spi_pins { + uint8 nss; + uint8 sck; + uint8 miso; + uint8 mosi; +} spi_pins; + +static void configure_gpios(spi_dev *dev, bool as_master) { + const spi_pins spi_pin_config[] = { + {BOARD_SPI1_NSS_PIN, + BOARD_SPI1_SCK_PIN, + BOARD_SPI1_MISO_PIN, + BOARD_SPI1_MOSI_PIN}, + {BOARD_SPI2_NSS_PIN, + BOARD_SPI2_SCK_PIN, + BOARD_SPI2_MISO_PIN, + BOARD_SPI2_MOSI_PIN}, +#ifdef STM32_HIGH_DENSITY + {BOARD_SPI3_NSS_PIN, + BOARD_SPI3_SCK_PIN, + BOARD_SPI3_MISO_PIN, + BOARD_SPI3_MOSI_PIN}, +#endif + }; + + const spi_pins *pins; + + switch (dev->clk_id) { + case RCC_SPI1: + pins = &spi_pin_config[0]; + break; + case RCC_SPI2: + pins = &spi_pin_config[1]; + break; +#ifdef STM32_HIGH_DENSITY + case RCC_SPI3: + pins = &spi_pin_config[2]; + break; +#endif + default: + ASSERT(0); + return; + } + + const stm32_pin_info *nssi = &PIN_MAP[pins->nss]; + const stm32_pin_info *scki = &PIN_MAP[pins->sck]; + const stm32_pin_info *misoi = &PIN_MAP[pins->miso]; + const stm32_pin_info *mosii = &PIN_MAP[pins->mosi]; + + disable_pwm(nssi); + disable_pwm(scki); + disable_pwm(misoi); + disable_pwm(mosii); + + spi_gpio_cfg(as_master, + nssi->gpio_device, + nssi->gpio_bit, + scki->gpio_device, + scki->gpio_bit, + misoi->gpio_bit, + mosii->gpio_bit); +} + +static const spi_baud_rate baud_rates[MAX_SPI_FREQS] __FLASH__ = { + SPI_BAUD_PCLK_DIV_2, + SPI_BAUD_PCLK_DIV_4, + SPI_BAUD_PCLK_DIV_8, + SPI_BAUD_PCLK_DIV_16, + SPI_BAUD_PCLK_DIV_32, + SPI_BAUD_PCLK_DIV_64, + SPI_BAUD_PCLK_DIV_128, + SPI_BAUD_PCLK_DIV_256, +}; + +/* + * Note: This assumes you're on a LeafLabs-style board + * (CYCLES_PER_MICROSECOND == 72, APB2 at 72MHz, APB1 at 36MHz). + */ +static spi_baud_rate determine_baud_rate(spi_dev *dev, SPIFrequency freq) { + if (rcc_dev_clk(dev->clk_id) == RCC_APB2 && freq == SPI_140_625KHZ) { + /* APB2 peripherals are too fast for 140.625 KHz */ + ASSERT(0); + return (spi_baud_rate)~0; + } + return (rcc_dev_clk(dev->clk_id) == RCC_APB2 ? + baud_rates[freq + 1] : + baud_rates[freq]); } diff --git a/wirish/comm/HardwareSPI.h b/wirish/comm/HardwareSPI.h index 7241d0b..3a6def5 100644 --- a/wirish/comm/HardwareSPI.h +++ b/wirish/comm/HardwareSPI.h @@ -3,60 +3,188 @@ * * Copyright (c) 2010 Perry Hung. * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. *****************************************************************************/ /** - * @brief HardwareSPI definitions + * @file HardwareSPI.h + * @brief High-level SPI interface + * + * This is a "bare essentials" polling driver for now. */ +/* TODO [0.1.0] Remove deprecated methods. */ + +#include "libmaple_types.h" +#include "spi.h" + +#include "boards.h" + #ifndef _HARDWARESPI_H_ #define _HARDWARESPI_H_ /** - * Defines the possible SPI communication speeds. + * @brief Defines the possible SPI communication speeds. */ typedef enum SPIFrequency { SPI_18MHZ = 0, /**< 18 MHz */ SPI_9MHZ = 1, /**< 9 MHz */ SPI_4_5MHZ = 2, /**< 4.5 MHz */ - SPI_2_25MHZ = 3, /**< 2.25 MHZ */ + SPI_2_25MHZ = 3, /**< 2.25 MHz */ SPI_1_125MHZ = 4, /**< 1.125 MHz */ SPI_562_500KHZ = 5, /**< 562.500 KHz */ SPI_281_250KHZ = 6, /**< 281.250 KHz */ SPI_140_625KHZ = 7, /**< 140.625 KHz */ - MAX_SPI_FREQS = 8, /**< The number of SPI frequencies. */ } SPIFrequency; -/* Documented by hand in docs/source/lang/api/hardwarespi.rst; if you - make any changes, make sure to update this document. */ +#define MAX_SPI_FREQS 8 + +#if CYCLES_PER_MICROSECOND != 72 +/* TODO [0.2.0?] something smarter than this */ +#warn "Unexpected clock speed; SPI frequency calculation will be incorrect" +#endif + +/** + * @brief Wirish SPI interface. + * + * This implementation uses software slave management, so the caller + * is responsible for controlling the slave select line. + */ class HardwareSPI { - private: - uint32 spi_num; +public: + /** + * @param spiPortNumber Number of the SPI port to manage. + */ + HardwareSPI(uint32 spiPortNumber); + + /** + * @brief Turn on a SPI port and set its GPIO pin modes for use as master. + * + * SPI port is enabled in full duplex mode, with software slave management. + * + * @param frequency Communication frequency + * @param bitOrder Either LSBFIRST (little-endian) or MSBFIRST (big-endian) + * @param mode SPI mode to use, one of SPI_MODE_0, SPI_MODE_1, + * SPI_MODE_2, and SPI_MODE_3. + */ + void begin(SPIFrequency frequency, uint32 bitOrder, uint32 mode); - public: - HardwareSPI(uint32 spi_num); + /** + * @brief Equivalent to begin(SPI_1_125MHZ, MSBFIRST, 0). + */ void begin(void); - void begin(SPIFrequency freq, uint32 endianness, uint32 mode); + + /** + * @brief Turn on a SPI port and set its GPIO pin modes for use as a slave. + * + * SPI port is enabled in full duplex mode, with software slave management. + * + * @param bitOrder Either LSBFIRST (little-endian) or MSBFIRST(big-endian) + * @param mode SPI mode to use + */ + void beginSlave(uint32 bitOrder, uint32 mode); + + /** + * @brief Equivalent to beginSlave(MSBFIRST, 0). + */ + void beginSlave(void); + + /** + * @brief Disables the SPI port, but leaves its GPIO pin modes unchanged. + */ + void end(void); + + /** + * @brief Return the next unread byte. + * + * If there is no unread byte waiting, this function will block + * until one is received. + */ + uint8 read(void); + + /** + * @brief Read length bytes, storing them into buffer. + * @param buffer Buffer to store received bytes into. + * @param length Number of bytes to store in buffer. This + * function will block until the desired number of + * bytes have been read. + */ + void read(uint8 *buffer, uint32 length); + + /** + * @brief Transmit a byte. + * @param data Byte to transmit. + */ + void write(uint8 data); + + /** + * @brief Transmit multiple bytes. + * @param buffer Bytes to transmit. + * @param length Number of bytes in buffer to transmit. + */ + void write(const uint8 *buffer, uint32 length); + + /** + * @brief Transmit a byte, then return the next unread byte. + * + * This function transmits before receiving. + * + * @param data Byte to transmit. + * @return Next unread byte. + */ + uint8 transfer(uint8 data); + + /* -- The following methods are deprecated --------------------------- */ + + /** + * @brief Deprecated. + * + * Use HardwareSPI::transfer() instead. + * + * @see HardwareSPI::transfer() + */ uint8 send(uint8 data); + + /** + * @brief Deprecated. + * + * Use HardwareSPI::write() in combination with + * HardwareSPI::read() (or HardwareSPI::transfer()) instead. + * + * @see HardwareSPI::write() + * @see HardwareSPI::read() + * @see HardwareSPI::transfer() + */ uint8 send(uint8 *data, uint32 length); + + /** + * @brief Deprecated. + * + * Use HardwareSPI::read() instead. + * + * @see HardwareSPI::read() + */ uint8 recv(void); +private: + spi_dev *spi_d; }; #endif diff --git a/wirish/comm/HardwareSerial.cpp b/wirish/comm/HardwareSerial.cpp index d6c7e82..5c9bff6 100644 --- a/wirish/comm/HardwareSerial.cpp +++ b/wirish/comm/HardwareSerial.cpp @@ -24,69 +24,86 @@ /** * @file HardwareSerial.cpp - * - * @brief Wiring-like serial api + * @brief Wirish serial port implementation. */ -#include "wirish.h" -#include "HardwareSerial.h" -#include "usart.h" #include "gpio.h" -#include "timers.h" +#include "timer.h" + +#include "HardwareSerial.h" +#include "boards.h" -HardwareSerial Serial1(USART1, 4500000UL, GPIOA_BASE, 9,10, TIMER1, 2); -HardwareSerial Serial2(USART2, 2250000UL, GPIOA_BASE, 2, 3, TIMER2, 3); -HardwareSerial Serial3(USART3, 2250000UL, GPIOB_BASE, 10,11, TIMER_INVALID, 0); -// TODO: High density device ports +#define TX1 BOARD_USART1_TX_PIN +#define RX1 BOARD_USART1_RX_PIN +#define TX2 BOARD_USART2_TX_PIN +#define RX2 BOARD_USART2_RX_PIN +#define TX3 BOARD_USART3_TX_PIN +#define RX3 BOARD_USART3_RX_PIN +#if defined STM32_HIGH_DENSITY && !defined(BOARD_maple_RET6) +#define TX4 BOARD_UART4_TX_PIN +#define RX4 BOARD_UART4_RX_PIN +#define TX5 BOARD_UART5_TX_PIN +#define RX5 BOARD_UART5_RX_PIN +#endif -HardwareSerial::HardwareSerial(uint8 usart_num, - uint32 max_baud, - GPIO_Port *gpio_port, +// TODO Put these magic numbers into boards.h #defines +HardwareSerial Serial1(USART1, TX1, RX1, 72000000UL); +HardwareSerial Serial2(USART2, TX2, RX2, 36000000UL); +HardwareSerial Serial3(USART3, TX3, RX3, 36000000UL); +#if defined(STM32_HIGH_DENSITY) && !defined(BOARD_maple_RET6) +HardwareSerial Serial4(UART4, TX4, RX4, 36000000UL); +HardwareSerial Serial5(UART5, TX5, RX5, 36000000UL); +#endif + +HardwareSerial::HardwareSerial(usart_dev *usart_device, uint8 tx_pin, uint8 rx_pin, - timer_dev_num timer_num, - uint8 compare_num) { - this->usart_num = usart_num; - this->max_baud = max_baud; - this->gpio_port = gpio_port; + uint32 clock_speed) { + this->usart_device = usart_device; + this->clock_speed = clock_speed; this->tx_pin = tx_pin; this->rx_pin = rx_pin; - this->timer_num = timer_num; - this->compare_num = compare_num; } uint8 HardwareSerial::read(void) { - return usart_getc(usart_num); + return usart_getc(usart_device); } uint32 HardwareSerial::available(void) { - return usart_data_available(usart_num); + return usart_data_available(usart_device); } void HardwareSerial::write(unsigned char ch) { - usart_putc(usart_num, ch); + usart_putc(usart_device, ch); } void HardwareSerial::begin(uint32 baud) { - if (baud > max_baud) { + ASSERT(baud <= usart_device->max_baud); + + if (baud > usart_device->max_baud) { return; } - gpio_set_mode(gpio_port, tx_pin, GPIO_MODE_AF_OUTPUT_PP); - gpio_set_mode(gpio_port, rx_pin, GPIO_MODE_INPUT_FLOATING); + const stm32_pin_info *txi = &PIN_MAP[tx_pin]; + const stm32_pin_info *rxi = &PIN_MAP[rx_pin]; + + gpio_set_mode(txi->gpio_device, txi->gpio_bit, GPIO_AF_OUTPUT_PP); + gpio_set_mode(rxi->gpio_device, rxi->gpio_bit, GPIO_INPUT_FLOATING); - if (timer_num != TIMER_INVALID) { - /* turn off any pwm if there's a conflict on this usart */ - timer_set_mode(timer_num, compare_num, TIMER_DISABLED); + if (txi->timer_device != NULL) { + /* Turn off any PWM if there's a conflict on this GPIO bit. */ + timer_set_mode(txi->timer_device, txi->timer_channel, TIMER_DISABLED); } - usart_init(usart_num, baud); + usart_init(usart_device); + usart_set_baud_rate(usart_device, clock_speed, baud); + usart_enable(usart_device); } void HardwareSerial::end(void) { - usart_disable(usart_num); + usart_disable(usart_device); } void HardwareSerial::flush(void) { - usart_reset_rx(usart_num); + usart_reset_rx(usart_device); } diff --git a/wirish/comm/HardwareSerial.h b/wirish/comm/HardwareSerial.h index aad8aa7..934db23 100644 --- a/wirish/comm/HardwareSerial.h +++ b/wirish/comm/HardwareSerial.h @@ -24,41 +24,34 @@ /** * @file HardwareSerial.h - * - * @brief Wirish interface to hardware serial communications. + * @brief Wirish serial port interface. */ #ifndef _HARDWARESERIAL_H_ #define _HARDWARESERIAL_H_ -#include "timers.h" +#include "libmaple_types.h" +#include "usart.h" #include "Print.h" -/* NB: this class documented "by hand" (i.e., not using Doxygen) in: - - libmaple/docs/source/lang/serial.rst - - If you alter the public HardwareSerial interface, you must update - the documentation accordingly. */ +/* + * IMPORTANT: + * + * This class documented "by hand" (i.e., not using Doxygen) in: + * + * libmaple/docs/source/lang/api/serial.rst + * + * If you alter the public HardwareSerial interface, you MUST update + * the documentation accordingly. + */ class HardwareSerial : public Print { - private: - uint8 usart_num; - uint32 max_baud; - GPIO_Port *gpio_port; - uint8 tx_pin; - uint8 rx_pin; - timer_dev_num timer_num; - uint8 compare_num; - public: - HardwareSerial(uint8 usart_num, - uint32 max_baud, - GPIO_Port *gpio_port, +public: + HardwareSerial(usart_dev *usart_device, uint8 tx_pin, uint8 rx_pin, - timer_dev_num timer_num, - uint8 compare_num); + uint32 clock_speed); void begin(uint32 baud); void end(void); uint32 available(void); @@ -66,10 +59,19 @@ class HardwareSerial : public Print { void flush(void); virtual void write(unsigned char); using Print::write; +private: + usart_dev *usart_device; + uint8 tx_pin; + uint8 rx_pin; + uint32 clock_speed; }; + extern HardwareSerial Serial1; extern HardwareSerial Serial2; extern HardwareSerial Serial3; -// TODO: high density device ports +#if defined(STM32_HIGH_DENSITY) && !defined(BOARD_maple_RET6) +extern HardwareSerial Serial4; +extern HardwareSerial Serial5; #endif +#endif diff --git a/wirish/ext_interrupts.c b/wirish/ext_interrupts.cpp index dd7c1a8..557fffd 100644 --- a/wirish/ext_interrupts.c +++ b/wirish/ext_interrupts.cpp @@ -28,52 +28,55 @@ * @brief Wiring-like interface for external interrupts */ -#include "wirish.h" +#include "boards.h" +#include "gpio.h" #include "exti.h" #include "ext_interrupts.h" -/* Attach ISR handler on pin, triggering on the given mode. */ -void attachInterrupt(uint8 pin, voidFuncPtr handler, ExtIntTriggerMode mode) { - uint8 outMode; - - /* Parameter checking */ - if (pin >= NR_GPIO_PINS) { - return; - } +static inline exti_trigger_mode exti_out_mode(ExtIntTriggerMode mode); - if (!handler) { +/** + * @brief Attach an interrupt handler to a pin, triggering on the given mode. + * @param pin Pin to attach an interrupt handler onto. + * @param handler Function to call when the external interrupt is triggered. + * @param mode Trigger mode for the given interrupt. + * @see ExtIntTriggerMode + */ +void attachInterrupt(uint8 pin, voidFuncPtr handler, ExtIntTriggerMode mode) { + if (pin >= BOARD_NR_GPIO_PINS || !handler) { return; } - switch (mode) { - case RISING: - outMode = EXTI_RISING; - break; - case FALLING: - outMode = EXTI_FALLING; - break; - case CHANGE: - outMode = EXTI_RISING_FALLING; - break; - default: - ASSERT(0); - return; - } + exti_trigger_mode outMode = exti_out_mode(mode); - exti_attach_interrupt(PIN_MAP[pin].exti_port, - PIN_MAP[pin].pin, + exti_attach_interrupt((afio_exti_num)(PIN_MAP[pin].gpio_bit), + gpio_exti_port(PIN_MAP[pin].gpio_device), handler, - mode); - - return; + outMode); } -/* Disable any interrupts */ +/** + * @brief Disable any external interrupt attached to a pin. + * @param pin Pin number to detach any interrupt from. + */ void detachInterrupt(uint8 pin) { - if (!(pin < NR_GPIO_PINS)) { + if (pin >= BOARD_NR_GPIO_PINS) { return; } - exti_detach_interrupt(PIN_MAP[pin].pin); + exti_detach_interrupt((afio_exti_num)(PIN_MAP[pin].gpio_bit)); } +static inline exti_trigger_mode exti_out_mode(ExtIntTriggerMode mode) { + switch (mode) { + case RISING: + return EXTI_RISING; + case FALLING: + return EXTI_FALLING; + case CHANGE: + return EXTI_RISING_FALLING; + } + // Can't happen + ASSERT(0); + return (exti_trigger_mode)0; +} diff --git a/wirish/ext_interrupts.h b/wirish/ext_interrupts.h index 304e267..7d20801 100644 --- a/wirish/ext_interrupts.h +++ b/wirish/ext_interrupts.h @@ -28,7 +28,7 @@ /** * @file ext_interrupts.h * - * @brief External interrupt wiring prototypes and types + * @brief Wiring-like external interrupt prototypes and types. */ #ifndef _EXT_INTERRUPTS_H_ @@ -38,7 +38,7 @@ * The kind of transition on an external pin which should trigger an * interrupt. */ -typedef enum ExtIntTriggerMode_ { +typedef enum ExtIntTriggerMode { RISING, /**< To trigger an interrupt when the pin transitions LOW to HIGH */ FALLING, /**< To trigger an interrupt when the pin transitions @@ -48,10 +48,6 @@ typedef enum ExtIntTriggerMode_ { changes). */ } ExtIntTriggerMode; -#ifdef __cplusplus -extern "C"{ -#endif - /** * @brief Registers an interrupt handler on a pin. * @@ -86,7 +82,7 @@ void detachInterrupt(uint8 pin); * * @see noInterrupts() */ -static ALWAYS_INLINE void interrupts() { +static inline void interrupts() { nvic_globalirq_enable(); } @@ -100,14 +96,9 @@ static ALWAYS_INLINE void interrupts() { * * @see interrupts() */ -static ALWAYS_INLINE void noInterrupts() { +static inline void noInterrupts() { nvic_globalirq_disable(); } -#ifdef __cplusplus -} -#endif - - #endif diff --git a/wirish/io.h b/wirish/io.h index 2d22dcd..ace40f2 100644 --- a/wirish/io.h +++ b/wirish/io.h @@ -28,30 +28,16 @@ * @brief Arduino-compatible digital pin I/O interface. */ -#ifndef _IO_H -#define _IO_H +#ifndef _IO_H_ +#define _IO_H_ #include "gpio.h" #include "adc.h" -#include "time.h" -#ifdef __cplusplus -extern "C"{ -#endif +#include "wirish_time.h" /** * Specifies a GPIO pin behavior. - * - * Each of the GPIO pins on a Maple board may be configured using - * pinMode() to behave in a number of ways: as a digital output pin, - * or as an analog input pin, etc., depending on the particular pin. - * - * This enum specifies the complete set of possible configurations; - * not every pin can have all of these modes. For example, on the - * Maple, pin 15 may be configured as INPUT_ANALOG, but not as PWM. - * See your device's silkscreen and and the GPIO documentation for - * more information. - * * @see pinMode() */ typedef enum WiringPinMode { @@ -68,11 +54,11 @@ typedef enum WiringPinMode { 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 + 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/ + mode, no current is ever actually sourced from the pin. */ INPUT, /**< Basic digital input. The pin voltage is sampled; when @@ -118,8 +104,7 @@ typedef enum WiringPinMode { /** * Configure behavior of a GPIO pin. * - * @param pin Pin to configure. One of: 0-38 (pin numbers as labeled - * on silkscreen), or D0-D38 (symbols for same) + * @param pin Number of pin to configure. * @param mode Mode corresponding to desired pin behavior. * @see WiringPinMode */ @@ -127,10 +112,9 @@ void pinMode(uint8 pin, WiringPinMode mode); /** * Writes a (digital) value to a pin. The pin must have its - * mode set to <code>OUTPUT</code> or <code>OUTPUT_OPEN_DRAIN</code>. + * mode set to OUTPUT or OUTPUT_OPEN_DRAIN. * - * @param pin Pin to write to. One of: 0-38 (pin numbers as labeled - * on silkscreen), or D0-D38 (symbols for same) + * @param pin Pin to write to. * @param value Either LOW (write a 0) or HIGH (write a 1). * @see pinMode() */ @@ -140,8 +124,7 @@ void digitalWrite(uint8 pin, uint8 value); * Read a digital value from a pin. The pin must have its mode set to * one of INPUT, INPUT_PULLUP, and INPUT_PULLDOWN. * - * @param pin Pin to read from. One of: 0-38 (pin numbers as labeled - * on silkscreen), or D0-D38 (symbols for same) + * @param pin Pin to read from. * @return LOW or HIGH. * @see pinMode() */ @@ -150,18 +133,14 @@ uint32 digitalRead(uint8 pin); /** * Read an analog value from pin. This function blocks during ADC * conversion, and has 12 bits of resolution. The pin must have its - * mode set to INPUT_ANALOG. Ignoring function call overhead, - * conversion time is 55.5 cycles. + * mode set to INPUT_ANALOG. * - * @param pin Pin to read from. One of: 0, 1, 2, 3, 10, 11, 12, 13, - * 15, 16, 17, 18, 19, 20, 27, 28. - - * @return ADC-converted voltage, in the range 0--4095, inclusive - * (i.e. a 12-bit ADC conversion). - + * @param pin Pin to read from. + * @return Converted voltage, in the range 0--4095, (i.e. a 12-bit ADC + * conversion). * @see pinMode() */ -uint32 analogRead(uint8 pin); +uint16 analogRead(uint8 pin); /** * Toggles the digital value at the given pin. @@ -211,17 +190,31 @@ uint8 isButtonPressed(); * pinMode(BOARD_BUTTON_PIN, INPUT). * * @param timeout_millis Number of milliseconds to wait until the - * button is pressed. If timeout_millis is 0, wait forever. + * button is pressed. If timeout_millis is left out (or 0), wait + * forever. * * @return true, if the button was pressed; false, if the timeout was * reached. * * @see pinMode() */ -uint8 waitForButtonPress(uint32 timeout_millis); +uint8 waitForButtonPress(uint32 timeout_millis=0); + +/** + * Shift out a byte of data, one bit at a time. + * + * This function starts at either the most significant or least + * significant bit in a byte value, and shifts out each byte in order + * onto a data pin. After each bit is written to the data pin, a + * separate clock pin is pulsed to indicate that the new bit is + * available. + * + * @param dataPin Pin to shift data out on + * @param clockPin Pin to pulse after each bit is shifted out + * @param bitOrder Either MSBFIRST (big-endian) or LSBFIRST (little-endian). + * @param value Value to shift out + */ +void shiftOut(uint8 dataPin, uint8 clockPin, uint8 bitOrder, uint8 value); -#ifdef __cplusplus -} // extern "C" -#endif #endif diff --git a/wirish/main.cxx b/wirish/main.cxx index f0158f8..dd5e296 100644 --- a/wirish/main.cxx +++ b/wirish/main.cxx @@ -1,4 +1,4 @@ -/* *****************************************************************************
+/******************************************************************************
* The MIT License
*
* Copyright (c) 2010 LeafLabs LLC.
@@ -20,16 +20,15 @@ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
- * ****************************************************************************/
+ *****************************************************************************/
// Force init to be called *first*, i.e. before static object allocation.
-// Otherwise, statically allocated object that need libmaple may fail.
+// Otherwise, statically allocated objects that need libmaple may fail.
__attribute__(( constructor )) void premain() {
init();
}
-int main(void)
-{
+int main(void) {
setup();
while (1) {
diff --git a/wirish/pwm.c b/wirish/pwm.cpp index 072e4cd..bf69bfb 100644 --- a/wirish/pwm.c +++ b/wirish/pwm.cpp @@ -23,26 +23,20 @@ *****************************************************************************/ /** - * @brief Arduino-compatible PWM implementation. + * @brief Arduino-style PWM implementation. */ -#include "wirish.h" -#include "timers.h" -#include "io.h" +#include "libmaple_types.h" +#include "timer.h" + +#include "boards.h" #include "pwm.h" void pwmWrite(uint8 pin, uint16 duty_cycle) { - TimerCCR ccr; - - if (pin >= NR_GPIO_PINS) { - return; - } - - ccr = PIN_MAP[pin].timer_ccr; - - if (ccr == 0) { + timer_dev *dev = PIN_MAP[pin].timer_device; + if (pin >= BOARD_NR_GPIO_PINS || dev == NULL || dev->type == TIMER_BASIC) { return; } - timer_pwm_write_ccr(ccr, duty_cycle); + timer_set_compare(dev, PIN_MAP[pin].timer_channel, duty_cycle); } diff --git a/wirish/pwm.h b/wirish/pwm.h index d0bc9e0..4ce4bb4 100644 --- a/wirish/pwm.h +++ b/wirish/pwm.h @@ -28,12 +28,8 @@ * @brief Arduino-compatible PWM interface. */ -#ifndef _PWM_H -#define _PWM_H - -#ifdef __cplusplus -extern "C"{ -#endif +#ifndef _PWM_H_ +#define _PWM_H_ /** * As a convenience, analogWrite is an alias of pwmWrite to ease @@ -47,13 +43,11 @@ extern "C"{ * * User code is expected to determine and honor the maximum value * (based on the configured period). + * + * @param pin PWM output pin + * @param duty_cycle Duty cycle to set. */ void pwmWrite(uint8 pin, uint16 duty_cycle); -#ifdef __cplusplus -} -#endif - - #endif diff --git a/wirish/rules.mk b/wirish/rules.mk index cb5a69f..6999288 100644 --- a/wirish/rules.mk +++ b/wirish/rules.mk @@ -4,30 +4,36 @@ dirstack_$(sp) := $(d) d := $(dir) BUILDDIRS += $(BUILD_PATH)/$(d) BUILDDIRS += $(BUILD_PATH)/$(d)/comm +BUILDDIRS += $(BUILD_PATH)/$(d)/boards -WIRISH_INCLUDES := -I$(d) -I$(d)/comm +WIRISH_INCLUDES := -I$(d) -I$(d)/comm -I$(d)/boards # Local flags CFLAGS_$(d) := $(WIRISH_INCLUDES) $(LIBMAPLE_INCLUDES) # Local rules and targets -cSRCS_$(d) := wirish.c \ - wirish_shift.c \ - wirish_analog.c \ - time.c \ - pwm.c \ - ext_interrupts.c \ - wirish_digital.c - -cppSRCS_$(d) := wirish_math.cpp \ - Print.cpp \ - comm/HardwareSerial.cpp \ - comm/HardwareSPI.cpp \ - usb_serial.cpp \ - HardwareTimer.cpp \ - cxxabi-compat.cpp \ - -cFILES_$(d) := $(cSRCS_$(d):%=$(d)/%) +cSRCS_$(d) := + +cppSRCS_$(d) := wirish_math.cpp \ + Print.cpp \ + boards.cpp \ + boards/maple.cpp \ + boards/maple_mini.cpp \ + boards/maple_native.cpp \ + boards/maple_RET6.cpp \ + comm/HardwareSerial.cpp \ + comm/HardwareSPI.cpp \ + HardwareTimer.cpp \ + usb_serial.cpp \ + cxxabi-compat.cpp \ + wirish_shift.cpp \ + wirish_analog.cpp \ + wirish_time.cpp \ + pwm.cpp \ + ext_interrupts.cpp \ + wirish_digital.cpp + +cFILES_$(d) := $(cSRCS_$(d):%=$(d)/%) cppFILES_$(d) := $(cppSRCS_$(d):%=$(d)/%) OBJS_$(d) := $(cFILES_$(d):%.c=$(BUILD_PATH)/%.o) \ diff --git a/wirish/usb_serial.cpp b/wirish/usb_serial.cpp index e2cdee3..0a2be43 100644 --- a/wirish/usb_serial.cpp +++ b/wirish/usb_serial.cpp @@ -3,28 +3,29 @@ * * Copyright (c) 2010 Perry Hung. * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. *****************************************************************************/ /** - * @brief Wirish USB class for easy communication, uses libmaple's - * virtual com port implementation + * @brief USB virtual serial terminal */ #include <string.h> @@ -34,7 +35,7 @@ #define USB_TIMEOUT 50 -USBSerial :: USBSerial(void) { +USBSerial::USBSerial(void) { } void USBSerial::begin(void) { @@ -46,54 +47,29 @@ void USBSerial::end(void) { } void USBSerial::write(uint8 ch) { - if(!(usbIsConnected() && usbIsConfigured())) { - return; - } - - uint16 status = 0; - uint32 start = millis(); - - while(status == 0 && (millis() - start <= USB_TIMEOUT)) { - status = usbSendBytes(&ch, 1); - } + const uint8 buf[] = {ch}; + this->write(buf, 1); } void USBSerial::write(const char *str) { - if(!(usbIsConnected() && usbIsConfigured())) { - return; - } - - uint32 len = strlen(str); - uint16 status = 0; - uint16 oldstatus = 0; - uint32 start = millis(); - - while(status < len && (millis() - start < USB_TIMEOUT)) { - status += usbSendBytes((uint8*)str+status, len-status); - if(oldstatus != status) - start = millis(); - oldstatus = status; - } + this->write(str, strlen(str)); } -void USBSerial::write(void *buf, uint32 size) { - if(!(usbIsConnected() && usbIsConfigured())) { +void USBSerial::write(const void *buf, uint32 len) { + if (!(usbIsConnected() && usbIsConfigured()) || !buf) { return; } - if (!buf) { - return; - } - - uint16 status = 0; - uint16 oldstatus = 0; + uint32 txed = 0; + uint32 old_txed = 0; uint32 start = millis(); - while(status < size && (millis() - start < USB_TIMEOUT)) { - status += usbSendBytes((uint8*)buf+status, size-status); - if(oldstatus != status) + while (txed < len && (millis() - start < USB_TIMEOUT)) { + txed += usbSendBytes((const uint8*)buf + txed, len - txed); + if (old_txed != txed) { start = millis(); - oldstatus = status; + } + old_txed = txed; } } @@ -101,47 +77,40 @@ uint32 USBSerial::available(void) { return usbBytesAvailable(); } -/* blocks forever until len_bytes is received */ uint32 USBSerial::read(void *buf, uint32 len) { if (!buf) { return 0; } - uint32 bytes_in = 0; - while (len > 0) { - uint32 new_bytes = usbReceiveBytes((uint8*)((uint8*)buf+bytes_in), len); - len -= new_bytes; - bytes_in += new_bytes; + uint32 rxed = 0; + while (rxed < len) { + rxed += usbReceiveBytes((uint8*)buf + rxed, len - rxed); } - return len; + return rxed; } -/* blocks forever until 1 byte is received */ +/* Blocks forever until 1 byte is received */ uint8 USBSerial::read(void) { - uint8 ch; - - while (usbReceiveBytes(&ch, 1) == 0); - return ch; + uint8 buf[1]; + this->read(buf, 1); + return buf[0]; } uint8 USBSerial::pending(void) { return usbGetPending(); } -// TODO deprecate the crap out of this +uint8 USBSerial::isConnected(void) { + return usbIsConnected() && usbIsConfigured(); +} + uint8 USBSerial::getDTR(void) { return usbGetDTR(); } -// TODO deprecate the crap out of this uint8 USBSerial::getRTS(void) { return usbGetRTS(); } -uint8 USBSerial::isConnected(void) { - return (usbIsConnected() && usbIsConfigured()); -} - USBSerial SerialUSB; - diff --git a/wirish/usb_serial.h b/wirish/usb_serial.h index c228837..ba9e18c 100644 --- a/wirish/usb_serial.h +++ b/wirish/usb_serial.h @@ -23,8 +23,7 @@ *****************************************************************************/ /** - * @brief wirish usb class for easy goin communication, uses libmaple's - * virtual com port implementation + * @brief Wirish virtual serial port */ #ifndef _USB_SERIAL_H_ @@ -32,6 +31,9 @@ #include "Print.h" +/** + * @brief Virtual serial terminal. + */ class USBSerial : public Print { public: USBSerial(void); @@ -46,7 +48,7 @@ public: void write(uint8); void write(const char *str); - void write(void *, uint32); + void write(const void*, uint32); uint8 getRTS(); uint8 getDTR(); diff --git a/wirish/wirish.c b/wirish/wirish.c deleted file mode 100644 index aaae9d4..0000000 --- a/wirish/wirish.c +++ /dev/null @@ -1,82 +0,0 @@ -/****************************************************************************** - * The MIT License - * - * Copyright (c) 2010 Perry Hung. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - *****************************************************************************/ - -/** - * @brief generic maple board bring up: - * - * By default, we bring up all maple boards running on the stm32 to 72mhz, - * clocked off the PLL, driven by the 8MHz external crystal. - * - * AHB and APB2 are clocked at 72MHz - * APB1 is clocked at 36MHz - */ - -#include "wirish.h" -#include "systick.h" -#include "gpio.h" -#include "nvic.h" -#include "usb.h" -#include "rcc.h" -#include "fsmc.h" -#include "dac.h" -#include "flash.h" - -void init(void) { - /* make sure the flash is ready before spinning the high speed clock up */ - flash_enable_prefetch(); - flash_set_latency(FLASH_WAIT_STATE_2); - -#if NR_FSMC > 0 - fsmc_native_sram_init(); -#endif - -#if NR_DAC_PINS > 0 - dac_init(); -#endif - - /* initialize clocks */ - rcc_clk_init(RCC_CLKSRC_PLL, RCC_PLLSRC_HSE, RCC_PLLMUL_9); - rcc_set_prescaler(RCC_PRESCALER_AHB, RCC_AHB_SYSCLK_DIV_1); - rcc_set_prescaler(RCC_PRESCALER_APB1, RCC_APB1_HCLK_DIV_2); - rcc_set_prescaler(RCC_PRESCALER_APB2, RCC_APB2_HCLK_DIV_1); - - nvic_init(); - systick_init(SYSTICK_RELOAD_VAL); - gpio_init(); - /* Initialize the ADC for slow conversions, to allow for high - impedance inputs. */ - adc_init(ADC_SMPR_55_5); - timer_init(TIMER1, 1); - timer_init(TIMER2, 1); - timer_init(TIMER3, 1); - timer_init(TIMER4, 1); -#if NR_TIMERS >= 8 - timer_init(TIMER5, 1); - timer_init(TIMER8, 1); -#endif - setupUSB(); - - /* include the board-specific init macro */ - BOARD_INIT; -} diff --git a/wirish/wirish.h b/wirish/wirish.h index 311c74f..82a9897 100644 --- a/wirish/wirish.h +++ b/wirish/wirish.h @@ -32,25 +32,20 @@ #define _WIRISH_H_ #include "libmaple.h" + +#include "wirish_types.h" #include "boards.h" -#include "time.h" -#include "timers.h" #include "io.h" #include "bits.h" #include "pwm.h" #include "ext_interrupts.h" +#include "wirish_debug.h" #include "wirish_math.h" - -#ifdef __cplusplus +#include "wirish_time.h" #include "HardwareSPI.h" #include "HardwareSerial.h" -#include "usb_serial.h" #include "HardwareTimer.h" -#endif - -#ifdef __cplusplus -extern "C"{ -#endif +#include "usb_serial.h" /* Arduino wiring macros and bit defines */ #define HIGH 0x1 @@ -74,12 +69,5 @@ extern "C"{ typedef uint8 boolean; typedef uint8 byte; -void init(void); -void shiftOut(uint8 dataPin, uint8 clockPin, uint8 bitOrder, byte val); - -#ifdef __cplusplus -} // extern "C" -#endif - #endif diff --git a/wirish/wirish_analog.c b/wirish/wirish_analog.cpp index 3c63342..8756caf 100644 --- a/wirish/wirish_analog.c +++ b/wirish/wirish_analog.cpp @@ -31,11 +31,12 @@ #include "io.h" /* Assumes that the ADC has been initialized and that the pin is set - * to ANALOG_INPUT */ -uint32 analogRead(uint8 pin) { - if(PIN_MAP[pin].adc == ADC_INVALID) { + * to INPUT_ANALOG */ +uint16 analogRead(uint8 pin) { + const adc_dev *dev = PIN_MAP[pin].adc_device; + if (dev == NULL) { return 0; } - return adc_read(PIN_MAP[pin].adc); + return adc_read(dev, PIN_MAP[pin].adc_channel); } diff --git a/wirish/wirish_debug.h b/wirish/wirish_debug.h new file mode 100644 index 0000000..d4c0bab --- /dev/null +++ b/wirish/wirish_debug.h @@ -0,0 +1,54 @@ +/****************************************************************************** + * The MIT License + * + * Copyright (c) 2011 LeafLabs, LLC. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + *****************************************************************************/ + +/** + * @file wirish_debug.h + * @brief High level debug port configuration + */ + +/** + * @brief Disable the JTAG and Serial Wire (SW) debug ports. + * + * You can call this function in order to use the JTAG and SW debug + * pins as ordinary GPIOs. + * + * @see enableDebugPorts() + */ +static inline void disableDebugPorts(void) { + afio_cfg_debug_ports(AFIO_DEBUG_NONE); +} + +/** + * @brief Enable the JTAG and Serial Wire (SW) debug ports. + * + * After you call this function, the JTAG and SW debug pins will no + * longer be usable as GPIOs. + * + * @see disableDebugPorts() + */ +static inline void enableDebugPorts(void) { + afio_cfg_debug_ports(AFIO_DEBUG_FULL_SWJ); +} diff --git a/wirish/wirish_digital.c b/wirish/wirish_digital.cpp index bb28f0a..9b9f175 100644 --- a/wirish/wirish_digital.c +++ b/wirish/wirish_digital.cpp @@ -30,39 +30,39 @@ #include "io.h" void pinMode(uint8 pin, WiringPinMode mode) { - uint8 outputMode; + gpio_pin_mode outputMode; boolean pwm = false; - if (pin >= NR_GPIO_PINS) { + if (pin >= BOARD_NR_GPIO_PINS) { return; } switch(mode) { case OUTPUT: - outputMode = GPIO_MODE_OUTPUT_PP; + outputMode = GPIO_OUTPUT_PP; break; case OUTPUT_OPEN_DRAIN: - outputMode = GPIO_MODE_OUTPUT_OD; + outputMode = GPIO_OUTPUT_OD; break; case INPUT: case INPUT_FLOATING: - outputMode = GPIO_MODE_INPUT_FLOATING; + outputMode = GPIO_INPUT_FLOATING; break; case INPUT_ANALOG: - outputMode = GPIO_MODE_INPUT_ANALOG; + outputMode = GPIO_INPUT_ANALOG; break; case INPUT_PULLUP: - outputMode = GPIO_MODE_INPUT_PU; + outputMode = GPIO_INPUT_PU; break; case INPUT_PULLDOWN: - outputMode = GPIO_MODE_INPUT_PD; + outputMode = GPIO_INPUT_PD; break; case PWM: - outputMode = GPIO_MODE_AF_OUTPUT_PP; + outputMode = GPIO_AF_OUTPUT_PP; pwm = true; break; case PWM_OPEN_DRAIN: - outputMode = GPIO_MODE_AF_OUTPUT_OD; + outputMode = GPIO_AF_OUTPUT_OD; pwm = true; break; default: @@ -70,51 +70,48 @@ void pinMode(uint8 pin, WiringPinMode mode) { return; } - gpio_set_mode(PIN_MAP[pin].port, PIN_MAP[pin].pin, outputMode); - - if (PIN_MAP[pin].timer_num != TIMER_INVALID) { - /* enable/disable timer channels if we're switching into or - out of pwm */ - if (pwm) { - timer_set_mode(PIN_MAP[pin].timer_num, - PIN_MAP[pin].timer_chan, - TIMER_PWM); - } else { - timer_set_mode(PIN_MAP[pin].timer_num, - PIN_MAP[pin].timer_chan, - TIMER_DISABLED); - } + gpio_set_mode(PIN_MAP[pin].gpio_device, PIN_MAP[pin].gpio_bit, outputMode); + + if (PIN_MAP[pin].timer_device != NULL) { + /* Enable/disable timer channels if we're switching into or + * out of PWM. */ + timer_set_mode(PIN_MAP[pin].timer_device, + PIN_MAP[pin].timer_channel, + pwm ? TIMER_PWM : TIMER_DISABLED); } } uint32 digitalRead(uint8 pin) { - if (pin >= NR_GPIO_PINS) { + if (pin >= BOARD_NR_GPIO_PINS) { return 0; } - return gpio_read_bit(PIN_MAP[pin].port, PIN_MAP[pin].pin); + return gpio_read_bit(PIN_MAP[pin].gpio_device, PIN_MAP[pin].gpio_bit) ? + HIGH : LOW; } void digitalWrite(uint8 pin, uint8 val) { - if (pin >= NR_GPIO_PINS) { + if (pin >= BOARD_NR_GPIO_PINS) { return; } - gpio_write_bit(PIN_MAP[pin].port, PIN_MAP[pin].pin, val); + gpio_write_bit(PIN_MAP[pin].gpio_device, PIN_MAP[pin].gpio_bit, val); } void togglePin(uint8 pin) { - if (pin >= NR_GPIO_PINS) { + if (pin >= BOARD_NR_GPIO_PINS) { return; } - - gpio_toggle_pin(PIN_MAP[pin].port, PIN_MAP[pin].pin); + + gpio_toggle_bit(PIN_MAP[pin].gpio_device, PIN_MAP[pin].gpio_bit); } +#define BUTTON_DEBOUNCE_DELAY 1 + uint8 isButtonPressed() { if (digitalRead(BOARD_BUTTON_PIN)) { - delay(1); + delay(BUTTON_DEBOUNCE_DELAY); while (digitalRead(BOARD_BUTTON_PIN)) ; return true; diff --git a/wirish/wirish_math.h b/wirish/wirish_math.h index 14614ba..f9062af 100644 --- a/wirish/wirish_math.h +++ b/wirish/wirish_math.h @@ -24,7 +24,7 @@ /** * @file wirish_math.h - * @brief Includes cmath; provides Arduino-compatible math routines. + * @brief Includes <math.h>; provides Arduino-compatible math routines. */ #ifndef _WIRING_MATH_H_ @@ -32,8 +32,6 @@ #include <math.h> -#ifdef __cplusplus - /** * @brief Initialize the pseudo-random number generator. * @param seed the number used to initialize the seed; cannot be zero. @@ -78,8 +76,7 @@ long random(long min, long max); * @param toEnd the end of the value's mapped range. * @return the mapped value. */ -/* TODO: profile code bloat due to inlining this */ -inline long map(long value, long fromStart, long fromEnd, +static inline long map(long value, long fromStart, long fromEnd, long toStart, long toEnd) { return (value - fromStart) * (toEnd - toStart) / (fromEnd - fromStart) + toStart; @@ -103,9 +100,7 @@ inline long map(long value, long fromStart, long fromEnd, #ifdef abs #undef abs #endif -#define abs(x) (((x) > 0) ? (x) : -(unsigned)(x)) - -#endif +#define abs(x) (((x) > 0) ? (x) : -(x)) /* Following are duplicate declarations (with Doxygen comments) for * some of the math.h functions; this is for the convenience of the diff --git a/wirish/wirish_shift.c b/wirish/wirish_shift.cpp index f67364d..f67364d 100644 --- a/wirish/wirish_shift.c +++ b/wirish/wirish_shift.cpp diff --git a/wirish/time.c b/wirish/wirish_time.cpp index c0a0649..2771e95 100644 --- a/wirish/time.c +++ b/wirish/wirish_time.cpp @@ -28,7 +28,8 @@ #include "libmaple.h" #include "systick.h" -#include "time.h" +#include "wirish_time.h" +#include "delay.h" void delay(unsigned long ms) { uint32 i; @@ -38,15 +39,5 @@ void delay(unsigned long ms) { } void delayMicroseconds(uint32 us) { - /* So (2^32)/12 micros max, or less than 6 minutes */ - us *= 12; - - /* fudge for function call overhead */ - us--; - asm volatile(" mov r0, %[us] \n\t" - "1: subs r0, #1 \n\t" - " bhi 1b \n\t" - : - : [us] "r" (us) - : "r0"); + delay_us(us); } diff --git a/wirish/time.h b/wirish/wirish_time.h index 8d3d074..d5349e3 100644 --- a/wirish/time.h +++ b/wirish/wirish_time.h @@ -23,22 +23,18 @@ *****************************************************************************/ /** - * @file time.h + * @file wirish_time.h * @brief Timing and delay functions. */ -#ifndef _TIME_H -#define _TIME_H +#ifndef _TIME_H_ +#define _TIME_H_ #include "libmaple.h" #include "nvic.h" #include "systick.h" #include "boards.h" -#ifdef __cplusplus -extern "C"{ -#endif - #define US_PER_MS 1000 /** @@ -60,17 +56,15 @@ static inline uint32 micros(void) { uint32 cycle_cnt; uint32 res; - nvic_globalirq_disable(); - - cycle_cnt = systick_get_count(); - ms = millis(); - - nvic_globalirq_enable(); + do { + cycle_cnt = systick_get_count(); + ms = millis(); + } while (ms != millis()); /* SYSTICK_RELOAD_VAL is 1 less than the number of cycles it actually takes to complete a SysTick reload */ res = (ms * US_PER_MS) + - (SYSTICK_RELOAD_VAL + 1 - cycle_cnt)/CYCLES_PER_MICROSECOND; + (SYSTICK_RELOAD_VAL + 1 - cycle_cnt) / CYCLES_PER_MICROSECOND; return res; } @@ -99,10 +93,4 @@ void delay(unsigned long ms); */ void delayMicroseconds(uint32 us); -#ifdef __cplusplus -} // extern "C" #endif - - -#endif - diff --git a/wirish/wirish_types.h b/wirish/wirish_types.h new file mode 100644 index 0000000..39efae0 --- /dev/null +++ b/wirish/wirish_types.h @@ -0,0 +1,65 @@ +/****************************************************************************** + * The MIT License + * + * Copyright (c) 2011 LeafLabs, LLC. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + *****************************************************************************/ + +/** + * @file wirish_types.h + * @author Marti Bolivar <mbolivar@leaflabs.com> + * @brief Wirish library type definitions. + */ + +#include "libmaple_types.h" +#include "gpio.h" +#include "timer.h" +#include "adc.h" + +#ifndef _WIRISH_TYPES_H_ +#define _WIRISH_TYPES_H_ + +/** + * Invalid stm32_pin_info adc_channel value. + * @see stm32_pin_info + */ +#define ADCx 0xFF + +/** + * @brief Stores STM32-specific information related to a given Maple pin. + * @see PIN_MAP + */ +typedef struct stm32_pin_info { + gpio_dev *gpio_device; /**< Maple pin's GPIO device */ + timer_dev *timer_device; /**< Pin's timer device, if any. */ + const adc_dev *adc_device; /**< ADC device, if any. */ + uint8 gpio_bit; /**< Pin's GPIO port bit. */ + uint8 timer_channel; /**< Timer channel, or 0 if none. */ + uint8 adc_channel; /**< Pin ADC channel, or ADCx if none. */ +} stm32_pin_info; + +/** + * Variable attribute, instructs the linker to place the marked + * variable in Flash instead of RAM. */ +#define __FLASH__ __attr_flash + +#endif |