From 7a89d1d3fe6ad65f2ed400760489f1f09338bf9c Mon Sep 17 00:00:00 2001 From: theshed Date: Sun, 2 Jun 2013 21:39:32 +0000 Subject: Add LPC11xx ext,i2c and pwm drivers + LPC11C24 Board Xpresso board git-svn-id: svn://svn.code.sf.net/p/chibios/svn/trunk@5799 35acf78f-673a-0410-8e92-d51de3d6d3f4 --- os/hal/platforms/LPC11xx/ext_lld.c | 259 +++++++++++++++++++ os/hal/platforms/LPC11xx/ext_lld.h | 185 ++++++++++++++ os/hal/platforms/LPC11xx/ext_lld_isr.c | 176 +++++++++++++ os/hal/platforms/LPC11xx/ext_lld_isr.h | 111 +++++++++ os/hal/platforms/LPC11xx/i2c_lld.c | 438 +++++++++++++++++++++++++++++++++ os/hal/platforms/LPC11xx/i2c_lld.h | 225 +++++++++++++++++ os/hal/platforms/LPC11xx/platform.mk | 6 +- os/hal/platforms/LPC11xx/pwm_lld.c | 407 ++++++++++++++++++++++++++++++ os/hal/platforms/LPC11xx/pwm_lld.h | 364 +++++++++++++++++++++++++++ 9 files changed, 2170 insertions(+), 1 deletion(-) create mode 100644 os/hal/platforms/LPC11xx/ext_lld.c create mode 100644 os/hal/platforms/LPC11xx/ext_lld.h create mode 100644 os/hal/platforms/LPC11xx/ext_lld_isr.c create mode 100644 os/hal/platforms/LPC11xx/ext_lld_isr.h create mode 100644 os/hal/platforms/LPC11xx/i2c_lld.c create mode 100644 os/hal/platforms/LPC11xx/i2c_lld.h create mode 100644 os/hal/platforms/LPC11xx/pwm_lld.c create mode 100644 os/hal/platforms/LPC11xx/pwm_lld.h (limited to 'os/hal') diff --git a/os/hal/platforms/LPC11xx/ext_lld.c b/os/hal/platforms/LPC11xx/ext_lld.c new file mode 100644 index 000000000..c22cd83be --- /dev/null +++ b/os/hal/platforms/LPC11xx/ext_lld.c @@ -0,0 +1,259 @@ +/* + ChibiOS/RT - Copyright (C) 2006-2013 Giovanni Di Sirio + LPC11xx EXT driver - Copyright (C) 2013 Marcin Jokel + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file LPC11xx/ext_lld.c + * @brief LPC11xx EXT subsystem low level driver source. + * + * @addtogroup EXT + * @{ + */ + +#include "ch.h" +#include "hal.h" + +#if HAL_USE_EXT || defined(__DOXYGEN__) + +#include "ext_lld_isr.h" + +/*===========================================================================*/ +/* Driver local definitions. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver exported variables. */ +/*===========================================================================*/ + +/** + * @brief EXTD0 driver identifier. + */ +#if LPC11xx_EXT_USE_EXT0 || defined(__DOXYGEN__) +EXTDriver EXTD0; +#endif + +/** + * @brief EXTD1 driver identifier. + */ +#if LPC11xx_EXT_USE_EXT1 || defined(__DOXYGEN__) +EXTDriver EXTD1; +#endif + +/** + * @brief EXTD2 driver identifier. + */ +#if LPC11xx_EXT_USE_EXT2 || defined(__DOXYGEN__) +EXTDriver EXTD2; +#endif + +/** + * @brief EXTD3 driver identifier. + */ +#if LPC11xx_EXT_USE_EXT3 || defined(__DOXYGEN__) +EXTDriver EXTD3; +#endif + +/*===========================================================================*/ +/* Driver local variables and types. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver local functions. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver interrupt handlers. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +/** + * @brief Low level EXT driver initialization. + * + * @notapi + */ +void ext_lld_init(void) { + + /* Driver initialization.*/ +#if LPC11xx_EXT_USE_EXT0 + extObjectInit(&EXTD0); + EXTD0.gpio = LPC_GPIO0; +#endif + +#if LPC11xx_EXT_USE_EXT1 + extObjectInit(&EXTD1); + EXTD1.gpio = LPC_GPIO1; +#endif + +#if LPC11xx_EXT_USE_EXT2 + extObjectInit(&EXTD2); + EXTD2.gpio = LPC_GPIO2; +#endif + +#if LPC11xx_EXT_USE_EXT3 + extObjectInit(&EXTD3); + EXTD3.gpio = LPC_GPIO3; +#endif +} + +/** + * @brief Configures and activates the EXT peripheral. + * + * @param[in] extp pointer to the @p EXTDriver object + * + * @notapi + */ +void ext_lld_start(EXTDriver *extp) { + int i; + + /* Configure all pins as edge sensitive */ +#if LPC11xx_EXT_USE_EXT0 + if (extp == &EXTD0) { + LPC_GPIO0->IS = 0; + ext_lld_exti_irq_enable(EXTI0_IRQ); + } +#endif + +#if LPC11xx_EXT_USE_EXT1 + if (extp == &EXTD1) { + LPC_GPIO1->IS = 0; + ext_lld_exti_irq_enable(EXTI1_IRQ); + } +#endif + +#if LPC11xx_EXT_USE_EXT2 + if (extp == &EXTD2) { + LPC_GPIO2->IS = 0; + ext_lld_exti_irq_enable(EXTI2_IRQ); + } +#endif + +#if LPC11xx_EXT_USE_EXT3 + if (extp == &EXTD3) { + LPC_GPIO3->IS = 0; + ext_lld_exti_irq_enable(EXTI3_IRQ); + } +#endif + + /* Configuration of autostart channels.*/ + for (i = 0; i < EXT_MAX_CHANNELS; i++) + if (extp->config->channels[i].mode & EXT_CH_MODE_AUTOSTART) + ext_lld_channel_enable(extp, i); + else + ext_lld_channel_disable(extp, i); +} + +/** + * @brief Deactivates the EXT peripheral. + * + * @param[in] extp pointer to the @p EXTDriver object + * + * @notapi + */ +void ext_lld_stop(EXTDriver *extp) { + + LPC_GPIO_TypeDef * gp = extp->gpio; + + if (extp->state == EXT_ACTIVE) { +#if LPC11xx_EXT_USE_EXT0 + if (extp == &EXTD0) { + ext_lld_exti_irq_disable(EXTI0_IRQ); + } +#endif + +#if LPC11xx_EXT_USE_EXT1 + if (extp == &EXTD1) { + ext_lld_exti_irq_disable(EXTI1_IRQ); + } +#endif + +#if LPC11xx_EXT_USE_EXT2 + if (extp == &EXTD2) { + ext_lld_exti_irq_disable(EXTI2_IRQ); + } +#endif + +#if LPC11xx_EXT_USE_EXT3 + if (extp == &EXTD3) { + ext_lld_exti_irq_disable(EXTI3_IRQ); + } +#endif + } + + gp->IE = 0; + gp->IC = 0xFFFFFFFF; + __NOP(); + __NOP(); +} + +/** + * @brief Enables an EXT channel. + * + * @param[in] extp pointer to the @p EXTDriver object + * @param[in] channel channel to be enabled + * + * @notapi + */ +void ext_lld_channel_enable(EXTDriver *extp, expchannel_t channel) { + + LPC_GPIO_TypeDef * gp; + + gp = extp->gpio; + + /* Programming edge irq enables */ + if (extp->config->channels[channel].mode & EXT_CH_MODE_BOTH_EDGES) + gp->IBE |= (1 << channel); + else { + gp->IBE &= ~(1 << channel); + if (extp->config->channels[channel].mode & EXT_CH_MODE_RISING_EDGE) + gp->IEV |= (1 << channel); + else + gp->IEV &= (1 << channel); + } + + gp->IC = (1 << channel); /* Clear interrupt on selected channel */ + __NOP(); + __NOP(); + + gp->IE |= (1 << channel); /* Interrupt on selected channel + is not masked */ +} + +/** + * @brief Disables an EXT channel. + * + * @param[in] extp pointer to the @p EXTDriver object + * @param[in] channel channel to be disabled + * + * @notapi + */ +void ext_lld_channel_disable(EXTDriver *extp, expchannel_t channel) { + + LPC_GPIO_TypeDef * gp; + + gp = extp->gpio; + + gp->IE &= ~(1 << channel); /* Mask interrupt on selected channel */ + gp->IC = (1 << channel); /* Clear interrupt on selected channel */ + __NOP(); + __NOP(); +} + +#endif /* HAL_USE_EXT */ + +/** @} */ diff --git a/os/hal/platforms/LPC11xx/ext_lld.h b/os/hal/platforms/LPC11xx/ext_lld.h new file mode 100644 index 000000000..9a05f6d22 --- /dev/null +++ b/os/hal/platforms/LPC11xx/ext_lld.h @@ -0,0 +1,185 @@ +/* + ChibiOS/RT - Copyright (C) 2006-2013 Giovanni Di Sirio + LPC11xx EXT driver - Copyright (C) 2013 Marcin Jokel + - Copyright (C) 2013 mike brown + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file LPC11xx/ext_lld.h + * @brief LPC11xx EXT subsystem low level driver header. + * + * @addtogroup EXT + * @{ + */ + +#ifndef _EXT_LLD_H_ +#define _EXT_LLD_H_ + +#if HAL_USE_EXT || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver constants. */ +/*===========================================================================*/ + +/** + * @brief Available number of EXT channels. + */ +#define EXT_MAX_CHANNELS 12 + + +/*===========================================================================*/ +/* Driver pre-compile time settings. */ +/*===========================================================================*/ + +/** + * @brief EXT0 driver enable switch. + * @details If set to @p TRUE the support for EXT0 is included. + * @note The default is @p FALSE. + */ +#if !defined(LPC11xx_EXT_USE_EXT0) || defined(__DOXYGEN__) +#define LPC11xx_EXT_USE_EXT0 FALSE +#endif + +/** + * @brief EXT1 driver enable switch. + * @details If set to @p TRUE the support for EXT1 is included. + * @note The default is @p FALSE. + */ +#if !defined(LPC11xx_EXT_USE_EXT1) || defined(__DOXYGEN__) +#define LPC11xx_EXT_USE_EXT1 FALSE +#endif + +/** + * @brief EXT2 driver enable switch. + * @details If set to @p TRUE the support for EXT2 is included. + * @note The default is @p FALSE. + */ +#if !defined(LPC11xx_EXT_USE_EXT2) || defined(__DOXYGEN__) +#define LPC11xx_EXT_USE_EXT2 FALSE +#endif + +/** + * @brief EXT3 driver enable switch. + * @details If set to @p TRUE the support for EXT3 is included. + * @note The default is @p FALSE. + */ +#if !defined(LPC11xx_EXT_USE_EXT3) || defined(__DOXYGEN__) +#define LPC11xx_EXT_USE_EXT3 FALSE +#endif +/*===========================================================================*/ +/* Derived constants and error checks. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver data structures and types. */ +/*===========================================================================*/ + +/** + * @brief EXT channel identifier. + */ +typedef uint32_t expchannel_t; + +/** + * @brief Type of an EXT generic notification callback. + * + * @param[in] extp pointer to the @p EXPDriver object triggering the + * callback + */ +typedef void (*extcallback_t)(EXTDriver *extp, expchannel_t channel); + +/** + * @brief Channel configuration structure. + */ +typedef struct { + /** + * @brief Channel mode. + */ + uint8_t mode; + + /** + * @brief Channel callback. + */ + extcallback_t cb; +} EXTChannelConfig; + +/** + * @brief Driver configuration structure. + * @note It could be empty on some architectures. + */ +typedef struct { + /** + * @brief Channel configurations. + */ + EXTChannelConfig channels[EXT_MAX_CHANNELS]; + /* End of the mandatory fields.*/ +} EXTConfig; + +/** + * @brief Structure representing an EXT driver. + */ +struct EXTDriver { + /** + * @brief Driver state. + */ + extstate_t state; + /** + * @brief Current configuration data. + */ + const EXTConfig *config; + /* End of the mandatory fields.*/ + LPC_GPIO_TypeDef *gpio; +}; + +/*===========================================================================*/ +/* Driver macros. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +#if LPC11xx_EXT_USE_EXT0 || !defined(__DOXYGEN__) +extern EXTDriver EXTD0; +#endif + +#if LPC11xx_EXT_USE_EXT1 || !defined(__DOXYGEN__) +extern EXTDriver EXTD1; +#endif + +#if LPC11xx_EXT_USE_EXT2 || !defined(__DOXYGEN__) +extern EXTDriver EXTD2; +#endif + +#if LPC11xx_EXT_USE_EXT3 || !defined(__DOXYGEN__) +extern EXTDriver EXTD3; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + void ext_lld_init(void); + void ext_lld_start(EXTDriver *extp); + void ext_lld_stop(EXTDriver *extp); + void ext_lld_channel_enable(EXTDriver *extp, expchannel_t channel); + void ext_lld_channel_disable(EXTDriver *extp, expchannel_t channel); +#ifdef __cplusplus +} +#endif + +#endif /* HAL_USE_EXT */ + +#endif /* _EXT_LLD_H_ */ + +/** @} */ diff --git a/os/hal/platforms/LPC11xx/ext_lld_isr.c b/os/hal/platforms/LPC11xx/ext_lld_isr.c new file mode 100644 index 000000000..9d3b92eeb --- /dev/null +++ b/os/hal/platforms/LPC11xx/ext_lld_isr.c @@ -0,0 +1,176 @@ +/* + ChibiOS/RT - Copyright (C) 2006-2013 Giovanni Di Sirio + LPC11xx EXT driver - Copyright (C) 2013 Marcin Jokel + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file LPC11xx/ext_lld_isr.c + * @brief LPC11xx EXT subsystem low level driver ISR code. + * + * @addtogroup EXT + * @{ + */ + +#include "ch.h" +#include "hal.h" + +#if HAL_USE_EXT || defined(__DOXYGEN__) + +#include "ext_lld_isr.h" + +/*===========================================================================*/ +/* Driver local definitions. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver exported variables. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver local variables and types. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver local functions. */ +/*===========================================================================*/ + +#if LPC11xx_EXT_USE_EXT0 || LPC11xx_EXT_USE_EXT1 || LPC11xx_EXT_USE_EXT2 || \ + LPC11xx_EXT_USE_EXT3 || defined(__DOXYGEN__) +/** + * @brief I2C error handler. + * + * @param[in] i2cp pointer to the @p I2CDriver object + * + * @notapi + */ +static void ext_lld_serve_interrupt(EXTDriver *extp) { + uint32_t port_stat; + uint8_t i; + + port_stat = extp->gpio->MIS; /* Read interrupt status */ + extp->gpio->IC = port_stat; /* Clear interrupt flags */ + + for (i = 0; i < EXT_MAX_CHANNELS; i++) { + if (port_stat & 0x01) { + extp->config->channels[i].cb(extp, i); + } + port_stat = port_stat >> 1; + } +} +#endif +/*===========================================================================*/ +/* Driver interrupt handlers. */ +/*===========================================================================*/ + +/** + * @brief PIO0 interrupt handler. + * + * @isr + */ +#if LPC11xx_EXT_USE_EXT0 || defined(__DOXYGEN__) +CH_IRQ_HANDLER(VectorBC) { + + CH_IRQ_PROLOGUE(); + ext_lld_serve_interrupt(&EXTD0); + CH_IRQ_EPILOGUE(); +} +#endif + +#if LPC11xx_EXT_USE_EXT1 || defined(__DOXYGEN__) +/** + * @brief PIO1 interrupt handler. + * + * @isr + */ +CH_IRQ_HANDLER(VectorB8) { + + CH_IRQ_PROLOGUE(); + ext_lld_serve_interrupt(&EXTD1); + CH_IRQ_EPILOGUE(); +} +#endif + +#if LPC11xx_EXT_USE_EXT2 || defined(__DOXYGEN__) +/** + * @brief PIO2 interrupt handler. + * + * @isr + */ +CH_IRQ_HANDLER(VectorB4) { + + CH_IRQ_PROLOGUE(); + ext_lld_serve_interrupt(&EXTD2); + CH_IRQ_EPILOGUE(); +} +#endif + +#if LPC11xx_EXT_USE_EXT3 || defined(__DOXYGEN__) +/** + * @brief PIO_3 interrupt handler. + * + * @isr + */ +CH_IRQ_HANDLER(VectorB0) { + + CH_IRQ_PROLOGUE(); + ext_lld_serve_interrupt(&EXTD3); + CH_IRQ_EPILOGUE(); +} +#endif + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +/** + * @brief Enables EXTI IRQ sources. + * + * @notapi + */ +void ext_lld_exti_irq_enable(extirq_t irqn) { + + uint32_t pmask; + + switch (irqn) { + case EXTI0_IRQ: + pmask = LPC11xx_EXT_EXTI0_IRQ_PRIORITY; + break; + case EXTI1_IRQ: + pmask = LPC11xx_EXT_EXTI1_IRQ_PRIORITY; + break; + case EXTI2_IRQ: + pmask = LPC11xx_EXT_EXTI2_IRQ_PRIORITY; + break; + case EXTI3_IRQ: + pmask = LPC11xx_EXT_EXTI3_IRQ_PRIORITY; + break; + } + nvicEnableVector(EINT0_IRQn - irqn, CORTEX_PRIORITY_MASK(pmask)); + +} + +/** + * @brief Disables EXTI IRQ sources. + * + * @notapi + */ +void ext_lld_exti_irq_disable(extirq_t irqn) { + + nvicDisableVector(EINT0_IRQn - irqn); +} + +#endif /* HAL_USE_EXT */ + +/** @} */ diff --git a/os/hal/platforms/LPC11xx/ext_lld_isr.h b/os/hal/platforms/LPC11xx/ext_lld_isr.h new file mode 100644 index 000000000..442d109da --- /dev/null +++ b/os/hal/platforms/LPC11xx/ext_lld_isr.h @@ -0,0 +1,111 @@ +/* + ChibiOS/RT - Copyright (C) 2006-2013 Giovanni Di Sirio + LPC11xx EXT driver - Copyright (C) 2013 Marcin Jokel + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file LPC11xx/ext_lld_isr.h + * @brief LPC11xx EXT subsystem low level driver ISR header. + * + * @addtogroup EXT + * @{ + */ + +#ifndef _EXT_LLD_ISR_H_ +#define _EXT_LLD_ISR_H_ + +#if HAL_USE_EXT || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver constants. */ +/*===========================================================================*/ + +#define EXTI0_IRQ 0 +#define EXTI1_IRQ 1 +#define EXTI2_IRQ 2 +#define EXTI3_IRQ 3 + +/*===========================================================================*/ +/* Driver pre-compile time settings. */ +/*===========================================================================*/ + +/** + * @name Configuration options + * @{ + */ +/** + * @brief EXTI0 interrupt priority level setting. + */ +#if !defined(LPC11xx_EXT_EXTI0_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define LPC11xx_EXT_EXTI0_IRQ_PRIORITY 3 +#endif + +/** + * @brief EXTI1 interrupt priority level setting. + */ +#if !defined(LPC11xx_EXT_EXTI1_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define LPC11xx_EXT_EXTI1_IRQ_PRIORITY 3 +#endif + +/** + * @brief EXTI2 interrupt priority level setting. + */ +#if !defined(LPC11xx_EXT_EXTI2_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define LPC11xx_EXT_EXTI2_IRQ_PRIORITY 3 +#endif + +/** + * @brief EXTI3 interrupt priority level setting. + */ +#if !defined(LPC11xx_EXT_EXTI3_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define LPC11xx_EXT_EXTI3_IRQ_PRIORITY 3 +#endif + +/** @} */ + +/*===========================================================================*/ +/* Derived constants and error checks. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver data structures and types. */ +/*===========================================================================*/ + +/** + * @brief EXT irq port identifier. + */ +typedef uint32_t extirq_t; +/*===========================================================================*/ +/* Driver macros. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +#ifdef __cplusplus +extern "C" { +#endif + void ext_lld_exti_irq_enable(extirq_t irqn); + void ext_lld_exti_irq_disable(extirq_t irqn); +#ifdef __cplusplus +} +#endif + +#endif /* HAL_USE_EXT */ + +#endif /* _EXT_LLD_ISR_H_ */ + +/** @} */ diff --git a/os/hal/platforms/LPC11xx/i2c_lld.c b/os/hal/platforms/LPC11xx/i2c_lld.c new file mode 100644 index 000000000..c68f11460 --- /dev/null +++ b/os/hal/platforms/LPC11xx/i2c_lld.c @@ -0,0 +1,438 @@ +/* + ChibiOS/RT - Copyright (C) 2006-2013 Giovanni Di Sirio + LPC11xx I2C driver - Copyright (C) 2013 Marcin Jokel + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/* + Concepts and parts of this file have been contributed by Uladzimir Pylinsky + aka barthess. + */ + + +/** + * @file LPC11xx/i2c_lld.h + * @brief LPC11xx I2C subsystem low level driver header. + * + * @addtogroup I2C + * @{ + */ + +#include "ch.h" +#include "hal.h" + +#if HAL_USE_I2C || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver local definitions. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver constants. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver exported variables. */ +/*===========================================================================*/ + +/** @brief I2C1 driver identifier.*/ +I2CDriver I2CD1; + +/*===========================================================================*/ +/* Driver local variables. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver local functions. */ +/*===========================================================================*/ + +/** + * @brief Wakes up the waiting thread. + * + * @param[in] i2cp pointer to the @p I2CDriver object + * @param[in] msg wakeup message + * + * @notapi + */ +#define wakeup_isr(i2cp, msg) { \ + chSysLockFromIsr(); \ + if ((i2cp)->thread != NULL) { \ + Thread *tp = (i2cp)->thread; \ + (i2cp)->thread = NULL; \ + tp->p_u.rdymsg = (msg); \ + chSchReadyI(tp); \ + } \ + chSysUnlockFromIsr(); \ +} + +/** + * @brief Handling of stalled I2C transactions. + * + * @param[in] i2cp pointer to the @p I2CDriver object + * + * @notapi + */ +static void i2c_lld_safety_timeout(void *p) { + I2CDriver *i2cp = (I2CDriver *)p; + + chSysLockFromIsr(); + if (i2cp->thread) { + Thread *tp = i2cp->thread; + i2cp->thread = NULL; + tp->p_u.rdymsg = RDY_TIMEOUT; + chSchReadyI(tp); + } + chSysUnlockFromIsr(); +} + +/** + * @brief I2C error handler. + * + * @param[in] i2cp pointer to the @p I2CDriver object + * + * @notapi + */ +static void i2c_lld_serve_error_interrupt(I2CDriver *i2cp, uint32_t status) { + i2cflags_t error = 0; + + switch (status) { + case I2C_STATE_ARB_LOST: + error = I2CD_ARBITRATION_LOST; + break; + case I2C_STATE_BUS_ERROR: + error = I2CD_BUS_ERROR; + break; + case I2C_STATE_MS_SLAR_NACK: + case I2C_STATE_MS_TDAT_NACK: + case I2C_STATE_MS_SLAW_NACK: + error = I2CD_ACK_FAILURE ; + break; + } + + /* If some error has been identified then sends wakes the waiting thread.*/ + i2cp->errors = error; + wakeup_isr(i2cp, RDY_RESET); +} + +/*===========================================================================*/ +/* Driver interrupt handlers. */ +/*===========================================================================*/ +/** + * @brief I2C event interrupt handler. + * + * @notapi + */ +CH_IRQ_HANDLER(Vector7C) { + uint32_t status; + + CH_IRQ_PROLOGUE(); + status = LPC_I2C->STAT; + switch(status) { + case I2C_STATE_MS_START: /* A START condition has been transmitted. */ + if (I2CD1.txbytes > 0) { + LPC_I2C->DAT = I2CD1.addr; /* Write slave address with WR bit. */ + } + else { + LPC_I2C->DAT = I2CD1.addr | I2C_RD_BIT; /* Write slave address with RD bit. */ + } + + LPC_I2C->CONCLR = I2C_CONCLR_STAC | I2C_CONCLR_SIC; /* Clear START and SI bit. */ + break; + + case I2C_STATE_MS_SLAR_NACK: /* NOT ACK has been received, Master will be transmitted STOP. */ + case I2C_STATE_MS_TDAT_NACK: /* NOT ACK has been received, Master will be transmitted STOP. */ + case I2C_STATE_MS_SLAW_NACK: /* NOT ACK has been received, Master will be transmitted STOP. */ + LPC_I2C->CONSET = I2C_CONSET_STO; /* Set STOP bit. */ + LPC_I2C->CONCLR = I2C_CONCLR_SIC; /* Clear SI bit. */ + i2c_lld_serve_error_interrupt(&I2CD1, status); + break; + + case I2C_STATE_MS_SLAW_ACK: /* SLA + W has been transmitted, ACK has been received. */ + case I2C_STATE_MS_TDAT_ACK: /* Data byte has been transmitted, ACK has been received. */ + if (I2CD1.txbytes > 0) { + LPC_I2C->DAT = *I2CD1.txbuf++; /* Write data. */ + I2CD1.txbytes--; + } + else { + if (I2CD1.rxbytes > 0) { + LPC_I2C->CONSET = I2C_CONSET_STO | I2C_CONSET_STA; /* Set START and STOP bit. */ + } /* STOP bit will be transmit, then START bit. */ + else { + LPC_I2C->CONSET = I2C_CONSET_STO; /* Set STOP bit. */ + wakeup_isr(&I2CD1, RDY_OK); + } + } + LPC_I2C->CONCLR = I2C_CONCLR_SIC; /* Clear SI bit. */ + break; + + case I2C_STATE_MS_SLAR_ACK: /* SLA + R has been transmitted, ACK has been received. */ + case I2C_STATE_MS_RDAT_ACK: /* Data byte has been received, ACK has been returned. */ + if (status == I2C_STATE_MS_RDAT_ACK) { + *I2CD1.rxbuf++ = LPC_I2C->DAT; /* Read data */ + I2CD1.rxbytes--; + } + if (I2CD1.rxbytes == 1) { + LPC_I2C->CONCLR = I2C_CONCLR_SIC | I2C_CONCLR_AAC; /* Clear SI and ACK bit. */ + } + else { + LPC_I2C->CONSET = I2C_CONSET_AA; /* Set ACK bit. */ + LPC_I2C->CONCLR = I2C_CONCLR_SIC; /* Clear SI bit. */ + } + break; + + case I2C_STATE_MS_RDAT_NACK: /* Data byte has been received, NOT ACK has been returned. */ + *I2CD1.rxbuf++ = LPC_I2C->DAT; /* Read data. */ + I2CD1.rxbytes--; + LPC_I2C->CONSET = I2C_CONSET_STO; /* Set STOP bit. */ + LPC_I2C->CONCLR = I2C_CONCLR_SIC; /* Clear SI bit. */ + wakeup_isr(&I2CD1, RDY_OK); + break; + + case I2C_STATE_BUS_ERROR: /* Bus error. */ + case I2C_STATE_ARB_LOST: /* Arbitration lost. */ + LPC_I2C->CONCLR = I2C_CONCLR_SIC; /* Clear SI bit. */ + i2c_lld_serve_error_interrupt(&I2CD1, status); + break; + + } + + CH_IRQ_EPILOGUE(); +} + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +/** + * @brief Low level I2C driver initialization. + * + * @notapi + */ +void i2c_lld_init(void) { + + i2cObjectInit(&I2CD1); + I2CD1.thread = NULL; + I2CD1.i2c = LPC_I2C; + + LPC_IOCON->PIO0_4 = 0x01; /* Set I2C SCL pin function */ + LPC_IOCON->PIO0_5 = 0x01; /* Set I2C SDA pin function */ +} + +/** + * @brief Configures and activates the I2C peripheral. + * + * @param[in] i2cp pointer to the @p I2CDriver object + * + * @notapi + */ +void i2c_lld_start(I2CDriver *i2cp) { + uint32_t i2cscl; + uint32_t mulh, mull, div; + LPC_I2C_TypeDef *dp = i2cp->i2c; + + /* Make sure I2C peripheral is disabled */ + dp->CONCLR = I2C_CONCLR_ENC; + + /* If in stopped state then enables the I2C clock. */ + if (i2cp->state == I2C_STOP) { + + if (&I2CD1 == i2cp) { + LPC_SYSCON->SYSAHBCLKCTRL |= (1 << 5); /* Enable clock. */ + LPC_SYSCON->PRESETCTRL &= ~(1 << 1); /* Reset I2C peripheral.*/ + __NOP(); + LPC_SYSCON->PRESETCTRL |= (1 << 1); + nvicEnableVector(I2C_IRQn, + CORTEX_PRIORITY_MASK(LPC11xx_I2C_IRQ_PRIORITY)); + } + + } + + /* Setup I2C clock parameters.*/ + i2cscl = (LPC11xx_SYSCLK/(i2cp->config->clock_timing)); + if (i2cp->config->mode == I2C_FAST_MODE) { + div = 19; + mull = 13; + mulh = 6; + } else if (i2cp->config->mode == I2C_FAST_MODE_PLUS) { + div = 3; + mull = 2; + mulh = 1; + } else { /* i2cp->config->mode == I2C_STANDARD_MODE */ + div = 2; + mull = 1; + mulh = 1; + } + + dp->SCLH = (mulh * i2cscl) / div; + dp->SCLL = (mull * i2cscl) / div; + + /* Enable I2C.*/ + dp->CONSET |= I2C_CONSET_EN; +} + +/** + * @brief Deactivates the I2C peripheral. + * + * @param[in] i2cp pointer to the @p I2CDriver object + * + * @notapi + */ +void i2c_lld_stop(I2CDriver *i2cp) { + + /* If not in stopped state then disables the I2C clock.*/ + if (i2cp->state != I2C_STOP) { + + /* I2C disable.*/ + i2cp->i2c->CONCLR = I2C_CONCLR_ENC; + + if (&I2CD1 == i2cp) { + nvicDisableVector(I2C_IRQn); + LPC_SYSCON->SYSAHBCLKCTRL &= ~(1 << 5); + } + } +} + +/** + * @brief Receives data via the I2C bus as master. + * @details Number of receiving bytes must be more than 1 on STM32F1x. This is + * hardware restriction. + * + * @param[in] i2cp pointer to the @p I2CDriver object + * @param[in] addr slave device address + * @param[out] rxbuf pointer to the receive buffer + * @param[in] rxbytes number of bytes to be received + * @param[in] timeout the number of ticks before the operation timeouts, + * the following special values are allowed: + * - @a TIME_INFINITE no timeout. + * . + * @return The operation status. + * @retval RDY_OK if the function succeeded. + * @retval RDY_RESET if one or more I2C errors occurred, the errors can + * be retrieved using @p i2cGetErrors(). + * @retval RDY_TIMEOUT if a timeout occurred before operation end. After a + * timeout the driver must be stopped and restarted + * because the bus is in an uncertain state. + * + * @notapi + */ +msg_t i2c_lld_master_receive_timeout(I2CDriver *i2cp, i2caddr_t addr, + uint8_t *rxbuf, size_t rxbytes, + systime_t timeout) { + LPC_I2C_TypeDef *dp = i2cp->i2c; + VirtualTimer vt; + + i2cp->addr = addr << 1; + /* Global timeout for the whole operation.*/ + if (timeout != TIME_INFINITE) + chVTSetI(&vt, timeout, i2c_lld_safety_timeout, (void *)i2cp); + + /* Releases the lock from high level driver.*/ + chSysUnlock(); + + /* Initializes driver fields */ + i2cp->errors = 0; + i2cp->rxbuf = rxbuf; + i2cp->rxbytes = rxbytes; + + /* This lock will be released in high level driver.*/ + chSysLock(); + + /* Atomic check on the timer in order to make sure that a timeout didn't + happen outside the critical zone.*/ + if ((timeout != TIME_INFINITE) && !chVTIsArmedI(&vt)) + return RDY_TIMEOUT; + + /* Starts the operation.*/ + dp->CONSET = I2C_CONSET_STA; + + /* Waits for the operation completion or a timeout.*/ + i2cp->thread = chThdSelf(); + chSchGoSleepS(THD_STATE_SUSPENDED); + if ((timeout != TIME_INFINITE) && chVTIsArmedI(&vt)) + chVTResetI(&vt); + + return chThdSelf()->p_u.rdymsg; +} + +/** + * @brief Transmits data via the I2C bus as master. + * @details Number of receiving bytes must be 0 or more than 1 on STM32F1x. + * This is hardware restriction. + * + * @param[in] i2cp pointer to the @p I2CDriver object + * @param[in] addr slave device address + * @param[in] txbuf pointer to the transmit buffer + * @param[in] txbytes number of bytes to be transmitted + * @param[out] rxbuf pointer to the receive buffer + * @param[in] rxbytes number of bytes to be received + * @param[in] timeout the number of ticks before the operation timeouts, + * the following special values are allowed: + * - @a TIME_INFINITE no timeout. + * . + * @return The operation status. + * @retval RDY_OK if the function succeeded. + * @retval RDY_RESET if one or more I2C errors occurred, the errors can + * be retrieved using @p i2cGetErrors(). + * @retval RDY_TIMEOUT if a timeout occurred before operation end. After a + * timeout the driver must be stopped and restarted + * because the bus is in an uncertain state. + * + * @notapi + */ +msg_t i2c_lld_master_transmit_timeout(I2CDriver *i2cp, i2caddr_t addr, + const uint8_t *txbuf, size_t txbytes, + uint8_t *rxbuf, size_t rxbytes, + systime_t timeout) { + LPC_I2C_TypeDef *dp = i2cp->i2c; + VirtualTimer vt; + + i2cp->addr = addr << 1; + /* Global timeout for the whole operation.*/ + if (timeout != TIME_INFINITE) + chVTSetI(&vt, timeout, i2c_lld_safety_timeout, (void *)i2cp); + + /* Releases the lock from high level driver.*/ + chSysUnlock(); + + /* Initializes driver fields */ + i2cp->errors = 0; + i2cp->txbuf = txbuf; + i2cp->txbytes = txbytes; + i2cp->rxbuf = rxbuf; + i2cp->rxbytes = rxbytes; + + /* This lock will be released in high level driver.*/ + chSysLock(); + + /* Atomic check on the timer in order to make sure that a timeout didn't + happen outside the critical zone.*/ + if ((timeout != TIME_INFINITE) && !chVTIsArmedI(&vt)) + return RDY_TIMEOUT; + + /* Starts the operation.*/ + dp->CONSET = I2C_CONSET_STA; + + /* Waits for the operation completion or a timeout.*/ + i2cp->thread = chThdSelf(); + chSchGoSleepS(THD_STATE_SUSPENDED); + + if ((timeout != TIME_INFINITE) && chVTIsArmedI(&vt)) + chVTResetI(&vt); + + return chThdSelf()->p_u.rdymsg; +} + +#endif /* HAL_USE_I2C */ + +/** @} */ diff --git a/os/hal/platforms/LPC11xx/i2c_lld.h b/os/hal/platforms/LPC11xx/i2c_lld.h new file mode 100644 index 000000000..b908ba6e4 --- /dev/null +++ b/os/hal/platforms/LPC11xx/i2c_lld.h @@ -0,0 +1,225 @@ +/* + ChibiOS/RT - Copyright (C) 2006-2013 Giovanni Di Sirio + LPC11xx I2C driver - Copyright (C) 2013 Marcin Jokel + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +/* + Concepts and parts of this file have been contributed by Uladzimir Pylinsky + aka barthess. + */ + +/** + * @file LPC11xx/i2c_lld.h + * @brief LPC11xx I2C subsystem low level driver header. + * + * @addtogroup I2C + * @{ + */ + +#ifndef _I2C_LLD_H_ +#define _I2C_LLD_H_ + +#if HAL_USE_I2C || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver constants. */ +/*===========================================================================*/ + +#define I2C_CONSET_AA 0x04 /* Assert acknowledge flag. */ +#define I2C_CONSET_SI 0x08 /* I2C interrupt flag. */ +#define I2C_CONSET_STO 0x10 /* STOP flag. */ +#define I2C_CONSET_STA 0x20 /* START flag. */ +#define I2C_CONSET_EN 0x40 /* I2C interface enable. */ + +#define I2C_CONCLR_AAC 0x04 /* Assert acknowledge Clear bit. */ +#define I2C_CONCLR_SIC 0x08 /* I2C interrupt Clear bit. */ +#define I2C_CONCLR_STAC 0x20 /* START flag Clear bit. */ +#define I2C_CONCLR_ENC 0x40 /* I2C interface Disable bit. */ + +#define I2C_WR_BIT 0x00 +#define I2C_RD_BIT 0x01 + +#define I2C_STATE_MS_START 0x08 +#define I2C_STATE_MS_RSTART 0x10 +#define I2C_STATE_MS_SLAW_ACK 0x18 +#define I2C_STATE_MS_SLAW_NACK 0x20 +#define I2C_STATE_MS_TDAT_ACK 0x28 +#define I2C_STATE_MS_TDAT_NACK 0x30 +#define I2C_STATE_ARB_LOST 0x38 + +#define I2C_STATE_MS_SLAR_ACK 0x40 +#define I2C_STATE_MS_SLAR_NACK 0x48 +#define I2C_STATE_MS_RDAT_ACK 0x50 +#define I2C_STATE_MS_RDAT_NACK 0x58 + +#define I2C_STATE_BUS_ERROR 0x00 + +/*===========================================================================*/ +/* Driver pre-compile time settings. */ +/*===========================================================================*/ +/** + * @name Configuration options + * @{ + */ + +/** + * @brief I2C1 interrupt priority level setting. + */ +#if !defined(LPC11xx_I2C_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define LPC11xx_I2C_IRQ_PRIORITY 3 +#endif + +/** @} */ + +/*===========================================================================*/ +/* Derived constants and error checks. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver data structures and types. */ +/*===========================================================================*/ + +/** + * @brief Type representing I2C address. + */ +typedef uint16_t i2caddr_t; + +/** + * @brief I2C Driver condition flags type. + */ +typedef uint32_t i2cflags_t; +/** + * @brief Supported modes for the I2C bus. + */ +typedef enum { + I2C_STANDARD_MODE = 1, + I2C_FAST_MODE = 2, + I2C_FAST_MODE_PLUS = 3, +} i2cmode_t; + +/** + * @brief Driver configuration structure. + */ +typedef struct { + i2cmode_t mode; /**< @brief Specifies the I2C mode. */ + uint32_t clock_timing; /**< @brief Specifies the clock timing */ +} I2CConfig; + +/** + * @brief Type of a structure representing an I2C driver. + */ +typedef struct I2CDriver I2CDriver; + +/** + * @brief Structure representing an I2C driver. + */ +struct I2CDriver { + /** + * @brief Driver state. + */ + i2cstate_t state; + /** + * @brief Current configuration data. + */ + const I2CConfig *config; + /** + * @brief Error flags. + */ + i2cflags_t errors; +#if I2C_USE_MUTUAL_EXCLUSION || defined(__DOXYGEN__) +#if CH_USE_MUTEXES || defined(__DOXYGEN__) + /** + * @brief Mutex protecting the bus. + */ + Mutex mutex; +#elif CH_USE_SEMAPHORES + Semaphore semaphore; +#endif +#endif /* I2C_USE_MUTUAL_EXCLUSION */ +#if defined(I2C_DRIVER_EXT_FIELDS) + I2C_DRIVER_EXT_FIELDS +#endif + /* End of the mandatory fields.*/ + /** + * @brief Thread waiting for I/O completion. + */ + Thread *thread; + /** + * @brief Current slave address without R/W bit. + */ + i2caddr_t addr; + /** + * @brief Pointer to the transmit buffer. + */ + const uint8_t *txbuf; + /** + * @brief Number of bytes to transmit. + */ + size_t txbytes; + /** + * @brief Pointer to the receive buffer. + */ + uint8_t *rxbuf; + /** + * @brief Number of bytes to receive. + */ + size_t rxbytes; + /** + * @brief Pointer to the I2C registers block. + */ + LPC_I2C_TypeDef *i2c; +}; + +/*===========================================================================*/ +/* Driver macros. */ +/*===========================================================================*/ + +/** + * @brief Get errors from I2C driver. + * + * @param[in] i2cp pointer to the @p I2CDriver object + * + * @notapi + */ +#define i2c_lld_get_errors(i2cp) ((i2cp)->errors) + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +#if !defined(__DOXYGEN__) +extern I2CDriver I2CD1; +#endif + +#endif /* !defined(__DOXYGEN__) */ + +#ifdef __cplusplus +extern "C" { +#endif + void i2c_lld_init(void); + void i2c_lld_start(I2CDriver *i2cp); + void i2c_lld_stop(I2CDriver *i2cp); + msg_t i2c_lld_master_transmit_timeout(I2CDriver *i2cp, i2caddr_t addr, + const uint8_t *txbuf, size_t txbytes, + uint8_t *rxbuf, size_t rxbytes, + systime_t timeout); + msg_t i2c_lld_master_receive_timeout(I2CDriver *i2cp, i2caddr_t addr, + uint8_t *rxbuf, size_t rxbytes, + systime_t timeout); +#ifdef __cplusplus +} +#endif +#endif /* _I2C_LLD_H_ */ + +/** @} */ diff --git a/os/hal/platforms/LPC11xx/platform.mk b/os/hal/platforms/LPC11xx/platform.mk index 4f0d19547..61f2a49dd 100644 --- a/os/hal/platforms/LPC11xx/platform.mk +++ b/os/hal/platforms/LPC11xx/platform.mk @@ -1,8 +1,12 @@ # List of all the LPC11xx platform files. PLATFORMSRC = ${CHIBIOS}/os/hal/platforms/LPC11xx/hal_lld.c \ - ${CHIBIOS}/os/hal/platforms/LPC11xx/gpt_lld.c \ ${CHIBIOS}/os/hal/platforms/LPC11xx/pal_lld.c \ + ${CHIBIOS}/os/hal/platforms/LPC11xx/ext_lld.c \ + ${CHIBIOS}/os/hal/platforms/LPC11xx/ext_lld_isr.c \ + ${CHIBIOS}/os/hal/platforms/LPC11xx/gpt_lld.c \ + ${CHIBIOS}/os/hal/platforms/LPC11xx/pwm_lld.c \ ${CHIBIOS}/os/hal/platforms/LPC11xx/serial_lld.c \ + ${CHIBIOS}/os/hal/platforms/LPC11xx/i2c_lld.c \ ${CHIBIOS}/os/hal/platforms/LPC11xx/spi_lld.c # Required include directories diff --git a/os/hal/platforms/LPC11xx/pwm_lld.c b/os/hal/platforms/LPC11xx/pwm_lld.c new file mode 100644 index 000000000..06de0bbc6 --- /dev/null +++ b/os/hal/platforms/LPC11xx/pwm_lld.c @@ -0,0 +1,407 @@ +/* + ChibiOS/RT - Copyright (C) 2006-2013 Giovanni Di Sirio + LPC11xx PWM driver - Copyright (C) 2013 Marcin Jokel + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file LPC11xx/pwm_lld.c + * @brief LPC11xx PWM subsystem low level driver header. + * + * @addtogroup PWM + * @{ + */ + +#include "ch.h" +#include "hal.h" + +#if HAL_USE_PWM || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver local definitions. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver exported variables. */ +/*===========================================================================*/ + +/** + * @brief PWMD1 driver identifier. + * @note The driver PWMD1 allocates the complex timer TIM1 when enabled. + */ +#if LPC11xx_PWM_USE_CT16B0 || defined(__DOXYGEN__) +PWMDriver PWMD1; +#endif + +/** + * @brief PWMD2 driver identifier. + * @note The driver PWMD2 allocates the timer TIM2 when enabled. + */ +#if LPC11xx_PWM_USE_CT16B1 || defined(__DOXYGEN__) +PWMDriver PWMD2; +#endif + +/** + * @brief PWMD3 driver identifier. + * @note The driver PWMD3 allocates the timer TIM3 when enabled. + */ +#if LPC11xx_PWM_USE_CT32B0 || defined(__DOXYGEN__) +PWMDriver PWMD3; +#endif + +/** + * @brief PWMD4 driver identifier. + * @note The driver PWMD4 allocates the timer TIM4 when enabled. + */ +#if LPC11xx_PWM_USE_CT32B1 || defined(__DOXYGEN__) +PWMDriver PWMD4; +#endif + +/*===========================================================================*/ +/* Driver local variables and types. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver local functions. */ +/*===========================================================================*/ + +#if LPC11xx_PWM_USE_CT16B0 || LPC11xx_PWM_USE_CT16B1 || LPC11xx_PWM_USE_CT32B0 || \ + LPC11xx_PWM_USE_CT32B1 || defined(__DOXYGEN__) +/** + * @brief Common TIM2...TIM5 IRQ handler. + * @note It is assumed that the various sources are only activated if the + * associated callback pointer is not equal to @p NULL in order to not + * perform an extra check in a potentially critical interrupt handler. + * + * @param[in] pwmp pointer to a @p PWMDriver object + */ +static void pwm_lld_serve_interrupt(PWMDriver *pwmp) { + uint16_t sr; + + sr = pwmp->tim->IR; + pwmp->tim->IR = sr; + if ((sr & IR_MR0INT) != 0) + pwmp->config->channels[0].callback(pwmp); + if ((sr & IR_MR1INT) != 0) + pwmp->config->channels[1].callback(pwmp); + if ((sr & IR_MR3INT) != 0) + pwmp->config->callback(pwmp); +} +#endif /* STM32_PWM_USE_TIM2 || ... || STM32_PWM_USE_TIM5 */ + +/*===========================================================================*/ +/* Driver interrupt handlers. */ +/*===========================================================================*/ + +#if LPC11xx_PWM_USE_CT16B0 || defined(__DOXYGEN__) +/** + * @brief CT16B0 interrupt handler. + * + * @isr + */ +CH_IRQ_HANDLER(Vector80) { + + CH_IRQ_PROLOGUE(); + + pwm_lld_serve_interrupt(&PWMD1); + + CH_IRQ_EPILOGUE(); +} + +#endif /* STM32_PWM_USE_TIM1 */ + +#if LPC11xx_PWM_USE_CT16B1 || defined(__DOXYGEN__) +/** + * @brief CT16B1 interrupt handler. + * + * @isr + */ +CH_IRQ_HANDLER(Vector84) { + + CH_IRQ_PROLOGUE(); + + pwm_lld_serve_interrupt(&PWMD2); + + CH_IRQ_EPILOGUE(); +} +#endif /* LPC11xx_PWM_USE_CT16B1 */ + +#if LPC11xx_PWM_USE_CT32B0 || defined(__DOXYGEN__) +/** + * @brief CT32B0 interrupt handler. + * + * @isr + */ +CH_IRQ_HANDLER(Vector88) { + + CH_IRQ_PROLOGUE(); + + pwm_lld_serve_interrupt(&PWMD3); + + CH_IRQ_EPILOGUE(); +} +#endif /* LPC11xx_PWM_USE_CT32B0 */ + +#if LPC11xx_PWM_USE_CT32B1 || defined(__DOXYGEN__) +/** + * @brief CT32B1 interrupt handler. + * + * @isr + */ +CH_IRQ_HANDLER(Vector8C) { + + CH_IRQ_PROLOGUE(); + + pwm_lld_serve_interrupt(&PWMD4); + + CH_IRQ_EPILOGUE(); +} +#endif /* LPC11xx_PWM_USE_CT32B1 */ + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +/** + * @brief Low level PWM driver initialization. + * + * @notapi + */ +void pwm_lld_init(void) { + +#if LPC11xx_PWM_USE_CT16B0 + /* Driver initialization.*/ + pwmObjectInit(&PWMD1); + PWMD1.tim = LPC_TMR16B0; + +#if LPC11xx_PWM_USE_CT16B0_CH0 + LPC_IOCON->PIO0_8 = 0xC2; +#endif + +#if LPC11xx_PWM_USE_CT16B0_CH1 + LPC_IOCON->PIO0_9 = 0xC2; +#endif +#endif + +#if LPC11xx_PWM_USE_CT16B1 + /* Driver initialization.*/ + pwmObjectInit(&PWMD2); + PWMD2.tim = LPC_TMR16B1; + +#if LPC11xx_PWM_USE_CT16B1_CH0 + LPC_IOCON->PIO1_9 = 0xC1; +#endif + +#if LPC11xx_PWM_USE_CT16B1_CH1 + LPC_IOCON->PIO1_10 = 0xC2; +#endif +#endif + +#if LPC11xx_PWM_USE_CT32B0 + /* Driver initialization.*/ + pwmObjectInit(&PWMD3); + PWMD3.tim = LPC_TMR32B0; + +#if LPC11xx_PWM_USE_CT32B0_CH0 + LPC_IOCON->PIO1_6 = 0xC2; +#endif + +#if LPC11xx_PWM_USE_CT32B0_CH1 + LPC_IOCON->PIO1_7 = 0xC2; +#endif +#endif + +#if LPC11xx_PWM_USE_CT32B1 + /* Driver initialization.*/ + pwmObjectInit(&PWMD4); + PWMD4.tim = LPC_TMR32B1; + +#if LPC11xx_PWM_USE_CT32B1_CH0 + LPC_IOCON->R_PIO1_1 = 0xC3; +#endif + +#if LPC11xx_PWM_USE_CT32B1_CH1 + LPC_IOCON->R_PIO1_2 = 0xC3; +#endif +#endif +} + +/** + * @brief Configures and activates the PWM peripheral. + * @note Starting a driver that is already in the @p PWM_READY state + * disables all the active channels. + * + * @param[in] pwmp pointer to a @p PWMDriver object + * + * @notapi + */ +void pwm_lld_start(PWMDriver *pwmp) { + uint32_t pr; + + if (pwmp->state == PWM_STOP) { + /* Clock activation and timer reset.*/ +#if LPC11xx_PWM_USE_CT16B0 + if (&PWMD1 == pwmp) { + LPC_SYSCON->SYSAHBCLKCTRL |= (1 << 7); + nvicEnableVector(TIMER_16_0_IRQn, LPC11xx_PWM_CT16B0_IRQ_PRIORITY); + } +#endif +#if LPC11xx_PWM_USE_CT16B1 + if (&PWMD2 == pwmp) { + LPC_SYSCON->SYSAHBCLKCTRL |= (1 << 8); + nvicEnableVector(TIMER_16_1_IRQn, LPC11xx_PWM_CT16B1_IRQ_PRIORITY); + } +#endif +#if LPC11xx_PWM_USE_CT32B0 + if (&PWMD3 == pwmp) { + LPC_SYSCON->SYSAHBCLKCTRL |= (1 << 9); + nvicEnableVector(TIMER_32_0_IRQn, LPC11xx_PWM_CT32B0_IRQ_PRIORITY); + } +#endif +#if LPC11xx_PWM_USE_CT32B1 + if (&PWMD4 == pwmp) { + LPC_SYSCON->SYSAHBCLKCTRL |= (1 << 10); + nvicEnableVector(TIMER_32_1_IRQn, LPC11xx_PWM_CT32B1_IRQ_PRIORITY); + } +#endif + } + else { + /* Driver re-configuration scenario, it must be stopped first.*/ + pwmp->tim->TCR = 0; + } + + /* Output enables and polarities setup.*/ + if(pwmp->config->channels[0].mode == PWM_OUTPUT_ACTIVE_LOW) + pwmp->tim->PWMC = (1 << 0); + + if(pwmp->config->channels[1].mode == PWM_OUTPUT_ACTIVE_LOW) + pwmp->tim->PWMC |= (1 << 1); + + /* Timer configured and started.*/ + pr = (uint16_t)((LPC11xx_SYSCLK / pwmp->config->frequency) - 1); + chDbgAssert(((uint32_t)(pr + 1) * pwmp->config->frequency) == LPC11xx_SYSCLK, + "pwm_lld_start(), #1", "invalid frequency"); + + pwmp->tim->TC = 0; + pwmp->tim->PR = pr; + pwmp->tim->IR = 0xFF; + pwmp->tim->MCR = MCR_MR3R; /* Reset on Match3 */ + pwmp->tim->MR3 = pwmp->config->period; + + if (pwmp->config->callback != NULL) + pwmp->tim->MCR |= MCR_MR3I; + + pwmp->tim->TCR = 1; /* Timer start */ +} + +/** + * @brief Deactivates the PWM peripheral. + * + * @param[in] pwmp pointer to a @p PWMDriver object + * + * @notapi + */ +void pwm_lld_stop(PWMDriver *pwmp) { + + /* If in ready state then disables the PWM clock.*/ + if (pwmp->state == PWM_READY) { + pwmp->tim->TCR = 0; /* Timer disabled. */ + pwmp->tim->MCR = 0; /* All IRQs disabled. */ + pwmp->tim->IR = 0xFF; /* Clear eventual pending IRQs. */ + pwmp->tim->PWMC = 0; /* PWM outputs disable. */ + +#if LPC11xx_PWM_USE_CT16B0 + if (&PWMD1 == pwmp) { + nvicDisableVector(TIMER_16_0_IRQn); + LPC_SYSCON->SYSAHBCLKCTRL &= ~(1 << 7); + } +#endif +#if LPC11xx_PWM_USE_CT16B1 + if (&PWMD2 == pwmp) { + nvicDisableVector(TIMER_16_1_IRQn); + LPC_SYSCON->SYSAHBCLKCTRL &= ~(1 << 8); + } +#endif +#if LPC11xx_PWM_USE_CT32B0 + if (&PWMD3 == pwmp) { + nvicDisableVector(TIMER_32_0_IRQn); + LPC_SYSCON->SYSAHBCLKCTRL &= ~(1 << 9); + } +#endif +#if LPC11xx_PWM_USE_CT32B1 + if (&PWMD4 == pwmp) { + nvicDisableVector(TIMER_32_1_IRQn); + LPC_SYSCON->SYSAHBCLKCTRL &= ~(1 << 10); + } +#endif + + } +} + +/** + * @brief Enables a PWM channel. + * @pre The PWM unit must have been activated using @p pwmStart(). + * @post The channel is active using the specified configuration. + * @note The function has effect at the next cycle start. + * + * @param[in] pwmp pointer to a @p PWMDriver object + * @param[in] channel PWM channel identifier (0...PWM_CHANNELS-1) + * @param[in] width PWM pulse width as clock pulses number + * + * @notapi + */ +void pwm_lld_enable_channel(PWMDriver *pwmp, + pwmchannel_t channel, + pwmcnt_t width) { + + pwmp->tim->MCR &= ~(7 << (channel * 3)); + + if ( channel == 0) + pwmp->tim->MR0 = width; /* New duty cycle. */ + else + pwmp->tim->MR1 = width; /* New duty cycle. */ + /* If there is a callback defined for the channel then the associated + interrupt must be enabled.*/ + if (pwmp->config->channels[channel].callback != NULL) { + pwmp->tim->IR = (1 << channel); /* Clear interrupt flag*/ + pwmp->tim->MCR |= (1 << (channel * 3)); /* Set interrupt on selected channel */ + } + +} + +/** + * @brief Disables a PWM channel. + * @pre The PWM unit must have been activated using @p pwmStart(). + * @post The channel is disabled and its output line returned to the + * idle state. + * @note The function has effect at the next cycle start. + * + * @param[in] pwmp pointer to a @p PWMDriver object + * @param[in] channel PWM channel identifier (0...PWM_CHANNELS-1) + * + * @notapi + */ +void pwm_lld_disable_channel(PWMDriver *pwmp, pwmchannel_t channel) { + + if ( channel == 0) + pwmp->tim->MR0 = 0; + else + pwmp->tim->MR1 = 0; + pwmp->tim->MCR &= ~(7 << (channel * 3)); + pwmp->tim->IR = (1 << channel); +} + +#endif /* HAL_USE_PWM */ + +/** @} */ diff --git a/os/hal/platforms/LPC11xx/pwm_lld.h b/os/hal/platforms/LPC11xx/pwm_lld.h new file mode 100644 index 000000000..2b3a37fd1 --- /dev/null +++ b/os/hal/platforms/LPC11xx/pwm_lld.h @@ -0,0 +1,364 @@ +/* + ChibiOS/RT - Copyright (C) 2006-2013 Giovanni Di Sirio + LPC11xx PWM driver - Copyright (C) 2013 Marcin Jokel + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file LPC11xx/pwm_lld.h + * @brief LPC11xx PWM subsystem low level driver header. + * + * @addtogroup PWM + * @{ + */ + +#ifndef _PWM_LLD_H_ +#define _PWM_LLD_H_ + +#if HAL_USE_PWM || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Unsupported modes and specific modes */ +/*===========================================================================*/ +#undef PWM_OUTPUT_ACTIVE_HIGH + +/*===========================================================================*/ +/* Driver constants. */ +/*===========================================================================*/ + +#define IR_MR0INT (1 << 0) +#define IR_MR1INT (1 << 1) +#define IR_MR2INT (1 << 2) +#define IR_MR3INT (1 << 3) +#define IR_CR0INT (1 << 4) +#define IR_CR1INT (1 << 5) +#define IR_CR2INT (1 << 6) +#define IR_CR3INT (1 << 7) + +#define MCR_MR3I (1 << 9) +#define MCR_MR3R (1 << 10) +#define MCR_MR3S (1 << 11) +/** + * @brief Number of PWM channels per PWM driver. + */ +#define PWM_CHANNELS 2 + +/*===========================================================================*/ +/* Driver pre-compile time settings. */ +/*===========================================================================*/ + +/** + * @name Configuration options + * @{ + */ + +/** + * @brief PWMD1 driver enable switch. + * @details If set to @p TRUE the support for PWMD1 is included. + * @note The default is @p TRUE. + */ +#if !defined(LPC11xx_PWM_USE_CT16B0) || defined(__DOXYGEN__) +#define LPC11xx_PWM_USE_CT16B0 FALSE +#endif + +/** + * @brief PWMD2 driver enable switch. + * @details If set to @p TRUE the support for PWMD2 is included. + * @note The default is @p TRUE. + */ +#if !defined(LPC11xx_PWM_USE_CT16B1) || defined(__DOXYGEN__) +#define LPC11xx_PWM_USE_CT16B1 FALSE +#endif + +/** + * @brief PWMD3 driver enable switch. + * @details If set to @p TRUE the support for PWMD3 is included. + * @note The default is @p TRUE. + */ +#if !defined(LPC11xx_PWM_USE_CT32B0) || defined(__DOXYGEN__) +#define LPC11xx_PWM_USE_CT32B0 FALSE +#endif + +/** + * @brief PWMD4 driver enable switch. + * @details If set to @p TRUE the support for PWMD4 is included. + * @note The default is @p TRUE. + */ +#if !defined(LPC11xx_PWM_USE_CT32B1) || defined(__DOXYGEN__) +#define LPC11xx_PWM_USE_CT32B1 FALSE +#endif + +/** + * @brief PWMD1 Channel 0 driver enable switch. + * @details If set to @p TRUE PWMD1 Channel 0 is enabled. + * @note The default is @p FALSE. + */ +#if !defined(LPC11xx_PWM_USE_CT16B0_CH0) || defined(__DOXYGEN__) +#define LPC11xx_PWM_USE_CT16B0_CH0 FALSE +#endif + +/** + * @brief PWMD1 Channel 1 driver enable switch. + * @details If set to @p TRUE PWMD1 Channel 1 is enabled. + * @note The default is @p FALSE. + */ +#if !defined(LPC11xx_PWM_USE_CT16B0_CH1) || defined(__DOXYGEN__) +#define LPC11xx_PWM_USE_CT16B0_CH1 FALSE +#endif + +/** + * @brief PWMD2 Channel 0 driver enable switch. + * @details If set to @p TRUE PWMD2 Channel 0 is enabled. + * @note The default is @p FALSE. + */ +#if !defined(LPC11xx_PWM_USE_CT16B1_CH0) || defined(__DOXYGEN__) +#define LPC11xx_PWM_USE_CT16B1_CH0 FALSE +#endif + +/** + * @brief PWMD2 Channel 1 driver enable switch. + * @details If set to @p TRUE PWMD2 Channel 1 is enabled. + * @note The default is @p FALSE. + */ +#if !defined(LPC11xx_PWM_USE_CT16B1_CH1) || defined(__DOXYGEN__) +#define LPC11xx_PWM_USE_CT16B1_CH1 FALSE +#endif + +/** + * @brief PWMD3 Channel 0 driver enable switch. + * @details If set to @p TRUE PWMD3 Channel 0 is enabled. + * @note The default is @p FALSE. + */ +#if !defined(LPC11xx_PWM_USE_CT32B0_CH0) || defined(__DOXYGEN__) +#define LPC11xx_PWM_USE_CT32B0_CH0 FALSE +#endif + +/** + * @brief PWMD3 Channel 1 driver enable switch. + * @details If set to @p TRUE PWMD3 Channel 1 is enabled. + * @note The default is @p FALSE. + */ +#if !defined(LPC11xx_PWM_USE_CT32B0_CH1) || defined(__DOXYGEN__) +#define LPC11xx_PWM_USE_CT32B0_CH1 FALSE +#endif + +/** + * @brief PWMD4 Channel 0 driver enable switch. + * @details If set to @p TRUE PWMD4 Channel 0 is enabled. + * @note The default is @p FALSE. + */ +#if !defined(LPC11xx_PWM_USE_CT32B1_CH0) || defined(__DOXYGEN__) +#define LPC11xx_PWM_USE_CT32B1_CH0 FALSE +#endif + +/** + * @brief PWMD4 Channel 1 driver enable switch. + * @details If set to @p TRUE PWMD4 Channel 1 is enabled. + * @note The default is @p FALSE. + */ +#if !defined(LPC11xx_PWM_USE_CT32B1_CH1) || defined(__DOXYGEN__) +#define LPC11xx_PWM_USE_CT32B1_CH1 FALSE +#endif +/** + * @brief PWMD1 interrupt priority level setting. + */ +#if !defined(LPC11xx_PWM_CT16B0_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define LPC11xx_PWM_CT16B0_IRQ_PRIORITY 3 +#endif + +/** + * @brief PWMD2 interrupt priority level setting. + */ +#if !defined(LPC11xx_PWM_CT16B1_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define LPC11xx_PWM_CT16B1_IRQ_PRIORITY 3 +#endif + +/** + * @brief PWMD3 interrupt priority level setting. + */ +#if !defined(LPC11xx_PWM_CT32B0_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define LPC11xx_PWM_CT32B0_IRQ_PRIORITY 3 +#endif + +/** + * @brief PWMD4 interrupt priority level setting. + */ +#if !defined(LPC11xx_PWM_CT32B1_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define LPC11xx_PWM_CT32B1_IRQ_PRIORITY 3 +#endif + +/*===========================================================================*/ +/* Configuration checks. */ +/*===========================================================================*/ + + + +/*===========================================================================*/ +/* Driver data structures and types. */ +/*===========================================================================*/ + +/** + * @brief PWM mode type. + */ +typedef uint32_t pwmmode_t; + +/** + * @brief PWM channel type. + */ +typedef uint8_t pwmchannel_t; + +/** + * @brief PWM counter type. + */ +typedef uint16_t pwmcnt_t; + +/** + * @brief PWM driver channel configuration structure. + */ +typedef struct { + /** + * @brief Channel active logic level. + */ + pwmmode_t mode; + /** + * @brief Channel callback pointer. + * @note This callback is invoked on the channel compare event. If set to + * @p NULL then the callback is disabled. + */ + pwmcallback_t callback; + /* End of the mandatory fields.*/ +} PWMChannelConfig; + +/** + * @brief PWM driver configuration structure. + */ +typedef struct { + /** + * @brief Timer clock in Hz. + * @note The low level can use assertions in order to catch invalid + * frequency specifications. + */ + uint32_t frequency; + /** + * @brief PWM period in ticks. + * @note The low level can use assertions in order to catch invalid + * period specifications. + */ + pwmcnt_t period; + /** + * @brief Periodic callback pointer. + * @note This callback is invoked on PWM counter reset. If set to + * @p NULL then the callback is disabled. + */ + pwmcallback_t callback; + /** + * @brief Channels configurations. + */ + PWMChannelConfig channels[PWM_CHANNELS]; + /* End of the mandatory fields.*/ + +} PWMConfig; + +/** + * @brief Structure representing a PWM driver. + */ +struct PWMDriver { + /** + * @brief Driver state. + */ + pwmstate_t state; + /** + * @brief Current driver configuration data. + */ + const PWMConfig *config; + /** + * @brief Current PWM period in ticks. + */ + pwmcnt_t period; +#if defined(PWM_DRIVER_EXT_FIELDS) + PWM_DRIVER_EXT_FIELDS +#endif + /* End of the mandatory fields.*/ + /** + * @brief Timer base clock. + */ + uint32_t clock; + /** + * @brief Pointer to the TIMx registers block. + */ + LPC_TMR_TypeDef *tim; +}; + +/*===========================================================================*/ +/* Driver macros. */ +/*===========================================================================*/ + +/** + * @brief Changes the period the PWM peripheral. + * @details This function changes the period of a PWM unit that has already + * been activated using @p pwmStart(). + * @pre The PWM unit must have been activated using @p pwmStart(). + * @post The PWM unit period is changed to the new value. + * @note The function has effect at the next cycle start. + * @note If a period is specified that is shorter than the pulse width + * programmed in one of the channels then the behavior is not + * guaranteed. + * + * @param[in] pwmp pointer to a @p PWMDriver object + * @param[in] period new cycle time in ticks + * + * @notapi + */ +#define pwm_lld_change_period(pwmp, period) \ + ((pwmp)->tim->MR3 = (period)) + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +#if LPC11xx_PWM_USE_CT16B0 && !defined(__DOXYGEN__) +extern PWMDriver PWMD1; +#endif + +#if LPC11xx_PWM_USE_CT16B1 && !defined(__DOXYGEN__) +extern PWMDriver PWMD2; +#endif + +#if LPC11xx_PWM_USE_CT32B0 && !defined(__DOXYGEN__) +extern PWMDriver PWMD3; +#endif + +#if LPC11xx_PWM_USE_CT32B1 && !defined(__DOXYGEN__) +extern PWMDriver PWMD4; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + void pwm_lld_init(void); + void pwm_lld_start(PWMDriver *pwmp); + void pwm_lld_stop(PWMDriver *pwmp); + void pwm_lld_enable_channel(PWMDriver *pwmp, + pwmchannel_t channel, + pwmcnt_t width); + void pwm_lld_disable_channel(PWMDriver *pwmp, pwmchannel_t channel); +#ifdef __cplusplus +} +#endif + +#endif /* HAL_USE_PWM */ + +#endif /* _PWM_LLD_H_ */ + +/** @} */ -- cgit v1.2.3