From b67ecdfeca2d1d5289e4c9b577a1940cf8702ab3 Mon Sep 17 00:00:00 2001 From: Fabio Utzig Date: Mon, 7 Mar 2016 12:33:00 -0300 Subject: [KINETIS] Move from main repo to contrib --- os/hal/ports/KINETIS/LLD/adc_lld.c | 258 +++++++++++++++++++++++ os/hal/ports/KINETIS/LLD/adc_lld.h | 360 ++++++++++++++++++++++++++++++++ os/hal/ports/KINETIS/LLD/ext_lld.c | 361 ++++++++++++++++++++++++++++++++ os/hal/ports/KINETIS/LLD/ext_lld.h | 188 +++++++++++++++++ os/hal/ports/KINETIS/LLD/i2c_lld.c | 415 +++++++++++++++++++++++++++++++++++++ os/hal/ports/KINETIS/LLD/i2c_lld.h | 208 +++++++++++++++++++ 6 files changed, 1790 insertions(+) create mode 100644 os/hal/ports/KINETIS/LLD/adc_lld.c create mode 100644 os/hal/ports/KINETIS/LLD/adc_lld.h create mode 100644 os/hal/ports/KINETIS/LLD/ext_lld.c create mode 100644 os/hal/ports/KINETIS/LLD/ext_lld.h create mode 100644 os/hal/ports/KINETIS/LLD/i2c_lld.c create mode 100644 os/hal/ports/KINETIS/LLD/i2c_lld.h (limited to 'os/hal/ports/KINETIS/LLD') diff --git a/os/hal/ports/KINETIS/LLD/adc_lld.c b/os/hal/ports/KINETIS/LLD/adc_lld.c new file mode 100644 index 0000000..c0904c8 --- /dev/null +++ b/os/hal/ports/KINETIS/LLD/adc_lld.c @@ -0,0 +1,258 @@ +/* + ChibiOS - Copyright (C) 2014 Derek Mulcahy + + 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 KINETIS/LLD/adc_lld.c + * @brief KINETIS ADC subsystem low level driver source. + * + * @addtogroup ADC + * @{ + */ + +#include "hal.h" + +#if HAL_USE_ADC || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver local definitions. */ +/*===========================================================================*/ + +#define ADC_CHANNEL_MASK 0x1f + +/*===========================================================================*/ +/* Driver exported variables. */ +/*===========================================================================*/ + +/** @brief ADC1 driver identifier.*/ +#if KINETIS_ADC_USE_ADC0 || defined(__DOXYGEN__) +ADCDriver ADCD1; +#endif + +/*===========================================================================*/ +/* Driver local variables and types. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver local functions. */ +/*===========================================================================*/ + +static void calibrate(ADCDriver *adcp) { + + /* Clock Divide by 8, Use Bus Clock Div 2 */ + /* At 48MHz this results in ADCCLK of 48/8/2 == 3MHz */ + adcp->adc->CFG1 = ADCx_CFG1_ADIV(ADCx_CFG1_ADIV_DIV_8) | + ADCx_CFG1_ADICLK(ADCx_CFG1_ADIVCLK_BUS_CLOCK_DIV_2); + + /* Use software trigger and disable DMA etc. */ + adcp->adc->SC2 = 0; + + /* Enable Hardware Average, Average 32 Samples, Calibrate */ + adcp->adc->SC3 = ADCx_SC3_AVGE | + ADCx_SC3_AVGS(ADCx_SC3_AVGS_AVERAGE_32_SAMPLES) | + ADCx_SC3_CAL; + + /* FIXME: May take several ms. Use an interrupt instead of busy wait */ + /* Wait for calibration completion */ + while (!(adcp->adc->SC1A & ADCx_SC1n_COCO)) + ; + + uint16_t gain = ((adcp->adc->CLP0 + adcp->adc->CLP1 + adcp->adc->CLP2 + + adcp->adc->CLP3 + adcp->adc->CLP4 + adcp->adc->CLPS) / 2) | 0x8000; + adcp->adc->PG = gain; + + gain = ((adcp->adc->CLM0 + adcp->adc->CLM1 + adcp->adc->CLM2 + + adcp->adc->CLM3 + adcp->adc->CLM4 + adcp->adc->CLMS) / 2) | 0x8000; + adcp->adc->MG = gain; + +} + +/*===========================================================================*/ +/* Driver interrupt handlers. */ +/*===========================================================================*/ + +#if KINETIS_ADC_USE_ADC0 || defined(__DOXYGEN__) +/** + * @brief ADC interrupt handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(KINETIS_ADC0_IRQ_VECTOR) { + OSAL_IRQ_PROLOGUE(); + + ADCDriver *adcp = &ADCD1; + + /* Disable Interrupt, Disable Channel */ + adcp->adc->SC1A = ADCx_SC1n_ADCH(ADCx_SC1n_ADCH_DISABLED); + + /* Read the sample into the buffer */ + adcp->samples[adcp->current_index++] = adcp->adc->RA; + + bool more = true; + + /* At the end of the buffer then we may be finished */ + if (adcp->current_index == adcp->number_of_samples) { + _adc_isr_full_code(&ADCD1); + + adcp->current_index = 0; + + /* We are never finished in circular mode */ + more = ADCD1.grpp->circular; + } + + if (more) { + + /* Signal half completion in circular mode. */ + if (ADCD1.grpp->circular && + (adcp->current_index == (adcp->number_of_samples / 2))) { + + _adc_isr_half_code(&ADCD1); + } + + /* Skip to the next channel */ + do { + adcp->current_channel = (adcp->current_channel + 1) & ADC_CHANNEL_MASK; + } while (((1 << adcp->current_channel) & adcp->grpp->channel_mask) == 0); + + /* Enable Interrupt, Select the Channel */ + adcp->adc->SC1A = ADCx_SC1n_AIEN | ADCx_SC1n_ADCH(adcp->current_channel); + } + + OSAL_IRQ_EPILOGUE(); +} +#endif + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +/** + * @brief Low level ADC driver initialization. + * + * @notapi + */ +void adc_lld_init(void) { + +#if KINETIS_ADC_USE_ADC0 + /* Driver initialization.*/ + adcObjectInit(&ADCD1); +#endif + + /* The shared vector is initialized on driver initialization and never + disabled.*/ + nvicEnableVector(ADC0_IRQn, KINETIS_ADC_IRQ_PRIORITY); +} + +/** + * @brief Configures and activates the ADC peripheral. + * + * @param[in] adcp pointer to the @p ADCDriver object + * + * @notapi + */ +void adc_lld_start(ADCDriver *adcp) { + + /* If in stopped state then enables the ADC clock.*/ + if (adcp->state == ADC_STOP) { + SIM->SCGC6 |= SIM_SCGC6_ADC0; + +#if KINETIS_ADC_USE_ADC0 + if (&ADCD1 == adcp) { + adcp->adc = ADC0; + if (adcp->config->calibrate) { + calibrate(adcp); + } + } +#endif /* KINETIS_ADC_USE_ADC0 */ + } +} + +/** + * @brief Deactivates the ADC peripheral. + * + * @param[in] adcp pointer to the @p ADCDriver object + * + * @notapi + */ +void adc_lld_stop(ADCDriver *adcp) { + + /* If in ready state then disables the ADC clock.*/ + if (adcp->state == ADC_READY) { + SIM->SCGC6 &= ~SIM_SCGC6_ADC0; + +#if KINETIS_ADC_USE_ADC0 + if (&ADCD1 == adcp) { + /* Disable Interrupt, Disable Channel */ + adcp->adc->SC1A = ADCx_SC1n_ADCH(ADCx_SC1n_ADCH_DISABLED); + } +#endif + } +} + +/** + * @brief Starts an ADC conversion. + * + * @param[in] adcp pointer to the @p ADCDriver object + * + * @notapi + */ +void adc_lld_start_conversion(ADCDriver *adcp) { + const ADCConversionGroup *grpp = adcp->grpp; + + /* Enable the Bandgap Buffer if channel mask includes BANDGAP */ + if (grpp->channel_mask & ADC_BANDGAP) { + PMC->REGSC |= PMC_REGSC_BGBE; + } + + adcp->number_of_samples = adcp->depth * grpp->num_channels; + adcp->current_index = 0; + + /* Skip to the next channel */ + adcp->current_channel = 0; + while (((1 << adcp->current_channel) & grpp->channel_mask) == 0) { + adcp->current_channel = (adcp->current_channel + 1) & ADC_CHANNEL_MASK; + } + + /* Set clock speed and conversion size */ + adcp->adc->CFG1 = grpp->cfg1; + + /* Set averaging */ + adcp->adc->SC3 = grpp->sc3; + + /* Enable Interrupt, Select Channel */ + adcp->adc->SC1A = ADCx_SC1n_AIEN | ADCx_SC1n_ADCH(adcp->current_channel); +} + +/** + * @brief Stops an ongoing conversion. + * + * @param[in] adcp pointer to the @p ADCDriver object + * + * @notapi + */ +void adc_lld_stop_conversion(ADCDriver *adcp) { + const ADCConversionGroup *grpp = adcp->grpp; + + /* Disable the Bandgap buffer if channel mask includes BANDGAP */ + if (grpp->channel_mask & ADC_BANDGAP) { + /* Clear BGBE, ACKISO is w1c, avoid setting */ + PMC->REGSC &= ~(PMC_REGSC_BGBE | PMC_REGSC_ACKISO); + } + +} + +#endif /* HAL_USE_ADC */ + +/** @} */ diff --git a/os/hal/ports/KINETIS/LLD/adc_lld.h b/os/hal/ports/KINETIS/LLD/adc_lld.h new file mode 100644 index 0000000..22db2c0 --- /dev/null +++ b/os/hal/ports/KINETIS/LLD/adc_lld.h @@ -0,0 +1,360 @@ +/* + ChibiOS - Copyright (C) 2014 Derek Mulcahy + + 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 KINETIS/LLD/adc_lld.h + * @brief KINETIS ADC subsystem low level driver header. + * + * @addtogroup ADC + * @{ + */ + +#ifndef _ADC_LLD_H_ +#define _ADC_LLD_H_ + +#if HAL_USE_ADC || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver constants. */ +/*===========================================================================*/ + +/** + * @name Absolute Maximum Ratings + * @{ + */ +/** + * @brief Minimum ADC clock frequency. + */ +#define KINETIS_ADCCLK_MIN 600000 + +/** + * @brief Maximum ADC clock frequency. + */ +#define KINETIS_ADCCLK_MAX 36000000 + +#define ADCx_SC3_AVGS_AVERAGE_4_SAMPLES 0 +#define ADCx_SC3_AVGS_AVERAGE_8_SAMPLES 1 +#define ADCx_SC3_AVGS_AVERAGE_16_SAMPLES 2 +#define ADCx_SC3_AVGS_AVERAGE_32_SAMPLES 3 + +#define ADCx_CFG1_ADIV_DIV_1 0 +#define ADCx_CFG1_ADIV_DIV_2 1 +#define ADCx_CFG1_ADIV_DIV_4 2 +#define ADCx_CFG1_ADIV_DIV_8 3 + +#define ADCx_CFG1_ADIVCLK_BUS_CLOCK 0 +#define ADCx_CFG1_ADIVCLK_BUS_CLOCK_DIV_2 1 +#define ADCx_CFG1_ADIVCLK_BUS_ALTCLK 2 +#define ADCx_CFG1_ADIVCLK_BUS_ADACK 3 + +#define ADCx_CFG1_MODE_8_OR_9_BITS 0 +#define ADCx_CFG1_MODE_12_OR_13_BITS 1 +#define ADCx_CFG1_MODE_10_OR_11_BITS 2 +#define ADCx_CFG1_MODE_16_BITS 3 + +#define ADCx_SC1n_ADCH_DAD0 0 +#define ADCx_SC1n_ADCH_DAD1 1 +#define ADCx_SC1n_ADCH_DAD2 2 +#define ADCx_SC1n_ADCH_DAD3 3 +#define ADCx_SC1n_ADCH_DADP0 0 +#define ADCx_SC1n_ADCH_DADP1 1 +#define ADCx_SC1n_ADCH_DADP2 2 +#define ADCx_SC1n_ADCH_DADP3 3 +#define ADCx_SC1n_ADCH_AD4 4 +#define ADCx_SC1n_ADCH_AD5 5 +#define ADCx_SC1n_ADCH_AD6 6 +#define ADCx_SC1n_ADCH_AD7 7 +#define ADCx_SC1n_ADCH_AD8 8 +#define ADCx_SC1n_ADCH_AD9 9 +#define ADCx_SC1n_ADCH_AD10 10 +#define ADCx_SC1n_ADCH_AD11 11 +#define ADCx_SC1n_ADCH_AD12 12 +#define ADCx_SC1n_ADCH_AD13 13 +#define ADCx_SC1n_ADCH_AD14 14 +#define ADCx_SC1n_ADCH_AD15 15 +#define ADCx_SC1n_ADCH_AD16 16 +#define ADCx_SC1n_ADCH_AD17 17 +#define ADCx_SC1n_ADCH_AD18 18 +#define ADCx_SC1n_ADCH_AD19 19 +#define ADCx_SC1n_ADCH_AD20 20 +#define ADCx_SC1n_ADCH_AD21 21 +#define ADCx_SC1n_ADCH_AD22 22 +#define ADCx_SC1n_ADCH_AD23 23 +#define ADCx_SC1n_ADCH_TEMP_SENSOR 26 +#define ADCx_SC1n_ADCH_BANDGAP 27 +#define ADCx_SC1n_ADCH_VREFSH 29 +#define ADCx_SC1n_ADCH_VREFSL 30 +#define ADCx_SC1n_ADCH_DISABLED 31 + +#define ADC_DAD0 (1 << ADCx_SC1n_ADCH_DAD0) +#define ADC_DAD1 (1 << ADCx_SC1n_ADCH_DAD1) +#define ADC_DAD2 (1 << ADCx_SC1n_ADCH_DAD2) +#define ADC_DAD3 (1 << ADCx_SC1n_ADCH_DAD3) +#define ADC_DADP0 (1 << ADCx_SC1n_ADCH_DADP0) +#define ADC_DADP1 (1 << ADCx_SC1n_ADCH_DADP1) +#define ADC_DADP2 (1 << ADCx_SC1n_ADCH_DADP2) +#define ADC_DADP3 (1 << ADCx_SC1n_ADCH_DADP3) +#define ADC_AD4 (1 << ADCx_SC1n_ADCH_AD4) +#define ADC_AD5 (1 << ADCx_SC1n_ADCH_AD5) +#define ADC_AD6 (1 << ADCx_SC1n_ADCH_AD6) +#define ADC_AD7 (1 << ADCx_SC1n_ADCH_AD7) +#define ADC_AD8 (1 << ADCx_SC1n_ADCH_AD8) +#define ADC_AD9 (1 << ADCx_SC1n_ADCH_AD9) +#define ADC_AD10 (1 << ADCx_SC1n_ADCH_AD10) +#define ADC_AD11 (1 << ADCx_SC1n_ADCH_AD11) +#define ADC_AD12 (1 << ADCx_SC1n_ADCH_AD12) +#define ADC_AD13 (1 << ADCx_SC1n_ADCH_AD13) +#define ADC_AD14 (1 << ADCx_SC1n_ADCH_AD14) +#define ADC_AD15 (1 << ADCx_SC1n_ADCH_AD15) +#define ADC_AD16 (1 << ADCx_SC1n_ADCH_AD16) +#define ADC_AD17 (1 << ADCx_SC1n_ADCH_AD17) +#define ADC_AD18 (1 << ADCx_SC1n_ADCH_AD18) +#define ADC_AD19 (1 << ADCx_SC1n_ADCH_AD19) +#define ADC_AD20 (1 << ADCx_SC1n_ADCH_AD20) +#define ADC_AD21 (1 << ADCx_SC1n_ADCH_AD21) +#define ADC_AD22 (1 << ADCx_SC1n_ADCH_AD22) +#define ADC_AD23 (1 << ADCx_SC1n_ADCH_AD23) +#define ADC_TEMP_SENSOR (1 << ADCx_SC1n_ADCH_TEMP_SENSOR) +#define ADC_BANDGAP (1 << ADCx_SC1n_ADCH_BANDGAP) +#define ADC_VREFSH (1 << ADCx_SC1n_ADCH_VREFSH) +#define ADC_VREFSL (1 << ADCx_SC1n_ADCH_VREFSL) +#define ADC_DISABLED (1 << ADCx_SC1n_ADCH_DISABLED) + +/** @} */ + +/*===========================================================================*/ +/* Driver pre-compile time settings. */ +/*===========================================================================*/ + +/** + * @name Configuration options + * @{ + */ + +/** + * @brief ADC1 driver enable switch. + * @details If set to @p TRUE the support for ADC1 is included. + * @note The default is @p TRUE. + */ +#if !defined(KINETIS_ADC_USE_ADC0) || defined(__DOXYGEN__) +#define KINETIS_ADC_USE_ADC0 FALSE +#endif + +/** + * @brief ADC interrupt priority level setting. + */ +#if !defined(KINETIS_ADC_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define KINETIS_ADC_IRQ_PRIORITY 5 +#endif + +/** @} */ + +/*===========================================================================*/ +/* Derived constants and error checks. */ +/*===========================================================================*/ + +#if KINETIS_ADC_USE_ADC0 && !KINETIS_HAS_ADC0 +#error "ADC1 not present in the selected device" +#endif + +#if !KINETIS_ADC_USE_ADC0 +#error "ADC driver activated but no ADC peripheral assigned" +#endif + +/*===========================================================================*/ +/* Driver data structures and types. */ +/*===========================================================================*/ + +/** + * @brief ADC sample data type. + */ +typedef uint16_t adcsample_t; + +/** + * @brief Channels number in a conversion group. + */ +typedef uint16_t adc_channels_num_t; + +/** + * @brief Possible ADC failure causes. + * @note Error codes are architecture dependent and should not relied + * upon. + */ +typedef enum { + ADC_ERR_DMAFAILURE = 0, /**< DMA operations failure. */ + ADC_ERR_OVERFLOW = 1 /**< ADC overflow condition. */ +} adcerror_t; + +/** + * @brief Type of a structure representing an ADC driver. + */ +typedef struct ADCDriver ADCDriver; + +/** + * @brief ADC notification callback type. + * + * @param[in] adcp pointer to the @p ADCDriver object triggering the + * callback + * @param[in] buffer pointer to the most recent samples data + * @param[in] n number of buffer rows available starting from @p buffer + */ +typedef void (*adccallback_t)(ADCDriver *adcp, adcsample_t *buffer, size_t n); + +/** + * @brief ADC error callback type. + * + * @param[in] adcp pointer to the @p ADCDriver object triggering the + * callback + * @param[in] err ADC error code + */ +typedef void (*adcerrorcallback_t)(ADCDriver *adcp, adcerror_t err); + +/** + * @brief Conversion group configuration structure. + * @details This implementation-dependent structure describes a conversion + * operation. + */ +typedef struct { + /** + * @brief Enables the circular buffer mode for the group. + */ + bool circular; + /** + * @brief Number of the analog channels belonging to the conversion group. + */ + adc_channels_num_t num_channels; + /** + * @brief Callback function associated to the group or @p NULL. + */ + adccallback_t end_cb; + /** + * @brief Error callback or @p NULL. + */ + adcerrorcallback_t error_cb; + /* End of the mandatory fields.*/ + /** + * @brief Bitmask of channels for ADC conversion. + */ + uint32_t channel_mask; + /** + * @brief ADC CFG1 register initialization data. + * @note All the required bits must be defined into this field. + */ + uint32_t cfg1; + /** + * @brief ADC SC3 register initialization data. + * @note All the required bits must be defined into this field. + */ + uint32_t sc3; +} ADCConversionGroup; + +/** + * @brief Driver configuration structure. + * @note It could be empty on some architectures. + */ +typedef struct { + /* Perform first time calibration */ + bool calibrate; +} ADCConfig; + +/** + * @brief Structure representing an ADC driver. + */ +struct ADCDriver { + /** + * @brief Driver state. + */ + adcstate_t state; + /** + * @brief Current configuration data. + */ + const ADCConfig *config; + /** + * @brief Current samples buffer pointer or @p NULL. + */ + adcsample_t *samples; + /** + * @brief Current samples buffer depth or @p 0. + */ + size_t depth; + /** + * @brief Current conversion group pointer or @p NULL. + */ + const ADCConversionGroup *grpp; +#if ADC_USE_WAIT || defined(__DOXYGEN__) + /** + * @brief Waiting thread. + */ + thread_reference_t thread; +#endif +#if ADC_USE_MUTUAL_EXCLUSION || defined(__DOXYGEN__) + /** + * @brief Mutex protecting the peripheral. + */ + mutex_t mutex; +#endif /* ADC_USE_MUTUAL_EXCLUSION */ +#if defined(ADC_DRIVER_EXT_FIELDS) + ADC_DRIVER_EXT_FIELDS +#endif + /* End of the mandatory fields.*/ + /** + * @brief Pointer to the ADCx registers block. + */ + ADC_TypeDef *adc; + /** + * @brief Number of samples expected. + */ + size_t number_of_samples; + /** + * @brief Current position in the buffer. + */ + size_t current_index; + /** + * @brief Current channel index into group channel_mask. + */ + size_t current_channel; +}; + +/*===========================================================================*/ +/* Driver macros. */ +/*===========================================================================*/ + + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +#if KINETIS_ADC_USE_ADC0 && !defined(__DOXYGEN__) +extern ADCDriver ADCD1; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + void adc_lld_init(void); + void adc_lld_start(ADCDriver *adcp); + void adc_lld_stop(ADCDriver *adcp); + void adc_lld_start_conversion(ADCDriver *adcp); + void adc_lld_stop_conversion(ADCDriver *adcp); +#ifdef __cplusplus +} +#endif + +#endif /* HAL_USE_ADC */ + +#endif /* _ADC_LLD_H_ */ + +/** @} */ diff --git a/os/hal/ports/KINETIS/LLD/ext_lld.c b/os/hal/ports/KINETIS/LLD/ext_lld.c new file mode 100644 index 0000000..e85f032 --- /dev/null +++ b/os/hal/ports/KINETIS/LLD/ext_lld.c @@ -0,0 +1,361 @@ +/* + ChibiOS - Copyright (C) 2014 Derek Mulcahy + + 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 KINETIS/LLD/ext_lld.c + * @brief KINETIS EXT subsystem low level driver source. + * + * @addtogroup EXT + * @{ + */ + +#include "hal.h" + +#if HAL_USE_EXT || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver local definitions. */ +/*===========================================================================*/ + +#define PCR_IRQC_DISABLED 0x0 +#define PCR_IRQC_DMA_RISING_EDGE 0x1 +#define PCR_IRQC_DMA_FALLING_EDGE 0x2 +#define PCR_IRQC_DMA_EITHER_EDGE 0x3 + +#define PCR_IRQC_LOGIC_ZERO 0x8 +#define PCR_IRQC_RISING_EDGE 0x9 +#define PCR_IRQC_FALLING_EDGE 0xA +#define PCR_IRQC_EITHER_EDGE 0xB +#define PCR_IRQC_LOGIC_ONE 0xC + +/*===========================================================================*/ +/* Driver exported variables. */ +/*===========================================================================*/ + +/** + * @brief EXTD1 driver identifier. + */ +EXTDriver EXTD1; + +/*===========================================================================*/ +/* Driver local variables and types. */ +/*===========================================================================*/ + +/* A channel map for each channel. + * + * The index is the pin number. + * The result is the channel for that pin. + */ +#if KINETIS_EXT_PORTA_WIDTH > 0 +uint8_t porta_channel_map[KINETIS_EXT_PORTA_WIDTH]; +#endif +#if KINETIS_EXT_PORTB_WIDTH > 0 +uint8_t portb_channel_map[KINETIS_EXT_PORTB_WIDTH]; +#endif +#if KINETIS_EXT_PORTC_WIDTH > 0 +uint8_t portc_channel_map[KINETIS_EXT_PORTC_WIDTH]; +#endif +#if KINETIS_EXT_PORTD_WIDTH > 0 +uint8_t portd_channel_map[KINETIS_EXT_PORTD_WIDTH]; +#endif +#if KINETIS_EXT_PORTE_WIDTH > 0 +uint8_t porte_channel_map[KINETIS_EXT_PORTE_WIDTH]; +#endif + +/*===========================================================================*/ +/* Driver local functions. */ +/*===========================================================================*/ + +/** + * @brief Enables EXTI IRQ sources. + * + * @notapi + */ +static void ext_lld_exti_irq_enable(void) { + +#if KINETIS_EXT_PORTA_WIDTH > 0 + nvicEnableVector(PINA_IRQn, KINETIS_EXT_PORTA_IRQ_PRIORITY); +#endif +#if KINETIS_EXT_PORTB_WIDTH > 0 + nvicEnableVector(PINB_IRQn, KINETIS_EXT_PORTB_IRQ_PRIORITY); +#endif +#if KINETIS_EXT_PORTC_WIDTH > 0 + nvicEnableVector(PINC_IRQn, KINETIS_EXT_PORTC_IRQ_PRIORITY); +#endif +#if KINETIS_EXT_PORTD_WIDTH > 0 + nvicEnableVector(PIND_IRQn, KINETIS_EXT_PORTD_IRQ_PRIORITY); +#endif +#if KINETIS_EXT_PORTE_WIDTH > 0 + nvicEnableVector(PINE_IRQn, KINETIS_EXT_PORTE_IRQ_PRIORITY); +#endif +} + +/** + * @brief Disables EXTI IRQ sources. + * + * @notapi + */ +static void ext_lld_exti_irq_disable(void) { + +#if KINETIS_EXT_PORTA_WIDTH > 0 + nvicDisableVector(PINA_IRQn); +#endif +#if KINETIS_EXT_PORTB_WIDTH > 0 + nvicDisableVector(PINB_IRQn); +#endif +#if KINETIS_EXT_PORTC_WIDTH > 0 + nvicDisableVector(PINC_IRQn); +#endif +#if KINETIS_EXT_PORTD_WIDTH > 0 + nvicDisableVector(PIND_IRQn); +#endif +#if KINETIS_EXT_PORTE_WIDTH > 0 + nvicDisableVector(PINE_IRQn); +#endif +} + +/*===========================================================================*/ +/* Driver interrupt handlers. */ +/*===========================================================================*/ + +/* + * Generic interrupt handler. + */ +static inline void irq_handler(PORT_TypeDef * const port, const unsigned port_width, const uint8_t *channel_map) { + unsigned pin; + uint32_t isfr = port->ISFR; + + /* Clear all pending interrupts on this port. */ + port->ISFR = 0xFFFFFFFF; + + for (pin = 0; pin < port_width; pin++) { + if (isfr & (1 << pin)) { + expchannel_t channel = channel_map[pin]; + EXTD1.config->channels[channel].cb(&EXTD1, channel); + } + } +} + +/** + * @brief PORTA interrupt handler. + * + * @isr + */ +#if defined(KINETIS_PORTA_IRQ_VECTOR) && KINETIS_EXT_PORTA_WIDTH > 0 +OSAL_IRQ_HANDLER(KINETIS_PORTA_IRQ_VECTOR) { + OSAL_IRQ_PROLOGUE(); + + irq_handler(PORTA, KINETIS_EXT_PORTA_WIDTH, porta_channel_map); + + OSAL_IRQ_EPILOGUE(); +} +#endif /* KINETIS_EXT_PORTA_WIDTH > 0 */ + +/** + * @brief PORTB interrupt handler. + * + * @isr + */ +#if defined(KINETIS_PORTB_IRQ_VECTOR) && KINETIS_EXT_PORTB_WIDTH > 0 +OSAL_IRQ_HANDLER(KINETIS_PORTB_IRQ_VECTOR) { + OSAL_IRQ_PROLOGUE(); + + irq_handler(PORTB, KINETIS_EXT_PORTB_WIDTH, portb_channel_map); + + OSAL_IRQ_EPILOGUE(); +} +#endif /* KINETIS_EXT_PORTB_WIDTH > 0 */ + +/** + * @brief PORTC interrupt handler. + * + * @isr + */ +#if defined(KINETIS_PORTC_IRQ_VECTOR) && KINETIS_EXT_PORTC_WIDTH > 0 +OSAL_IRQ_HANDLER(KINETIS_PORTC_IRQ_VECTOR) { + OSAL_IRQ_PROLOGUE(); + + irq_handler(PORTC, KINETIS_EXT_PORTC_WIDTH, portc_channel_map); + + OSAL_IRQ_EPILOGUE(); +} +#endif /* KINETIS_EXT_PORTC_WIDTH > 0 */ + +/** + * @brief PORTD interrupt handler. + * + * @isr + */ +#if defined(KINETIS_PORTD_IRQ_VECTOR) && KINETIS_EXT_PORTD_WIDTH > 0 +OSAL_IRQ_HANDLER(KINETIS_PORTD_IRQ_VECTOR) { + OSAL_IRQ_PROLOGUE(); + + irq_handler(PORTD, KINETIS_EXT_PORTD_WIDTH, portd_channel_map); + + OSAL_IRQ_EPILOGUE(); +} +#endif /* KINETIS_EXT_PORTD_WIDTH > 0 */ + +/** + * @brief PORTE interrupt handler. + * + * @isr + */ +#if defined(KINETIS_PORTE_IRQ_VECTOR) && KINETIS_EXT_PORTE_WIDTH > 0 +OSAL_IRQ_HANDLER(KINETIS_PORTE_IRQ_VECTOR) { + OSAL_IRQ_PROLOGUE(); + + irq_handler(PORTE, KINETIS_EXT_PORTE_WIDTH, porte_channel_map); + + OSAL_IRQ_EPILOGUE(); +} +#endif /* KINETIS_EXT_PORTE_WIDTH > 0 */ + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +/** + * @brief Low level EXT driver initialization. + * + * @notapi + */ +void ext_lld_init(void) { + + /* Driver initialization.*/ + extObjectInit(&EXTD1); +} + +/** + * @brief Configures and activates the EXT peripheral. + * + * @param[in] extp pointer to the @p EXTDriver object + * + * @notapi + */ +void ext_lld_start(EXTDriver *extp) { + expchannel_t channel; + + if (extp->state == EXT_STOP) + ext_lld_exti_irq_enable(); + + /* Configuration of automatic channels.*/ + for (channel = 0; channel < EXT_MAX_CHANNELS; channel++) { + + uint32_t mode = extp->config->channels[channel].mode; + PORT_TypeDef *port = extp->config->channels[channel].port; + uint32_t pin = extp->config->channels[channel].pin; + + /* Initialize the channel map */ +#if KINETIS_EXT_PORTA_WIDTH > 0 + if (port == PORTA) + porta_channel_map[pin] = channel; + else +#endif +#if KINETIS_EXT_PORTB_WIDTH > 0 + if (port == PORTB) + portb_channel_map[pin] = channel; + else +#endif +#if KINETIS_EXT_PORTC_WIDTH > 0 + if (port == PORTC) + portc_channel_map[pin] = channel; + else +#endif +#if KINETIS_EXT_PORTD_WIDTH > 0 + if (port == PORTD) + portd_channel_map[pin] = channel; + else +#endif +#if KINETIS_EXT_PORTE_WIDTH > 0 + if (port == PORTE) + porte_channel_map[pin] = channel; + else +#endif + {} + + if (mode & EXT_CH_MODE_AUTOSTART) + ext_lld_channel_enable(extp, channel); + else if (port != NULL) + ext_lld_channel_disable(extp, channel); + } +} + +/** + * @brief Deactivates the EXT peripheral. + * + * @param[in] extp pointer to the @p EXTDriver object + * + * @notapi + */ +void ext_lld_stop(EXTDriver *extp) { + + if (extp->state == EXT_ACTIVE) + ext_lld_exti_irq_disable(); +} + +/** + * @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) { + + uint32_t irqc; + uint32_t mode = extp->config->channels[channel].mode; + if (mode & EXT_CH_MODE_RISING_EDGE) + irqc = PCR_IRQC_RISING_EDGE; + else if (extp->config->channels[channel].mode & EXT_CH_MODE_FALLING_EDGE) + irqc = PCR_IRQC_FALLING_EDGE; + else if (extp->config->channels[channel].mode & EXT_CH_MODE_BOTH_EDGES) + irqc = PCR_IRQC_EITHER_EDGE; + else + irqc = PCR_IRQC_DISABLED; + + PORT_TypeDef *port = extp->config->channels[channel].port; + uint32_t pin = extp->config->channels[channel].pin; + + uint32_t pcr = port->PCR[pin]; + + /* Clear all the IRQC bits */ + pcr &= ~PORTx_PCRn_IRQC_MASK; + /* Set the required IRQC bits */ + pcr |= PORTx_PCRn_IRQC(irqc); + + port->PCR[pin] = pcr; +} + +/** + * @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) { + + PORT_TypeDef *port = extp->config->channels[channel].port; + uint32_t pin = extp->config->channels[channel].pin; + port->PCR[pin] |= PORTx_PCRn_IRQC(PCR_IRQC_DISABLED); +} + +#endif /* HAL_USE_EXT */ + +/** @} */ diff --git a/os/hal/ports/KINETIS/LLD/ext_lld.h b/os/hal/ports/KINETIS/LLD/ext_lld.h new file mode 100644 index 0000000..465bb89 --- /dev/null +++ b/os/hal/ports/KINETIS/LLD/ext_lld.h @@ -0,0 +1,188 @@ +/* + ChibiOS - Copyright (C) 2014 Derek Mulcahy + + 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 KINETIS/LLD/ext_lld.h + * @brief KINETIS 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 Number of EXT channels required. + */ +#define EXT_MAX_CHANNELS KINETIS_EXTI_NUM_CHANNELS + +/** + * @name KINETIS-specific EXT channel modes + * @{ + */ +/** @} */ + +/*===========================================================================*/ +/* Driver pre-compile time settings. */ +/*===========================================================================*/ + +/** + * @name Configuration options + * @{ + */ +/** + * @brief PORTA interrupt priority level setting. + */ +#if !defined(KINETIS_EXT_PORTA_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define KINETIS_EXT_PORTA_IRQ_PRIORITY 3 +#endif + +/** + * @brief PORTB interrupt priority level setting. + */ +#if !defined(KINETIS_EXT_PORTB_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define KINETIS_EXT_PORTB_IRQ_PRIORITY 3 +#endif + +/** + * @brief PORTC interrupt priority level setting. + */ +#if !defined(KINETIS_EXT_PORTC_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define KINETIS_EXT_PORTC_IRQ_PRIORITY 3 +#endif + +/** + * @brief PORTD interrupt priority level setting. + */ +#if !defined(KINETIS_EXT_PORTD_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define KINETIS_EXT_PORTD_IRQ_PRIORITY 3 +#endif + +/** + * @brief PORTE interrupt priority level setting. + */ +#if !defined(KINETIS_EXT_PORTE_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define KINETIS_EXT_PORTE_IRQ_PRIORITY 3 +#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. + */ + uint32_t mode; + /** + * @brief Channel callback. + */ + extcallback_t cb; + + /** + * @brief Port. + */ + PORT_TypeDef *port; + + /** + * @brief Pin. + */ + uint32_t pin; +} 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.*/ +}; + +/*===========================================================================*/ +/* Driver macros. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +#if !defined(__DOXYGEN__) +extern EXTDriver EXTD1; +#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/ports/KINETIS/LLD/i2c_lld.c b/os/hal/ports/KINETIS/LLD/i2c_lld.c new file mode 100644 index 0000000..4e42d16 --- /dev/null +++ b/os/hal/ports/KINETIS/LLD/i2c_lld.c @@ -0,0 +1,415 @@ +/* + ChibiOS - Copyright (C) 2014-2015 Fabio Utzig + + 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 KINETIS/i2c_lld.c + * @brief KINETIS I2C subsystem low level driver source. + * + * @addtogroup I2C + * @{ + */ + +#include "osal.h" +#include "hal.h" + +#if HAL_USE_I2C || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver local definitions. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver exported variables. */ +/*===========================================================================*/ + +/** + * @brief I2C0 driver identifier. + */ +#if KINETIS_I2C_USE_I2C0 || defined(__DOXYGEN__) +I2CDriver I2CD1; +#endif + +/** + * @brief I2C1 driver identifier. + */ +#if KINETIS_I2C_USE_I2C1 || defined(__DOXYGEN__) +I2CDriver I2CD2; +#endif + +/*===========================================================================*/ +/* Driver local variables and types. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver local functions. */ +/*===========================================================================*/ + +void config_frequency(I2CDriver *i2cp) { + + /* Each index in the table corresponds to a a frequency + * divider used to generate the SCL clock from the main + * system clock. + */ + uint16_t icr_table[] = { + /* 0x00 - 0x0F */ + 20,22,24,26,28,30,34,40,28,32,36,40,44,48,56,68, + /* 0x10 - 0x1F */ + 48,56,64,72,80,88,104,128,80,96,112,128,144,160,192,240, + /* 0x20 - 0x2F */ + 160,192,224,256,288,320,384,480,320,384,448,512,576,640,768,960, + /* 0x30 - 0x3F */ + 640,768,896,1024,1152,1280,1536,1920,1280,1536,1792,2048,2304,2560,3072,3840, + }; + + int length = sizeof(icr_table) / sizeof(icr_table[0]); + uint16_t divisor; + uint8_t i = 0, index = 0; + uint16_t best, diff; + + if (i2cp->config != NULL) + divisor = KINETIS_SYSCLK_FREQUENCY / i2cp->config->clock; + else + divisor = KINETIS_SYSCLK_FREQUENCY / 100000; + + best = ~0; + index = 0; + /* Tries to find the SCL clock which is the closest + * approximation to the clock passed in config. To + * stay on the safe side, only values that generate + * lower frequency are used. + */ + for (i = 0; i < length; i++) { + if (icr_table[i] >= divisor) { + diff = icr_table[i] - divisor; + if (diff < best) { + best = diff; + index = i; + } + } + } + + i2cp->i2c->F = index; +} + +/** + * @brief Common IRQ handler. + * @note Tries hard to clear all the pending interrupt sources, we don't + * want to go through the whole ISR and have another interrupt soon + * after. + * + * @param[in] i2cp pointer to an I2CDriver + */ +static void serve_interrupt(I2CDriver *i2cp) { + + I2C_TypeDef *i2c = i2cp->i2c; + intstate_t state = i2cp->intstate; + + if (i2c->S & I2Cx_S_ARBL) { + + i2cp->errors |= I2C_ARBITRATION_LOST; + i2c->S |= I2Cx_S_ARBL; + + } else if (state == STATE_SEND) { + + if (i2c->S & I2Cx_S_RXAK) + i2cp->errors |= I2C_ACK_FAILURE; + else if (i2cp->txbuf != NULL && i2cp->txidx < i2cp->txbytes) + i2c->D = i2cp->txbuf[i2cp->txidx++]; + else + i2cp->intstate = STATE_STOP; + + } else if (state == STATE_DUMMY) { + + if (i2c->S & I2Cx_S_RXAK) + i2cp->errors |= I2C_ACK_FAILURE; + else { + i2c->C1 &= ~I2Cx_C1_TX; + + if (i2cp->rxbytes > 1) + i2c->C1 &= ~I2Cx_C1_TXAK; + else + i2c->C1 |= I2Cx_C1_TXAK; + (void) i2c->D; + i2cp->intstate = STATE_RECV; + } + + } else if (state == STATE_RECV) { + + if (i2cp->rxbytes > 1) { + if (i2cp->rxidx == (i2cp->rxbytes - 2)) + i2c->C1 |= I2Cx_C1_TXAK; + else + i2c->C1 &= ~I2Cx_C1_TXAK; + } + + if (i2cp->rxidx == i2cp->rxbytes - 1) + i2c->C1 &= ~(I2Cx_C1_TX | I2Cx_C1_MST); + + i2cp->rxbuf[i2cp->rxidx++] = i2c->D; + + if (i2cp->rxidx == i2cp->rxbytes) + i2cp->intstate = STATE_STOP; + } + + /* Reset interrupt flag */ + i2c->S |= I2Cx_S_IICIF; + + if (i2cp->errors != I2C_NO_ERROR) + _i2c_wakeup_error_isr(i2cp); + + if (i2cp->intstate == STATE_STOP) + _i2c_wakeup_isr(i2cp); +} + +/*===========================================================================*/ +/* Driver interrupt handlers. */ +/*===========================================================================*/ + +#if KINETIS_I2C_USE_I2C0 || defined(__DOXYGEN__) + +OSAL_IRQ_HANDLER(KINETIS_I2C0_IRQ_VECTOR) { + + OSAL_IRQ_PROLOGUE(); + serve_interrupt(&I2CD1); + OSAL_IRQ_EPILOGUE(); +} + +#endif + +#if KINETIS_I2C_USE_I2C1 || defined(__DOXYGEN__) + +/* FIXME: KL2x has I2C1 on Vector64; K2x don't have I2C1! */ +OSAL_IRQ_HANDLER(Vector64) { + + OSAL_IRQ_PROLOGUE(); + serve_interrupt(&I2CD2); + OSAL_IRQ_EPILOGUE(); +} + +#endif + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +/** + * @brief Low level I2C driver initialization. + * + * @notapi + */ +void i2c_lld_init(void) { + +#if KINETIS_I2C_USE_I2C0 + i2cObjectInit(&I2CD1); + I2CD1.thread = NULL; + I2CD1.i2c = I2C0; +#endif + +#if KINETIS_I2C_USE_I2C1 + i2cObjectInit(&I2CD2); + I2CD2.thread = NULL; + I2CD2.i2c = I2C1; +#endif + +} + +/** + * @brief Configures and activates the I2C peripheral. + * + * @param[in] i2cp pointer to the @p I2CDriver object + * + * @notapi + */ +void i2c_lld_start(I2CDriver *i2cp) { + + if (i2cp->state == I2C_STOP) { + + /* TODO: + * The PORT must be enabled somewhere. The PIN multiplexer + * will map the I2C functionality to some PORT which must + * than be enabled. The easier way is enabling all PORTs at + * startup, which is currently being done in __early_init. + */ + +#if KINETIS_I2C_USE_I2C0 + if (&I2CD1 == i2cp) { + SIM->SCGC4 |= SIM_SCGC4_I2C0; + nvicEnableVector(I2C0_IRQn, KINETIS_I2C_I2C0_PRIORITY); + } +#endif + +#if KINETIS_I2C_USE_I2C1 + if (&I2CD2 == i2cp) { + SIM->SCGC4 |= SIM_SCGC4_I2C1; + nvicEnableVector(I2C1_IRQn, KINETIS_I2C_I2C1_PRIORITY); + } +#endif + + } + + config_frequency(i2cp); + i2cp->i2c->C1 |= I2Cx_C1_IICEN | I2Cx_C1_IICIE; + i2cp->intstate = STATE_STOP; +} + +/** + * @brief Deactivates the I2C peripheral. + * + * @param[in] i2cp pointer to the @p I2CDriver object + * + * @notapi + */ +void i2c_lld_stop(I2CDriver *i2cp) { + + if (i2cp->state != I2C_STOP) { + + i2cp->i2c->C1 &= ~(I2Cx_C1_IICEN | I2Cx_C1_IICIE); + +#if KINETIS_I2C_USE_I2C0 + if (&I2CD1 == i2cp) { + SIM->SCGC4 &= ~SIM_SCGC4_I2C0; + nvicDisableVector(I2C0_IRQn); + } +#endif + +#if KINETIS_I2C_USE_I2C1 + if (&I2CD2 == i2cp) { + SIM->SCGC4 &= ~SIM_SCGC4_I2C1; + nvicDisableVector(I2C1_IRQn); + } +#endif + + } +} + +static inline msg_t _i2c_txrx_timeout(I2CDriver *i2cp, i2caddr_t addr, + const uint8_t *txbuf, size_t txbytes, + uint8_t *rxbuf, size_t rxbytes, + systime_t timeout) { + + (void)timeout; + msg_t msg; + + uint8_t op = (i2cp->intstate == STATE_SEND) ? 0 : 1; + + i2cp->errors = I2C_NO_ERROR; + i2cp->addr = addr; + + i2cp->txbuf = txbuf; + i2cp->txbytes = txbytes; + i2cp->txidx = 0; + + i2cp->rxbuf = rxbuf; + i2cp->rxbytes = rxbytes; + i2cp->rxidx = 0; + + /* send START */ + i2cp->i2c->C1 |= I2Cx_C1_MST; + i2cp->i2c->C1 |= I2Cx_C1_TX; + + /* FIXME: should not use busy waiting! */ + while (!(i2cp->i2c->S & I2Cx_S_BUSY)); + + i2cp->i2c->D = addr << 1 | op; + + msg = osalThreadSuspendTimeoutS(&i2cp->thread, TIME_INFINITE); + + /* FIXME */ + //if (i2cp->i2c->S & I2Cx_S_RXAK) + // i2cp->errors |= I2C_ACK_FAILURE; + + if (msg == MSG_OK && txbuf != NULL && rxbuf != NULL) { + i2cp->i2c->C1 |= I2Cx_C1_RSTA; + /* FIXME */ + while (!(i2cp->i2c->S & I2Cx_S_BUSY)); + + i2cp->intstate = STATE_DUMMY; + i2cp->i2c->D = i2cp->addr << 1 | 1; + + msg = osalThreadSuspendTimeoutS(&i2cp->thread, TIME_INFINITE); + } + + i2cp->i2c->C1 &= ~(I2Cx_C1_TX | I2Cx_C1_MST); + /* FIXME */ + while (i2cp->i2c->S & I2Cx_S_BUSY); + + return msg; +} + +/** + * @brief Receives data via the I2C bus as master. + * + * @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 MSG_OK if the function succeeded. + * @retval MSG_RESET if one or more I2C errors occurred, the errors can + * be retrieved using @p i2cGetErrors(). + * @retval MSG_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) { + + i2cp->intstate = STATE_DUMMY; + return _i2c_txrx_timeout(i2cp, addr, NULL, 0, rxbuf, rxbytes, timeout); +} + +/** + * @brief Transmits data via the I2C bus as master. + * + * @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 MSG_OK if the function succeeded. + * @retval MSG_RESET if one or more I2C errors occurred, the errors can + * be retrieved using @p i2cGetErrors(). + * @retval MSG_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) { + + i2cp->intstate = STATE_SEND; + return _i2c_txrx_timeout(i2cp, addr, txbuf, txbytes, rxbuf, rxbytes, timeout); +} + +#endif /* HAL_USE_I2C */ + +/** @} */ diff --git a/os/hal/ports/KINETIS/LLD/i2c_lld.h b/os/hal/ports/KINETIS/LLD/i2c_lld.h new file mode 100644 index 0000000..11de3ae --- /dev/null +++ b/os/hal/ports/KINETIS/LLD/i2c_lld.h @@ -0,0 +1,208 @@ +/* + ChibiOS - Copyright (C) 2014-2015 Fabio Utzig + + 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 KINETIS/i2c_lld.h + * @brief KINETIS 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 STATE_STOP 0x00 +#define STATE_SEND 0x01 +#define STATE_RECV 0x02 +#define STATE_DUMMY 0x03 + +/*===========================================================================*/ +/* Driver pre-compile time settings. */ +/*===========================================================================*/ + +/** + * @name Configuration options + * @{ + */ +/** + * @brief I2C0 driver enable switch. + * @details If set to @p TRUE the support for I2C0 is included. + * @note The default is @p FALSE. + */ +#if !defined(KINETIS_I2C_USE_I2C0) || defined(__DOXYGEN__) +#define KINETIS_I2C_USE_I2C0 FALSE +#endif + +/** + * @brief I2C1 driver enable switch. + * @details If set to @p TRUE the support for I2C1 is included. + * @note The default is @p FALSE. + */ +#if !defined(KINETIS_I2C_USE_I2C1) || defined(__DOXYGEN__) +#define KINETIS_I2C_USE_I2C1 FALSE +#endif +/** @} */ + +/*===========================================================================*/ +/* Derived constants and error checks. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver data structures and types. */ +/*===========================================================================*/ + +/* @brief Type representing I2C address. */ +typedef uint8_t i2caddr_t; + +/* @brief Type of I2C Driver condition flags. */ +typedef uint32_t i2cflags_t; + +/* @brief Type used to control the ISR state machine. */ +typedef uint8_t intstate_t; + +/** + * @brief Driver configuration structure. + * @note Implementations may extend this structure to contain more, + * architecture dependent, fields. + */ + +/** + * @brief Driver configuration structure. + */ +typedef struct { + + /* @brief Clock to be used for the I2C bus. */ + uint32_t clock; + +} 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_CFG_USE_MUTEXES || defined(__DOXYGEN__) + /** + * @brief Mutex protecting the bus. + */ + mutex_t mutex; +#elif CH_CFG_USE_SEMAPHORES + semaphore_t semaphore; +#endif +#endif /* I2C_USE_MUTUAL_EXCLUSION */ +#if defined(I2C_DRIVER_EXT_FIELDS) + I2C_DRIVER_EXT_FIELDS +#endif + /* @brief Thread waiting for I/O completion. */ + thread_reference_t thread; + /* @brief Current slave address without R/W bit. */ + i2caddr_t addr; + + /* End of the mandatory fields.*/ + + /* @brief Pointer to the buffer with data to send. */ + const uint8_t *txbuf; + /* @brief Number of bytes of data to send. */ + size_t txbytes; + /* @brief Current index in buffer when sending data. */ + size_t txidx; + /* @brief Pointer to the buffer to put received data. */ + uint8_t *rxbuf; + /* @brief Number of bytes of data to receive. */ + size_t rxbytes; + /* @brief Current index in buffer when receiving data. */ + size_t rxidx; + /* @brief Tracks current ISR state. */ + intstate_t intstate; + /* @brief Low-level register access. */ + 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__) + +#if KINETIS_I2C_USE_I2C0 +extern I2CDriver I2CD1; +#endif + +#if KINETIS_I2C_USE_I2C1 +extern I2CDriver I2CD2; +#endif + +#endif + +#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 /* HAL_USE_I2C */ + +#endif /* _I2C_LLD_H_ */ + +/** @} */ -- cgit v1.2.3