From 6c956a383834b66c29591294f0926ced22f3e3b7 Mon Sep 17 00:00:00 2001 From: Marti Bolivar Date: Fri, 22 Oct 2010 21:13:02 -0400 Subject: maple mini runs blinky now. still need usb descriptors to improve, and also nothing else is tested. --- wirish/HardwareTimer.cpp | 18 +-- wirish/HardwareTimer.h | 285 +++++++++++++++++++++++++++++++++++++++++++++-- wirish/boards.h | 79 +++++++++++++ wirish/ext_interrupts.c | 20 +--- wirish/ext_interrupts.h | 41 ++++++- wirish/io.h | 149 ++++++++++++++++++------- wirish/pwm.h | 11 +- wirish/wirish_digital.c | 7 +- 8 files changed, 522 insertions(+), 88 deletions(-) (limited to 'wirish') diff --git a/wirish/HardwareTimer.cpp b/wirish/HardwareTimer.cpp index 6fbad8b..5b80ec1 100644 --- a/wirish/HardwareTimer.cpp +++ b/wirish/HardwareTimer.cpp @@ -22,10 +22,8 @@ * THE SOFTWARE. *****************************************************************************/ -/** - * @brief wirish timer class to manage the four 16-bit timer peripherals - * - * This implementation is not very efficient (lots of duplicated functions) +/* + * wirish timer class to manage the four 16-bit timer peripherals */ #include "wirish.h" @@ -69,10 +67,6 @@ uint16 HardwareTimer::getCount(void) { return timer_get_count(this->timerNum); } -/* This function will set the prescaler and overflow to get a period - * of the given length with the most resolution; the return value is - * the overflow value and thus the largest value that can be set as a - * compare. */ uint16 HardwareTimer::setPeriod(uint32 microseconds) { // XXX: 72MHz shouldn't be hard coded in here... global define? @@ -92,19 +86,19 @@ uint16 HardwareTimer::setPeriod(uint32 microseconds) { return this->overflow; } -void HardwareTimer::setChannel1Mode(uint8 mode) { +void HardwareTimer::setChannel1Mode(TimerMode mode) { timer_set_mode(this->timerNum,1,mode); } -void HardwareTimer::setChannel2Mode(uint8 mode) { +void HardwareTimer::setChannel2Mode(TimerMode mode) { timer_set_mode(this->timerNum,2,mode); } -void HardwareTimer::setChannel3Mode(uint8 mode) { +void HardwareTimer::setChannel3Mode(TimerMode mode) { timer_set_mode(this->timerNum,3,mode); } -void HardwareTimer::setChannel4Mode(uint8 mode) { +void HardwareTimer::setChannel4Mode(TimerMode mode) { timer_set_mode(this->timerNum,4,mode); } diff --git a/wirish/HardwareTimer.h b/wirish/HardwareTimer.h index c6e11c8..b05085f 100644 --- a/wirish/HardwareTimer.h +++ b/wirish/HardwareTimer.h @@ -29,6 +29,26 @@ #ifndef _TIMER_H_ #define _TIMER_H_ +/** + * Interface to one of the 16-bit timer peripherals. + * + * User code should not instantiate this class directly; instead, use + * one of the predefined Timer instances (Timer1, Timer2, etc.). + * + * HardwareTimer instances can be configured to generate periodic or + * delayed events with minimal work done by the microcontroller. Each + * timer maintains a single 16-bit count that can be configured with a + * prescaler and overflow value. + * + * By default, a timer's counter is incremented once per clock cycle. + * The prescaler acts as a divider of the 72MHz Maple system clock; + * without prescaling, the timer's count would reach 65535 (2**16-1) + * and roll over over 1000 times per second. + * + * The overflow value is the maximum value the counter will reach. It + * defaults to 65535; smaller values will cause the counter to reset + * more frequently. + */ class HardwareTimer { private: uint16 overflow; @@ -37,39 +57,282 @@ class HardwareTimer { public: HardwareTimer(uint8 timer_num); + /** + * Stop the counter, without affecting its configuration. + * + * The timer will no longer count or fire interrupts after this + * function is called, until it is resumed. This function is + * useful during timer setup periods, in order to prevent + * interrupts from firing before the timer is fully configured. + * + * Note that there is some function call overhead associated with + * this method, so using it in concert with + * HardwareTimer::resume() is not a robust way to align multiple + * timers to the same count value. + * + * @see HardwareTimer::resume() + */ void pause(void); + + /** + * Resume a paused timer, without affecting its configuration. + * + * The timer will resume counting and firing interrupts as + * appropriate. + * + * Note that there is some function call overhead associated with + * using this method, so using it in concert with + * HardwareTimer::resume() is not a robust way to align multiple + * timers to the same count value. + * + * @see HardwareTimer::pause() + */ void resume(void); + + /** + * Set the timer prescale. + * + * The prescaler acts as a clock divider to slow down the rate at + * which the counter increments. + * + * For example, the system clock rate is 72MHz, so the counter + * will reach 65535 in (13.89 nanoseconds) * (65535 counts) = + * (910.22 microseconds), or about a thousand times a second. If + * the prescaler equals 1098, then the clock rate is effectively + * 65.56KHz, and the counter will reach 65536 in (15.25 + * microseconds) * (65536 counts) = (0.999 seconds), or about once + * per second. + * + * The HardwareTimer::setPeriod() method may also be used as a + * convenient alternative. + * + * @param factor The new prescale value to set. + * @see HardwareTimer::setPeriod() + */ void setPrescaleFactor(uint16 factor); - void setOverflow(uint16 val); // truncates to overflow - void setCount(uint16 val); // truncates to overflow + + /** + * Sets the timer overflow (or "reload") value. + * + * When the timer's counter reaches this, value it resets to + * zero. Its default value is 65535 (the largest unsigned 16-bit + * integer); setting the overflow to anything lower will cause + * interrupts to be called more frequently (see the setPeriod() + * function below for a shortcut). This number sets the maximum + * value for the channel compare values. + * + * @param val The new overflow value to set + * @see HardwareTimer::setOverflow() + */ + void setOverflow(uint16 val); + + /** + * Set the current timer count. + * + * Note that there is some function call overhead associated with + * callign this method, so using it is not a robust way to get + * multiple timers to share a count value. + * + * @param val The new count value to set. If this value exceeds + * the timer's overflow value, it is truncated to the + * overflow value. + */ + void setCount(uint16 val); + + /** + * Retrieve the current timer count. + * + * @return The timer's current count value + */ uint16 getCount(void); - // tries to set prescaler and overflow wisely; returns overflow + /** + * Configure the prescaler and overflow values to generate a timer + * reload with a period as close to the given number of + * microseconds as possible. + * + * The return value is the overflow, which may be used to set + * channel compare values. However, if a clock that fires an + * interrupt every given number of microseconds is all that is + * desired, and the relative "phases" are unimportant, channel + * compare values may all be set to 1. + * + * @param microseconds the desired period of the timer. + * @return the overflow value (and thus, the largest value that can be + * set as a compare). + */ uint16 setPeriod(uint32 microseconds); - void setChannel1Mode(uint8 mode); - void setChannel2Mode(uint8 mode); - void setChannel3Mode(uint8 mode); - void setChannel4Mode(uint8 mode); - void setCompare1(uint16 val); // truncates to overflow - void setCompare2(uint16 val); // truncates to overflow - void setCompare3(uint16 val); // truncates to overflow - void setCompare4(uint16 val); // truncates to overflow + + /** + * Set channel 1 of this timer to the given mode. + * + * Note: Timer1.setChannel1Mode(TIMER_PWM) may not work as + * expected; if you want PWM functionality on a channel make sure + * you don't set it to something else! + * + * @see TimerMode + */ + void setChannel1Mode(TimerMode mode); + + /** + * Set channel 2 of this timer to the given mode. + * @see TimerMode + */ + void setChannel2Mode(TimerMode mode); + + /** + * Set channel 3 of this timer to the given mode. + * @see TimerMode + */ + void setChannel3Mode(TimerMode mode); + + /** + * Set channel 4 of this timer to the given mode. + * @see TimerMode + */ + void setChannel4Mode(TimerMode mode); + + /** + * Sets the compare value for channel 1. + * + * When the counter reaches this value the interrupt for this + * channel will fire if channel 1 mode is TIMER_OUTPUTCOMPARE and + * an interrupt is attached. + * + * By default, this only changes the relative offsets between + * events on a single timer ("phase"); they don't control the + * frequency with which they occur. However, a common trick is to + * increment the compare value manually in the interrupt handler + * so that the event will fire again after the increment + * period. There can be a different increment value for each + * channel, so this trick allows events to be programmed at 4 + * different rates on a single timer. Note that function call + * overheads mean that the smallest increment rate is at least a + * few microseconds. + * + * @param val The compare value to set. If greater than this + * timer's overflow value, it will be truncated to the + * overflow value. + * + * @see TimerMode + * @see HardwareTimer::setChannel1Mode() + */ + void setCompare1(uint16 val); + + /** + * Sets the compare value for channel 2. + * + * @param val The compare value to set. If greater than this + * timer's overflow value, it will be truncated to the + * overflow value. + * @see HardwareTimer::setCompare1() + */ + void setCompare2(uint16 val); + + /** + * Sets the compare value for channel 3. + * + * @param val The compare value to set. If greater than this + * timer's overflow value, it will be truncated to the + * overflow value. + * @see HardwareTimer::setCompare1() + */ + void setCompare3(uint16 val); + + /** + * Sets the compare value for channel 4. + * + * @param val The compare value to set. If greater than this + * timer's overflow value, it will be truncated to the + * overflow value. + * @see HardwareTimer::setCompare1() + */ + void setCompare4(uint16 val); + + /** + * Attach an interrupt handler to this timer's channel 1. This + * interrupt handler will be called when the timer's counter + * reaches its channel 1 compare value. + * + * The argument should be a function which takes no arguments and + * has no return value; i.e. it should have signature + * + * void (*handler)(void); + * + * Note: The function (often called an interrupt service routine, + * or ISR) should attempt to return as quickly as possible. + * Blinking the LED, some logic, PWM updates, and Serial writes + * are fine; writing to SerialUSB or waiting for user input can + * take a long time and other compare interrupts won't fire. Tip: + * if you have a delay() in your interrupt routine, you're probably + * doing it wrong. + * + * @param handler The ISR to attach to channel 1. + * @see voidFuncPtr + */ void attachCompare1Interrupt(voidFuncPtr handler); + + /** + * Like attachCompare1Interrupt(), but for channel 2. + * @see HardwareTimer::attachCompare1Interrupt() + */ void attachCompare2Interrupt(voidFuncPtr handler); + + /** + * Like attachCompare1Interrupt(), but for channel 3. + * @see HardwareTimer::attachCompare1Interrupt() + */ void attachCompare3Interrupt(voidFuncPtr handler); + + /** + * Like attachCompare1Interrupt(), but for channel 4. + * @see HardwareTimer::attachCompare1Interrupt() + */ void attachCompare4Interrupt(voidFuncPtr handler); + + /** + * Remove the interrupt handler attached to channel 1, if any. + * The handler will no longer be called by this timer. + * @see HardwareTimer::attachCompare1Interrupt() + */ void detachCompare1Interrupt(void); + + /** + * Remove the interrupt handler attached to channel 2, if any. + * The handler will no longer be called by this timer. + * @see HardwareTimer::attachCompare1Interrupt() + */ void detachCompare2Interrupt(void); + + /** + * Remove the interrupt handler attached to channel 3, if any. + * The handler will no longer be called by this timer. + * @see HardwareTimer::attachCompare1Interrupt() + */ void detachCompare3Interrupt(void); + + /** + * Remove the interrupt handler attached to channel 4, if any. + * The handler will no longer be called by this timer. + * @see HardwareTimer::attachCompare1Interrupt() + */ void detachCompare4Interrupt(void); }; +/** Pre-instantiated timer for use by user code. */ extern HardwareTimer Timer1; +/** Pre-instantiated timer for use by user code. */ extern HardwareTimer Timer2; +/** Pre-instantiated timer for use by user code. */ extern HardwareTimer Timer3; +/** Pre-instantiated timer for use by user code. */ extern HardwareTimer Timer4; #if NR_TIMERS >= 8 +/** Pre-instantiated timer for use by user code, on devices with + more than four timers (this does not include the Maple). */ extern HardwareTimer Timer5; +/** Pre-instantiated timer for use by user code, on devices with + more than four timers (this does not include the Maple). */ extern HardwareTimer Timer8; #endif diff --git a/wirish/boards.h b/wirish/boards.h index fed5ead..0625d0a 100644 --- a/wirish/boards.h +++ b/wirish/boards.h @@ -292,6 +292,85 @@ typedef struct PinMapping { }; #endif +#ifdef BOARD_maple_mini + + #define CYCLES_PER_MICROSECOND 72 + #define MAPLE_RELOAD_VAL 71999 /* takes a cycle to reload */ + + static __attribute__ ((unused)) PinMapping PIN_MAP[NR_GPIO_PINS] = { + /* D0/PC15 */ + {GPIOC_BASE, 15, ADC_INVALID, TIMER_INVALID, EXTI_CONFIG_PORTC, TIMER_INVALID, TIMER_INVALID}, + /* D1/PA0 */ + {GPIOA_BASE, 0, ADC0, TIMER2_CH1_CCR, EXTI_CONFIG_PORTA, TIMER2, 1}, + /* D2/PA1 */ + {GPIOA_BASE, 1, ADC1, TIMER2_CH2_CCR, EXTI_CONFIG_PORTA, TIMER2, 2}, + /* D3/PA2 */ + {GPIOA_BASE, 2, ADC2, TIMER2_CH3_CCR, EXTI_CONFIG_PORTA, TIMER2, 3}, + /* D4/PA3 */ + {GPIOA_BASE, 3, ADC3, TIMER2_CH4_CCR, EXTI_CONFIG_PORTA, TIMER2, 4}, + /* D5/PA4 */ + {GPIOA_BASE, 4, ADC4, TIMER_INVALID, EXTI_CONFIG_PORTA, TIMER_INVALID, TIMER_INVALID}, + /* D6/PA5 */ + {GPIOA_BASE, 5, ADC5, TIMER_INVALID, EXTI_CONFIG_PORTA, TIMER_INVALID, TIMER_INVALID}, + /* D7/PA6 */ + {GPIOA_BASE, 6, ADC6, TIMER3_CH1_CCR, EXTI_CONFIG_PORTA, TIMER3, 1}, + /* D8/PA7 */ + {GPIOA_BASE, 7, ADC7, TIMER3_CH2_CCR, EXTI_CONFIG_PORTA, TIMER3, 2}, + /* D9/PB0 */ + {GPIOB_BASE, 0, ADC8, TIMER3_CH3_CCR, EXTI_CONFIG_PORTB, TIMER3, 3}, + /* D10/PB1 */ + {GPIOB_BASE, 1, ADC9, TIMER3_CH4_CCR, EXTI_CONFIG_PORTB, TIMER3, 4}, + /* D11/PB2 */ + {GPIOB_BASE, 2, ADC_INVALID, TIMER_INVALID, EXTI_CONFIG_PORTB, TIMER_INVALID, TIMER_INVALID}, + /* D12/PB10 */ + {GPIOB_BASE, 10, ADC_INVALID, TIMER_INVALID, EXTI_CONFIG_PORTB, TIMER_INVALID, TIMER_INVALID}, + /* D13/PB11 */ + {GPIOB_BASE, 11, ADC_INVALID, TIMER_INVALID, EXTI_CONFIG_PORTB, TIMER_INVALID, TIMER_INVALID}, + /* D14/PB13 */ + {GPIOB_BASE, 13, ADC_INVALID, TIMER_INVALID, EXTI_CONFIG_PORTB, TIMER_INVALID, TIMER_INVALID}, + /* D15/PB14 */ + {GPIOB_BASE, 14, ADC_INVALID, TIMER_INVALID, EXTI_CONFIG_PORTB, TIMER_INVALID, TIMER_INVALID}, + /* D16/PB15 */ + {GPIOB_BASE, 15, ADC_INVALID, TIMER_INVALID, EXTI_CONFIG_PORTB, TIMER_INVALID, TIMER_INVALID}, + /* D17/PA8 */ + {GPIOA_BASE, 8, ADC_INVALID, TIMER1_CH1_CCR, EXTI_CONFIG_PORTB, TIMER1, 1}, + /* D18/PA9 */ + {GPIOA_BASE, 9, ADC_INVALID, TIMER1_CH2_CCR, EXTI_CONFIG_PORTA, TIMER2, 2}, + /* D19/PA10 */ + {GPIOA_BASE, 10, ADC_INVALID, TIMER1_CH3_CCR, EXTI_CONFIG_PORTA, TIMER1, 3}, + /* D20/PA11 */ + {GPIOA_BASE, 11, ADC_INVALID, TIMER1_CH4_CCR, EXTI_CONFIG_PORTA, TIMER1, 4}, + /* D21/PA12 */ + {GPIOA_BASE, 12, ADC_INVALID, TIMER_INVALID, EXTI_CONFIG_PORTA, TIMER_INVALID, TIMER_INVALID}, + /* D22/PA13 */ + {GPIOA_BASE, 13, ADC_INVALID, TIMER_INVALID, EXTI_CONFIG_PORTA, TIMER_INVALID, TIMER_INVALID}, + /* D23/PA14 */ + {GPIOA_BASE, 14, ADC_INVALID, TIMER_INVALID, EXTI_CONFIG_PORTA, TIMER_INVALID, TIMER_INVALID}, + /* D24/PA15 */ + {GPIOA_BASE, 15, ADC_INVALID, TIMER_INVALID, EXTI_CONFIG_PORTA, TIMER_INVALID, TIMER_INVALID}, + /* D25/PB3 */ + {GPIOB_BASE, 3, ADC_INVALID, TIMER_INVALID, EXTI_CONFIG_PORTB, TIMER_INVALID, TIMER_INVALID}, + /* D26/PB4 */ + {GPIOB_BASE, 4, ADC_INVALID, TIMER_INVALID, EXTI_CONFIG_PORTB, TIMER_INVALID, TIMER_INVALID}, + /* D27/PB5 */ + {GPIOB_BASE, 5, ADC_INVALID, TIMER_INVALID, EXTI_CONFIG_PORTB, TIMER_INVALID, TIMER_INVALID}, + /* D28/PB6 */ + {GPIOB_BASE, 6, ADC_INVALID, TIMER4_CH1_CCR, EXTI_CONFIG_PORTB, TIMER4, 1}, + /* D29/PB7 */ + {GPIOB_BASE, 7, ADC_INVALID, TIMER4_CH2_CCR, EXTI_CONFIG_PORTB, TIMER4, 1}, + /* D30/PC13 */ + {GPIOC_BASE, 13, ADC_INVALID, TIMER_INVALID, EXTI_CONFIG_PORTC, TIMER_INVALID, TIMER_INVALID}, + /* D31/PC14 */ + {GPIOC_BASE, 14, ADC_INVALID, TIMER_INVALID, EXTI_CONFIG_PORTC, TIMER_INVALID, TIMER_INVALID}, + /* D32/PB8 */ + {GPIOB_BASE, 8, ADC_INVALID, TIMER4_CH3_CCR, EXTI_CONFIG_PORTB, TIMER4, 3}, + /* D33/PB12 */ + {GPIOB_BASE, 12, ADC_INVALID, TIMER_INVALID, EXTI_CONFIG_PORTB, TIMER_INVALID, TIMER_INVALID}, + }; + + +#endif + #ifndef CYCLES_PER_MICROSECOND #error "Board type has not been selected correctly." #endif diff --git a/wirish/ext_interrupts.c b/wirish/ext_interrupts.c index f02cdc5..dd7c1a8 100644 --- a/wirish/ext_interrupts.c +++ b/wirish/ext_interrupts.c @@ -32,17 +32,8 @@ #include "exti.h" #include "ext_interrupts.h" -/** - * @brief Attach an interrupt handler to be triggered on a given - * transition on the pin. Runs in interrupt context - * - * @param pin Maple pin number - * @param handler Function to run upon external interrupt trigger. - * @param mode Type of transition to trigger on, eg falling, rising, etc. - * - * @sideeffect Registers a handler - */ -void attachInterrupt(uint8 pin, voidFuncPtr handler, uint32 mode) { +/* Attach ISR handler on pin, triggering on the given mode. */ +void attachInterrupt(uint8 pin, voidFuncPtr handler, ExtIntTriggerMode mode) { uint8 outMode; /* Parameter checking */ @@ -65,6 +56,7 @@ void attachInterrupt(uint8 pin, voidFuncPtr handler, uint32 mode) { outMode = EXTI_RISING_FALLING; break; default: + ASSERT(0); return; } @@ -76,11 +68,7 @@ void attachInterrupt(uint8 pin, voidFuncPtr handler, uint32 mode) { return; } -/** - * @brief Disable an external interrupt - * @param pin maple pin number - * @sideeffect unregisters external interrupt handler - */ +/* Disable any interrupts */ void detachInterrupt(uint8 pin) { if (!(pin < NR_GPIO_PINS)) { return; diff --git a/wirish/ext_interrupts.h b/wirish/ext_interrupts.h index fef8c8f..80e2e9e 100644 --- a/wirish/ext_interrupts.h +++ b/wirish/ext_interrupts.h @@ -22,6 +22,8 @@ * THE SOFTWARE. *****************************************************************************/ +#include "libmaple_types.h" + /** * @file ext_interrupts.h * @@ -31,17 +33,44 @@ #ifndef _EXT_INTERRUPTS_H_ #define _EXT_INTERRUPTS_H_ -enum { - RISING, - FALLING, - CHANGE -}; +/** + * The kind transition on an external pin which should trigger an + * interrupt. + */ +typedef enum ExtIntTriggerMode_ { + RISING, /**< To trigger an interrupt when the pin transitions LOW + to HIGH */ + FALLING, /**< To trigger an interrupt when the pin transitions + HIGH to LOW */ + CHANGE /**< To trigger an interrupt when the pin transitions from + LOW to HIGH or HIGH to LOW (i.e., when the pin + changes). */ +} ExtIntTriggerMode; #ifdef __cplusplus extern "C"{ #endif -void attachInterrupt(uint8 pin, voidFuncPtr, uint32 mode); +/** + * @brief Registers an interrupt handler on a pin. + * + * The interrupt will be triggered on a given transition on the pin, + * as specified by the mode parameter. The handler runs in interrupt + * context. + * + * @param pin Maple pin number + * @param handler Function to run upon external interrupt trigger. + * @param mode Type of transition to trigger on, e.g. falling, rising, etc. + * + * @sideeffect Registers a handler + */ +void attachInterrupt(uint8 pin, voidFuncPtr handler, ExtIntTriggerMode mode); + +/** + * @brief Disable any registered external interrupt. + * @param pin Maple pin number + * @sideeffect unregisters external interrupt handler + */ void detachInterrupt(uint8 pin); #ifdef __cplusplus diff --git a/wirish/io.h b/wirish/io.h index 647e79c..f82e414 100644 --- a/wirish/io.h +++ b/wirish/io.h @@ -38,54 +38,125 @@ extern "C"{ #endif +/** + * Specifies a GPIO pin behavior. + * + * Each of the GPIO pins on a Maple board may be configured using + * pinMode() to behave in a number of ways: as a digital output pin, + * or as an analog input pin, etc., depending on the particular pin. + * + * This enum specifies the complete set of possible configurations; + * not every pin can have all of these modes. For example, on the + * Maple, pin 15 may be configured as INPUT_ANALOG, but not as PWM. + * See your device's silkscreen and and the GPIO documentation for + * more information. + * + * @see pinMode() + */ typedef enum WiringPinMode { - OUTPUT, - OUTPUT_OPEN_DRAIN, - INPUT, - INPUT_ANALOG, - INPUT_PULLUP, - INPUT_PULLDOWN, - INPUT_FLOATING, - PWM, - PWM_OPEN_DRAIN, + OUTPUT, /**< Basic digital output: when the pin is HIGH, the + voltage is held at +3.3v (Vcc) and when it is LOW, it + is pulled down to ground. */ + + OUTPUT_OPEN_DRAIN, /**< In open drain mode, the pin indicates + "low" by accepting current flow to ground + and "high" by providing increased + impedance. An example use would be to + connect a pin to a bus line (which is pulled + up to a positive voltage by a separate + supply through a large resistor). When the + pin is high, not much current flows through + to ground and the line stays at positive + voltage; when the pin is low the bus + "drains" to ground with a small amount of + current constantly flowing through the large + resistor from the external supply. In this + mode no current is ever actually /sourced/ + from the pin. */ + + INPUT, /**< Basic digital input. The pin voltage is sampled; when + it is closer to 3.3v (Vcc) the pin status is high, and + when it is closer to 0v (ground) it is low. If no + external circuit is pulling the pin voltage to high or + low, it will tend to randomly oscillate and be very + sensitive to noise (eg a breath of air across the pin + will cause the state to flip). */ + + INPUT_ANALOG, /**< This is a special mode for when the pin will be + used for analog (not digital) reads. Enables ADC + conversion to be performed on the voltage at the + pin. */ + + INPUT_PULLUP, /**< The state of the pin in this mode is reported + the same way as with INPUT, but the pin voltage + is gently "pulled up" towards +3.3v. This means + the state will be high unless an external device + is specifically pulling the pin down to ground, + in which case the "gentle" pull up will not + affect the state of the input. */ + + INPUT_PULLDOWN, /**< The state of the pin in this mode is reported + the same way as with INPUT, but the pin voltage + is gently "pulled down" towards 0v. This means + the state will be low unless an external device + is specifically pulling the pin up to 3.3v, in + which case the "gentle" pull down will not + effect the state of the input. */ + + INPUT_FLOATING, /**< Synonym for INPUT. */ + + PWM, /**< This is a special mode for when the pin will be used for + PWM output (a special case of digital output). */ + + PWM_OPEN_DRAIN, /**< Like PWM, except that instead of alternating + cycles of LOW and HIGH, the voltage on the pin + consists of alternating cycles of LOW and + floating (disconnected). */ } WiringPinMode; +/** + * Configure behavior of a GPIO pin. + * + * @param pin Pin to configure. One of: 0-38 (pin numbers as labeled + * on silkscreen), or D0-D38 (symbols for same) + * @param mode Mode corresponding to desired pin behavior. + * @see WiringPinMode + */ +void pinMode(uint8 pin, WiringPinMode mode); -/* Set pin to mode - * pinMode(pin, mode): - * pin -> {0-38, D0-D39, A0-16} - * mode -> { - * INPUT/INPUT_DIGITAL - * INPUT_PULLUP - * INPUT_PULLDOWN - * INPUT_ANALOG - * OUTPUT/OUTPUT_PP - * OUTPUT_OPEN_DRAIN - * } +/** + * Writes a (digital) value to a pin. The pin must have its + * mode set to 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 value Either LOW (write a 0) or HIGH (write a 1). + * @see pinMode() */ -void pinMode(uint8, uint8); - -/* - * Writes VALUE to digital pin[0-38] - * digitalWrite(pin, value): - * pin -> {0-38, D0-D39, A0-16} - * value -> LOW, HIGH; -*/ -void digitalWrite(uint8, uint8); - -/* Read a digital value from pin, the pin mode must be set to - * {INPUT, INPUT_PULLUP, INPUT_PULLDOWN} - * digitalRead(pin) - * pin -> {0-38, D0-D39, A0-16} +void digitalWrite(uint8 pin, uint8 value); + +/** + * Read a digital value from a pin. The pin must have its mode set to + * one of INPUT, INPUT_PULLUP, and INPUT_PULLDOWN. + * + * @param pin Pin to read from. One of: 0-38 (pin numbers as labeled + * on silkscreen), or D0-D38 (symbols for same) + * @return LOW or HIGH. + * @see pinMode() */ uint32 digitalRead(uint8); -/* Read an analog value from pin, the pin mode must be set - * to INPUT_ANALOG - * analogRead(pin) - * pin -> {A0-A16} +/** + * Read an analog value from pin. This function blocks during ADC + * conversion. The pin must have its mode set to INPUT_ANALOG. + * + * @param pin Pin to read from. One of: 0-38 (pin numbers as labeled + * on silkscreen), or D0-D38 (symbols for same) + * @return ADC-converted voltage, in the range 0--4095, inclusive. + * @see pinMode() + * @see analogReference() */ -uint32 analogRead(uint8); +uint32 analogRead(uint8 pin); #ifdef __cplusplus } // extern "C" diff --git a/wirish/pwm.h b/wirish/pwm.h index fe170cd..6d0ddaf 100644 --- a/wirish/pwm.h +++ b/wirish/pwm.h @@ -36,7 +36,16 @@ extern "C"{ #endif #define analogWrite pwmWrite -void pwmWrite(uint8, uint16); + +/** + * Set the PWM duty. + * + * User code is expected to determine and honor the maximum value + * (based on the configured period). As a convenience, analogWrite is + * an alias of pwmWrite to ease porting Arduino code, though period + * and duty will have to be recalibrated + */ +void pwmWrite(uint8 pin, uint16 duty_cycle); #ifdef __cplusplus } diff --git a/wirish/wirish_digital.c b/wirish/wirish_digital.c index 9298b60..aa22196 100644 --- a/wirish/wirish_digital.c +++ b/wirish/wirish_digital.c @@ -22,8 +22,8 @@ * THE SOFTWARE. *****************************************************************************/ -/** - * @brief Arduino-compatible digital I/O implementation. +/* + * Arduino-compatible digital I/O implementation. */ #include "wirish.h" @@ -73,7 +73,8 @@ void pinMode(uint8 pin, WiringPinMode mode) { gpio_set_mode(PIN_MAP[pin].port, PIN_MAP[pin].pin, outputMode); if (PIN_MAP[pin].timer_num != TIMER_INVALID) { - /* enable/disable timer channels if we're switching into or out of pwm */ + /* enable/disable timer channels if we're switching into or + out of pwm */ if (pwm) { timer_set_mode(PIN_MAP[pin].timer_num, PIN_MAP[pin].timer_chan, -- cgit v1.2.3