copy LLD from KINETIS HAL (no changes yet)

This commit is contained in:
Michael Stapelberg 2020-06-05 23:45:22 +02:00
parent b4b3579c86
commit 480534795c
19 changed files with 3995 additions and 0 deletions

View File

@ -0,0 +1,9 @@
ifeq ($(USE_SMART_BUILD),yes)
ifneq ($(findstring HAL_USE_PAL TRUE,$(HALCONF)),)
PLATFORMSRC_CONTRIB += ${CHIBIOS_CONTRIB}/os/hal/ports/KINETIS/LLD/GPIOv1/hal_pal_lld.c
endif
else
PLATFORMSRC_CONTRIB += ${CHIBIOS_CONTRIB}/os/hal/ports/KINETIS/LLD/GPIOv1/hal_pal_lld.c
endif
PLATFORMINC_CONTRIB += ${CHIBIOS_CONTRIB}/os/hal/ports/KINETIS/LLD/GPIOv1

View File

@ -0,0 +1,245 @@
/*
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 GPIOv1/hal_pal_lld.c
* @brief PAL subsystem low level driver.
*
* @addtogroup PAL
* @{
*/
#include "osal.h"
#include "hal.h"
#if HAL_USE_PAL || defined(__DOXYGEN__)
/*===========================================================================*/
/* Driver local definitions. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver exported variables. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver local variables and types. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver local functions. */
/*===========================================================================*/
/**
* @brief Reads a logical state from an I/O pad.
* @note The @ref PAL provides a default software implementation of this
* functionality, implement this function if can optimize it by using
* special hardware functionalities or special coding.
*
* @param[in] port port identifier
* @param[in] pad pad number within the port
* @return The logical state.
* @retval PAL_LOW low logical state.
* @retval PAL_HIGH high logical state.
*
* @notapi
*/
uint8_t _pal_lld_readpad(ioportid_t port,
uint8_t pad) {
return (port->PDIR & ((uint32_t) 1 << pad)) ? PAL_HIGH : PAL_LOW;
}
/**
* @brief Writes a logical state on an output pad.
* @note This function is not meant to be invoked directly by the
* application code.
* @note The @ref PAL provides a default software implementation of this
* functionality, implement this function if can optimize it by using
* special hardware functionalities or special coding.
*
* @param[in] port port identifier
* @param[in] pad pad number within the port
* @param[in] bit logical value, the value must be @p PAL_LOW or
* @p PAL_HIGH
*
* @notapi
*/
void _pal_lld_writepad(ioportid_t port,
uint8_t pad,
uint8_t bit) {
if (bit == PAL_HIGH)
port->PDOR |= ((uint32_t) 1 << pad);
else
port->PDOR &= ~((uint32_t) 1 << pad);
}
/**
* @brief Pad mode setup.
* @details This function programs a pad with the specified mode.
* @note The @ref PAL provides a default software implementation of this
* functionality, implement this function if can optimize it by using
* special hardware functionalities or special coding.
* @note Programming an unknown or unsupported mode is silently ignored.
*
* @param[in] port port identifier
* @param[in] pad pad number within the port
* @param[in] mode pad mode
*
* @notapi
*/
void _pal_lld_setpadmode(ioportid_t port,
uint8_t pad,
iomode_t mode) {
PORT_TypeDef *portcfg = NULL;
osalDbgAssert(pad < PADS_PER_PORT, "pal_lld_setpadmode() #1, invalid pad");
if (mode == PAL_MODE_OUTPUT_PUSHPULL)
port->PDDR |= ((uint32_t) 1 << pad);
else
port->PDDR &= ~((uint32_t) 1 << pad);
if (port == IOPORT1)
portcfg = PORTA;
else if (port == IOPORT2)
portcfg = PORTB;
else if (port == IOPORT3)
portcfg = PORTC;
else if (port == IOPORT4)
portcfg = PORTD;
else if (port == IOPORT5)
portcfg = PORTE;
osalDbgAssert(portcfg != NULL, "pal_lld_setpadmode() #2, invalid port");
switch (mode) {
case PAL_MODE_RESET:
case PAL_MODE_INPUT:
case PAL_MODE_OUTPUT_PUSHPULL:
portcfg->PCR[pad] = PIN_MUX_ALTERNATIVE(1);
break;
#if KINETIS_GPIO_HAS_OPENDRAIN
case PAL_MODE_OUTPUT_OPENDRAIN:
portcfg->PCR[pad] = PIN_MUX_ALTERNATIVE(1) |
PORTx_PCRn_ODE;
break;
#else
#undef PAL_MODE_OUTPUT_OPENDRAIN
#endif
case PAL_MODE_INPUT_PULLUP:
portcfg->PCR[pad] = PIN_MUX_ALTERNATIVE(1) |
PORTx_PCRn_PE |
PORTx_PCRn_PS;
break;
case PAL_MODE_INPUT_PULLDOWN:
portcfg->PCR[pad] = PIN_MUX_ALTERNATIVE(1) |
PORTx_PCRn_PE;
break;
case PAL_MODE_UNCONNECTED:
case PAL_MODE_INPUT_ANALOG:
portcfg->PCR[pad] = PIN_MUX_ALTERNATIVE(0);
break;
case PAL_MODE_ALTERNATIVE_1:
portcfg->PCR[pad] = PIN_MUX_ALTERNATIVE(1);
break;
case PAL_MODE_ALTERNATIVE_2:
portcfg->PCR[pad] = PIN_MUX_ALTERNATIVE(2);
break;
case PAL_MODE_ALTERNATIVE_3:
portcfg->PCR[pad] = PIN_MUX_ALTERNATIVE(3);
break;
case PAL_MODE_ALTERNATIVE_4:
portcfg->PCR[pad] = PIN_MUX_ALTERNATIVE(4);
break;
case PAL_MODE_ALTERNATIVE_5:
portcfg->PCR[pad] = PIN_MUX_ALTERNATIVE(5);
break;
case PAL_MODE_ALTERNATIVE_6:
portcfg->PCR[pad] = PIN_MUX_ALTERNATIVE(6);
break;
case PAL_MODE_ALTERNATIVE_7:
portcfg->PCR[pad] = PIN_MUX_ALTERNATIVE(7);
break;
}
}
/*===========================================================================*/
/* Driver interrupt handlers. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver exported functions. */
/*===========================================================================*/
/**
* @brief Kinetis I/O ports configuration.
* @details Ports A-E clocks enabled.
*
* @param[in] config the Kinetis ports configuration
*
* @notapi
*/
void _pal_lld_init(const PALConfig *config) {
int i, j;
/* Enable clocking on all Ports */
SIM->SCGC5 |= SIM_SCGC5_PORTA |
SIM_SCGC5_PORTB |
SIM_SCGC5_PORTC |
SIM_SCGC5_PORTD |
SIM_SCGC5_PORTE;
/* Initial PORT and GPIO setup */
for (i = 0; i < TOTAL_PORTS; i++) {
for (j = 0; j < PADS_PER_PORT; j++) {
pal_lld_setpadmode(config->ports[i].port,
j,
config->ports[i].pads[j]);
}
}
}
/**
* @brief Pads mode setup.
* @details This function programs a pads group belonging to the same port
* with the specified mode.
*
* @param[in] port the port identifier
* @param[in] mask the group mask
* @param[in] mode the mode
*
* @notapi
*/
void _pal_lld_setgroupmode(ioportid_t port,
ioportmask_t mask,
iomode_t mode) {
int i;
(void)mask;
for (i = 0; i < PADS_PER_PORT; i++) {
pal_lld_setpadmode(port, i, mode);
}
}
#endif /* HAL_USE_PAL */
/** @} */

View File

@ -0,0 +1,428 @@
/*
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 GPIOv1/hal_pal_lld.h
* @brief PAL subsystem low level driver header.
*
* @addtogroup PAL
* @{
*/
#ifndef HAL_PAL_LLD_H_
#define HAL_PAL_LLD_H_
#if HAL_USE_PAL || defined(__DOXYGEN__)
/*===========================================================================*/
/* Unsupported modes and specific modes */
/*===========================================================================*/
#define PAL_MODE_ALTERNATIVE_1 0x10
#define PAL_MODE_ALTERNATIVE_2 0x11
#define PAL_MODE_ALTERNATIVE_3 0x12
#define PAL_MODE_ALTERNATIVE_4 0x13
#define PAL_MODE_ALTERNATIVE_5 0x14
#define PAL_MODE_ALTERNATIVE_6 0x15
#define PAL_MODE_ALTERNATIVE_7 0x16
#define PIN_MUX_ALTERNATIVE(x) PORTx_PCRn_MUX(x)
/*===========================================================================*/
/* I/O Ports Types and constants. */
/*===========================================================================*/
#define TOTAL_PORTS 5
#define PADS_PER_PORT 32
/**
* @brief Width, in bits, of an I/O port.
*/
#define PAL_IOPORTS_WIDTH 32
/**
* @brief Whole port mask.
* @brief This macro specifies all the valid bits into a port.
*/
#define PAL_WHOLE_PORT ((ioportmask_t)0xFFFFFFFF)
/**
* @brief Digital I/O port sized unsigned type.
*/
typedef uint32_t ioportmask_t;
/**
* @brief Digital I/O modes.
*/
typedef uint32_t iomode_t;
/**
* @brief Type of an I/O line.
*/
typedef uint32_t ioline_t;
/**
* @brief Port Identifier.
* @details This type can be a scalar or some kind of pointer, do not make
* any assumption about it, use the provided macros when populating
* variables of this type.
*/
typedef GPIO_TypeDef *ioportid_t;
/**
* @brief Type of an pad identifier.
*/
typedef uint32_t iopadid_t;
/**
* @brief Port Configuration.
* @details This structure stores the configuration parameters of all pads
* belonging to a port.
*/
typedef struct {
ioportid_t port;
iomode_t pads[PADS_PER_PORT];
} PortConfig;
/**
* @brief Generic I/O ports static initializer.
* @details An instance of this structure must be passed to @p palInit() at
* system startup time in order to initialized the digital I/O
* subsystem. This represents only the initial setup, specific pads
* or whole ports can be reprogrammed at later time.
* @note Implementations may extend this structure to contain more,
* architecture dependent, fields.
*/
typedef struct {
PortConfig ports[TOTAL_PORTS];
} PALConfig;
/*===========================================================================*/
/* I/O Ports Identifiers. */
/*===========================================================================*/
/**
* @brief GPIO port A identifier.
*/
#define IOPORT1 GPIOA
/**
* @brief GPIO port B identifier.
*/
#define IOPORT2 GPIOB
/**
* @brief GPIO port C identifier.
*/
#define IOPORT3 GPIOC
/**
* @brief GPIO port D identifier.
*/
#define IOPORT4 GPIOD
/**
* @brief GPIO port E identifier.
*/
#define IOPORT5 GPIOE
/**
* @name Line handling macros
* @{
*/
/**
* @brief Forms a line identifier.
* @details A port/pad pair are encoded into an @p ioline_t type. The encoding
* of this type is platform-dependent.
* @note In this driver the pad number is encoded in the byte of the GPIO
* address that's zero on all Kinetis devices.
*/
#define PAL_LINE(port, pad) \
((ioline_t)((uint32_t)(port) | ((uint32_t)(pad)<<20)))
/**
* @brief Decodes a port identifier from a line identifier.
*/
#define PAL_PORT(line) \
((GPIO_TypeDef *)(((uint32_t)(line)) & 0xF00FFFFFU))
/**
* @brief Decodes a pad identifier from a line identifier.
*/
#define PAL_PAD(line) \
((uint32_t)((uint32_t)(line) & 0x0FF00000U)>>20)
/**
* @brief Value identifying an invalid line.
*/
#define PAL_NOLINE 0U
/** @} */
/*===========================================================================*/
/* Implementation, some of the following macros could be implemented as */
/* functions, if so please put them in pal_lld.c. */
/*===========================================================================*/
/**
* @brief Low level PAL subsystem initialization.
*
* @param[in] config architecture-dependent ports configuration
*
* @notapi
*/
#define pal_lld_init(config) _pal_lld_init(config)
/**
* @brief Reads the physical I/O port states.
*
* @param[in] port port identifier
* @return The port bits.
*
* @notapi
*/
#define pal_lld_readport(port) \
(port)->PDIR
/**
* @brief Reads the output latch.
* @details The purpose of this function is to read back the latched output
* value.
*
* @param[in] port port identifier
* @return The latched logical states.
*
* @notapi
*/
#define pal_lld_readlatch(port) \
(port)->PDOR
/**
* @brief Writes a bits mask on a I/O port.
*
* @param[in] port port identifier
* @param[in] bits bits to be written on the specified port
*
* @notapi
*/
#define pal_lld_writeport(port, bits) \
(port)->PDOR = (bits)
/**
* @brief Sets a bits mask on a I/O port.
* @note The @ref PAL provides a default software implementation of this
* functionality, implement this function if can optimize it by using
* special hardware functionalities or special coding.
*
* @param[in] port port identifier
* @param[in] bits bits to be ORed on the specified port
*
* @notapi
*/
#define pal_lld_setport(port, bits) \
(port)->PSOR = (bits)
/**
* @brief Clears a bits mask on a I/O port.
* @note The @ref PAL provides a default software implementation of this
* functionality, implement this function if can optimize it by using
* special hardware functionalities or special coding.
*
* @param[in] port port identifier
* @param[in] bits bits to be cleared on the specified port
*
* @notapi
*/
#define pal_lld_clearport(port, bits) \
(port)->PCOR = (bits)
/**
* @brief Toggles a bits mask on a I/O port.
* @note The @ref PAL provides a default software implementation of this
* functionality, implement this function if can optimize it by using
* special hardware functionalities or special coding.
*
* @param[in] port port identifier
* @param[in] bits bits to be toggled on the specified port
*
* @notapi
*/
#define pal_lld_toggleport(port, bits) \
(port)->PTOR = (bits)
/**
* @brief Reads a group of bits.
* @note The @ref PAL provides a default software implementation of this
* functionality, implement this function if can optimize it by using
* special hardware functionalities or special coding.
*
* @param[in] port port identifier
* @param[in] mask group mask
* @param[in] offset group bit offset within the port
* @return The group logical states.
*
* @notapi
*/
#define pal_lld_readgroup(port, mask, offset) 0
/**
* @brief Writes a group of bits.
* @note The @ref PAL provides a default software implementation of this
* functionality, implement this function if can optimize it by using
* special hardware functionalities or special coding.
*
* @param[in] port port identifier
* @param[in] mask group mask
* @param[in] offset group bit offset within the port
* @param[in] bits bits to be written. Values exceeding the group width
* are masked.
*
* @notapi
*/
#define pal_lld_writegroup(port, mask, offset, bits) (void)bits
/**
* @brief Pads group mode setup.
* @details This function programs a pads group belonging to the same port
* with the specified mode.
* @note Programming an unknown or unsupported mode is silently ignored.
*
* @param[in] port port identifier
* @param[in] mask group mask
* @param[in] offset group bit offset within the port
* @param[in] mode group mode
*
* @notapi
*/
#define pal_lld_setgroupmode(port, mask, offset, mode) \
_pal_lld_setgroupmode(port, mask << offset, mode)
/**
* @brief Reads a logical state from an I/O pad.
* @note The @ref PAL provides a default software implementation of this
* functionality, implement this function if can optimize it by using
* special hardware functionalities or special coding.
*
* @param[in] port port identifier
* @param[in] pad pad number within the port
* @return The logical state.
* @retval PAL_LOW low logical state.
* @retval PAL_HIGH high logical state.
*
* @notapi
*/
#define pal_lld_readpad(port, pad) _pal_lld_readpad(port, pad)
/**
* @brief Writes a logical state on an output pad.
* @note This function is not meant to be invoked directly by the
* application code.
* @note The @ref PAL provides a default software implementation of this
* functionality, implement this function if can optimize it by using
* special hardware functionalities or special coding.
*
* @param[in] port port identifier
* @param[in] pad pad number within the port
* @param[in] bit logical value, the value must be @p PAL_LOW or
* @p PAL_HIGH
*
* @notapi
*/
#define pal_lld_writepad(port, pad, bit) _pal_lld_writepad(port, pad, bit)
/**
* @brief Sets a pad logical state to @p PAL_HIGH.
* @note The @ref PAL provides a default software implementation of this
* functionality, implement this function if can optimize it by using
* special hardware functionalities or special coding.
*
* @param[in] port port identifier
* @param[in] pad pad number within the port
*
* @notapi
*/
#define pal_lld_setpad(port, pad) (port)->PSOR = ((uint32_t) 1 << (pad))
/**
* @brief Clears a pad logical state to @p PAL_LOW.
* @note The @ref PAL provides a default software implementation of this
* functionality, implement this function if can optimize it by using
* special hardware functionalities or special coding.
*
* @param[in] port port identifier
* @param[in] pad pad number within the port
*
* @notapi
*/
#define pal_lld_clearpad(port, pad) (port)->PCOR = ((uint32_t) 1 << (pad))
/**
* @brief Toggles a pad logical state.
* @note The @ref PAL provides a default software implementation of this
* functionality, implement this function if can optimize it by using
* special hardware functionalities or special coding.
*
* @param[in] port port identifier
* @param[in] pad pad number within the port
*
* @notapi
*/
#define pal_lld_togglepad(port, pad) (port)->PTOR = ((uint32_t) 1 << (pad))
/**
* @brief Pad mode setup.
* @details This function programs a pad with the specified mode.
* @note The @ref PAL provides a default software implementation of this
* functionality, implement this function if can optimize it by using
* special hardware functionalities or special coding.
* @note Programming an unknown or unsupported mode is silently ignored.
*
* @param[in] port port identifier
* @param[in] pad pad number within the port
* @param[in] mode pad mode
*
* @notapi
*/
#define pal_lld_setpadmode(port, pad, mode) \
_pal_lld_setpadmode(port, pad, mode)
#if !defined(__DOXYGEN__)
extern const PALConfig pal_default_config;
#endif
#ifdef __cplusplus
extern "C" {
#endif
void _pal_lld_init(const PALConfig *config);
void _pal_lld_setgroupmode(ioportid_t port,
ioportmask_t mask,
iomode_t mode);
void _pal_lld_setpadmode(ioportid_t port,
uint8_t pad,
iomode_t mode);
uint8_t _pal_lld_readpad(ioportid_t port,
uint8_t pad);
void _pal_lld_writepad(ioportid_t port,
uint8_t pad,
uint8_t bit);
#ifdef __cplusplus
}
#endif
#endif /* HAL_USE_PAL */
#endif /* HAL_PAL_LLD_H_ */
/** @} */

View File

@ -0,0 +1,9 @@
ifeq ($(USE_SMART_BUILD),yes)
ifneq ($(findstring HAL_USE_GPT TRUE,$(HALCONF)),)
PLATFORMSRC_CONTRIB += ${CHIBIOS_CONTRIB}/os/hal/ports/KINETIS/LLD/PITv1/hal_gpt_lld.c
endif
else
PLATFORMSRC_CONTRIB += ${CHIBIOS_CONTRIB}/os/hal/ports/KINETIS/LLD/PITv1/hal_gpt_lld.c
endif
PLATFORMINC_CONTRIB += ${CHIBIOS_CONTRIB}/os/hal/ports/KINETIS/LLD/PITv1

View File

@ -0,0 +1,391 @@
/*
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 PITv1/hal_gpt_lld.c
* @brief KINETIS GPT subsystem low level driver source.
*
* @addtogroup GPT
* @{
*/
#include "hal.h"
#if HAL_USE_GPT || defined(__DOXYGEN__)
/*===========================================================================*/
/* Driver local definitions. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver exported variables. */
/*===========================================================================*/
/**
* @brief GPTD1 driver identifier.
* @note The driver GPTD1 allocates the complex timer PIT0 when enabled.
*/
#if KINETIS_GPT_USE_PIT0 || defined(__DOXYGEN__)
GPTDriver GPTD1;
#endif
/**
* @brief GPTD2 driver identifier.
* @note The driver GPTD2 allocates the timer PIT1 when enabled.
*/
#if KINETIS_GPT_USE_PIT1 || defined(__DOXYGEN__)
GPTDriver GPTD2;
#endif
/**
* @brief GPTD3 driver identifier.
* @note The driver GPTD3 allocates the timer PIT2 when enabled.
*/
#if KINETIS_GPT_USE_PIT2 || defined(__DOXYGEN__)
GPTDriver GPTD3;
#endif
/**
* @brief GPTD4 driver identifier.
* @note The driver GPTD4 allocates the timer PIT3 when enabled.
*/
#if KINETIS_GPT_USE_PIT3 || defined(__DOXYGEN__)
GPTDriver GPTD4;
#endif
/*===========================================================================*/
/* Driver local variables and types. */
/*===========================================================================*/
#if KINETIS_HAS_PIT_COMMON_IRQ
static uint8_t active_channels = 0;
#endif /* KINETIS_HAS_PIT_COMMON_IRQ */
/*===========================================================================*/
/* Driver local functions. */
/*===========================================================================*/
/**
* @brief Shared IRQ handler.
*
* @param[in] gptp pointer to a @p GPTDriver object
*/
static void gpt_lld_serve_interrupt(GPTDriver *gptp) {
/* Clear the interrupt */
gptp->channel->TFLG |= PIT_TFLGn_TIF;
if (gptp->state == GPT_ONESHOT) {
gptp->state = GPT_READY; /* Back in GPT_READY state. */
gpt_lld_stop_timer(gptp); /* Timer automatically stopped. */
}
gptp->config->callback(gptp);
}
/*===========================================================================*/
/* Driver interrupt handlers. */
/*===========================================================================*/
#if !KINETIS_HAS_PIT_COMMON_IRQ
#if KINETIS_GPT_USE_PIT0
/**
* @brief PIT1 interrupt handler.
*
* @isr
*/
OSAL_IRQ_HANDLER(KINETIS_PIT0_IRQ_VECTOR) {
OSAL_IRQ_PROLOGUE();
gpt_lld_serve_interrupt(&GPTD1);
OSAL_IRQ_EPILOGUE();
}
#endif /* KINETIS_GPT_USE_PIT0 */
#if KINETIS_GPT_USE_PIT1
/**
* @brief PIT1 interrupt handler.
*
* @isr
*/
OSAL_IRQ_HANDLER(KINETIS_PIT1_IRQ_VECTOR) {
OSAL_IRQ_PROLOGUE();
gpt_lld_serve_interrupt(&GPTD2);
OSAL_IRQ_EPILOGUE();
}
#endif /* KINETIS_GPT_USE_PIT1 */
#if KINETIS_GPT_USE_PIT2
/**
* @brief PIT2 interrupt handler.
*
* @isr
*/
OSAL_IRQ_HANDLER(KINETIS_PIT2_IRQ_VECTOR) {
OSAL_IRQ_PROLOGUE();
gpt_lld_serve_interrupt(&GPTD3);
OSAL_IRQ_EPILOGUE();
}
#endif /* KINETIS_GPT_USE_PIT2 */
#if KINETIS_GPT_USE_PIT3
/**
* @brief PIT3 interrupt handler.
*
* @isr
*/
OSAL_IRQ_HANDLER(KINETIS_PIT3_IRQ_VECTOR) {
OSAL_IRQ_PROLOGUE();
gpt_lld_serve_interrupt(&GPTD4);
OSAL_IRQ_EPILOGUE();
}
#endif /* KINETIS_GPT_USE_PIT3 */
#else /* !KINETIS_HAS_PIT_COMMON_IRQ */
/**
* @brief Common PIT interrupt handler.
*
* @isr
*/
OSAL_IRQ_HANDLER(KINETIS_PIT_IRQ_VECTOR) {
OSAL_IRQ_PROLOGUE();
#if KINETIS_GPT_USE_PIT0
if(GPTD1.channel->TFLG & PIT_TFLGn_TIF)
gpt_lld_serve_interrupt(&GPTD1);
#endif /* KINETIS_GPT_USE_PIT0 */
#if KINETIS_GPT_USE_PIT1
if(GPTD2.channel->TFLG & PIT_TFLGn_TIF)
gpt_lld_serve_interrupt(&GPTD2);
#endif /* KINETIS_GPT_USE_PIT1 */
#if KINETIS_GPT_USE_PIT2
if(GPTD3.channel->TFLG & PIT_TFLGn_TIF)
gpt_lld_serve_interrupt(&GPTD3);
#endif /* KINETIS_GPT_USE_PIT2 */
#if KINETIS_GPT_USE_PIT3
if(GPTD4.channel->TFLG & PIT_TFLGn_TIF)
gpt_lld_serve_interrupt(&GPTD4);
#endif /* KINETIS_GPT_USE_PIT3 */
OSAL_IRQ_EPILOGUE();
}
#endif /* !KINETIS_HAS_PIT_COMMON_IRQ */
/*===========================================================================*/
/* Driver exported functions. */
/*===========================================================================*/
/**
* @brief Low level GPT driver initialization.
*
* @notapi
*/
void gpt_lld_init(void) {
#if KINETIS_GPT_USE_PIT0
/* Driver initialization.*/
GPTD1.channel = &PIT->CHANNEL[0];
gptObjectInit(&GPTD1);
#endif
#if KINETIS_GPT_USE_PIT1
/* Driver initialization.*/
GPTD2.channel = &PIT->CHANNEL[1];
gptObjectInit(&GPTD2);
#endif
#if KINETIS_GPT_USE_PIT2
/* Driver initialization.*/
GPTD3.channel = &PIT->CHANNEL[2];
gptObjectInit(&GPTD3);
#endif
#if KINETIS_GPT_USE_PIT3
/* Driver initialization.*/
GPTD4.channel = &PIT->CHANNEL[3];
gptObjectInit(&GPTD4);
#endif
}
/**
* @brief Configures and activates the GPT peripheral.
*
* @param[in] gptp pointer to the @p GPTDriver object
*
* @notapi
*/
void gpt_lld_start(GPTDriver *gptp) {
uint16_t psc;
if (gptp->state == GPT_STOP) {
/* Clock activation.*/
SIM->SCGC6 |= SIM_SCGC6_PIT;
gptp->clock = KINETIS_SYSCLK_FREQUENCY;
#if !KINETIS_HAS_PIT_COMMON_IRQ
#if KINETIS_GPT_USE_PIT0
if (&GPTD1 == gptp) {
nvicEnableVector(PITChannel0_IRQn, KINETIS_GPT_PIT0_IRQ_PRIORITY);
}
#endif
#if KINETIS_GPT_USE_PIT1
if (&GPTD2 == gptp) {
nvicEnableVector(PITChannel1_IRQn, KINETIS_GPT_PIT1_IRQ_PRIORITY);
}
#endif
#if KINETIS_GPT_USE_PIT2
if (&GPTD3 == gptp) {
nvicEnableVector(PITChannel2_IRQn, KINETIS_GPT_PIT2_IRQ_PRIORITY);
}
#endif
#if KINETIS_GPT_USE_PIT3
if (&GPTD4 == gptp) {
nvicEnableVector(PITChannel3_IRQn, KINETIS_GPT_PIT3_IRQ_PRIORITY);
}
#endif
#else /* !KINETIS_HAS_PIT_COMMON_IRQ */
nvicEnableVector(PIT_IRQn, KINETIS_GPT_PIT_IRQ_PRIORITY);
active_channels++;
#endif /* !KINETIS_HAS_PIT_COMMON_IRQ */
}
/* Prescaler value calculation.*/
psc = (uint16_t)((gptp->clock / gptp->config->frequency) - 1);
osalDbgAssert(((uint32_t)(psc + 1) * gptp->config->frequency) == gptp->clock,
"invalid frequency");
/* Enable the PIT */
PIT->MCR = 0;
}
/**
* @brief Deactivates the GPT peripheral.
*
* @param[in] gptp pointer to the @p GPTDriver object
*
* @notapi
*/
void gpt_lld_stop(GPTDriver *gptp) {
if (gptp->state == GPT_READY) {
SIM->SCGC6 &= ~SIM_SCGC6_PIT;
/* Disable the channel */
gptp->channel->TCTRL = 0;
/* Clear pending interrupts */
gptp->channel->TFLG |= PIT_TFLGn_TIF;
#if !KINETIS_HAS_PIT_COMMON_IRQ
#if KINETIS_GPT_USE_PIT0
if (&GPTD1 == gptp) {
nvicDisableVector(PITChannel0_IRQn);
}
#endif
#if KINETIS_GPT_USE_PIT1
if (&GPTD2 == gptp) {
nvicDisableVector(PITChannel1_IRQn);
}
#endif
#if KINETIS_GPT_USE_PIT2
if (&GPTD3 == gptp) {
nvicDisableVector(PITChannel2_IRQn);
}
#endif
#if KINETIS_GPT_USE_PIT3
if (&GPTD4 == gptp) {
nvicDisableVector(PITChannel3_IRQn);
}
#endif
#else /* !KINETIS_HAS_PIT_COMMON_IRQ */
if(--active_channels == 0)
nvicDisableVector(PIT_IRQn);
#endif /* !KINETIS_HAS_PIT_COMMON_IRQ */
}
}
/**
* @brief Starts the timer in continuous mode.
*
* @param[in] gptp pointer to the @p GPTDriver object
* @param[in] interval period in ticks
*
* @notapi
*/
void gpt_lld_start_timer(GPTDriver *gptp, gptcnt_t interval) {
/* Clear pending interrupts */
gptp->channel->TFLG |= PIT_TFLGn_TIF;
/* Set the interval */
gpt_lld_change_interval(gptp, interval);
/* Start the timer */
gptp->channel->TCTRL |= PIT_TCTRLn_TIE | PIT_TCTRLn_TEN;
}
/**
* @brief Stops the timer.
*
* @param[in] gptp pointer to the @p GPTDriver object
*
* @notapi
*/
void gpt_lld_stop_timer(GPTDriver *gptp) {
/* Stop the timer */
gptp->channel->TCTRL = 0;
}
/**
* @brief Starts the timer in one shot mode and waits for completion.
* @details This function specifically polls the timer waiting for completion
* in order to not have extra delays caused by interrupt servicing,
* this function is only recommended for short delays.
*
* @param[in] gptp pointer to the @p GPTDriver object
* @param[in] interval time interval in ticks
*
* @notapi
*/
void gpt_lld_polled_delay(GPTDriver *gptp, gptcnt_t interval) {
struct PIT_CHANNEL *channel = gptp->channel;
/* Disable timer and disable interrupts */
channel->TCTRL = 0;
/* Clear the interrupt flag */
channel->TFLG |= PIT_TFLGn_TIF;
/* Set the interval */
channel->LDVAL = (gptp->clock / gptp->config->frequency) * interval;
/* Enable Timer but keep interrupts disabled */
channel->TCTRL = PIT_TCTRLn_TEN;
/* Wait for the interrupt flag to be set */
while (!(channel->TFLG & PIT_TFLGn_TIF))
;
/* Disable timer and disable interrupts */
channel->TCTRL = 0;
}
#endif /* HAL_USE_GPT */
/** @} */

View File

@ -0,0 +1,333 @@
/*
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 PITv1/hal_gpt_lld.h
* @brief KINETIS GPT subsystem low level driver header.
*
* @addtogroup GPT
* @{
*/
#ifndef HAL_GPT_LLD_H_
#define HAL_GPT_LLD_H_
#if HAL_USE_GPT || defined(__DOXYGEN__)
/*===========================================================================*/
/* Driver constants. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver pre-compile time settings. */
/*===========================================================================*/
/**
* @name Configuration options
* @{
*/
/**
* @brief GPTD1 driver enable switch.
* @details If set to @p TRUE the support for GPTD1 is included.
* @note The default is @p TRUE.
*/
#if !defined(KINETIS_GPT_USE_PIT0) || defined(__DOXYGEN__)
#define KINETIS_GPT_USE_PIT0 FALSE
#endif
/**
* @brief GPTD2 driver enable switch.
* @details If set to @p TRUE the support for GPTD2 is included.
* @note The default is @p TRUE.
*/
#if !defined(KINETIS_GPT_USE_PIT1) || defined(__DOXYGEN__)
#define KINETIS_GPT_USE_PIT1 FALSE
#endif
/**
* @brief GPTD3 driver enable switch.
* @details If set to @p TRUE the support for GPTD3 is included.
* @note The default is @p TRUE.
*/
#if !defined(KINETIS_GPT_USE_PIT2) || defined(__DOXYGEN__)
#define KINETIS_GPT_USE_PIT2 FALSE
#endif
/**
* @brief GPTD4 driver enable switch.
* @details If set to @p TRUE the support for GPTD4 is included.
* @note The default is @p TRUE.
*/
#if !defined(KINETIS_GPT_USE_PIT3) || defined(__DOXYGEN__)
#define KINETIS_GPT_USE_PIT3 FALSE
#endif
/**
* @brief GPTD1 interrupt priority level setting.
*/
#if !defined(KINETIS_GPT_PIT0_IRQ_PRIORITY) || defined(__DOXYGEN__)
#define KINETIS_GPT_PIT0_IRQ_PRIORITY 7
#endif
/**
* @brief GPTD2 interrupt priority level setting.
*/
#if !defined(KINETIS_GPT_PIT1_IRQ_PRIORITY) || defined(__DOXYGEN__)
#define KINETIS_GPT_PIT1_IRQ_PRIORITY 7
#endif
/**
* @brief GPTD3 interrupt priority level setting.
*/
#if !defined(KINETIS_GPT_PIT2_IRQ_PRIORITY) || defined(__DOXYGEN__)
#define KINETIS_GPT_PIT2_IRQ_PRIORITY 7
#endif
/**
* @brief GPTD4 interrupt priority level setting.
*/
#if !defined(KINETIS_GPT_PIT3_IRQ_PRIORITY) || defined(__DOXYGEN__)
#define KINETIS_GPT_PIT3_IRQ_PRIORITY 7
#endif
/**
* @brief GPTD* common interrupt priority level setting.
*/
#if (KINETIS_HAS_PIT_COMMON_IRQ && !defined(KINETIS_GPT_PIT_IRQ_PRIORITY)) \
|| defined(__DOXYGEN__)
#define KINETIS_GPT_PIT_IRQ_PRIORITY 2
#endif
/*===========================================================================*/
/* Derived constants and error checks. */
/*===========================================================================*/
#if KINETIS_GPT_USE_PIT0 && !KINETIS_HAS_PIT0
#error "PIT0 not present in the selected device"
#endif
#if KINETIS_GPT_USE_PIT1 && !KINETIS_HAS_PIT1
#error "PIT1 not present in the selected device"
#endif
#if KINETIS_GPT_USE_PIT2 && !KINETIS_HAS_PIT2
#error "PIT2 not present in the selected device"
#endif
#if KINETIS_GPT_USE_PIT3 && !KINETIS_HAS_PIT3
#error "PIT3 not present in the selected device"
#endif
#if !KINETIS_GPT_USE_PIT0 && !KINETIS_GPT_USE_PIT1 && \
!KINETIS_GPT_USE_PIT2 && !KINETIS_GPT_USE_PIT3
#error "GPT driver activated but no PIT peripheral assigned"
#endif
#if KINETIS_GPT_USE_PIT0 && !KINETIS_HAS_PIT_COMMON_IRQ && \
!OSAL_IRQ_IS_VALID_PRIORITY(KINETIS_GPT_PIT0_IRQ_PRIORITY)
#error "Invalid IRQ priority assigned to PIT0"
#endif
#if KINETIS_GPT_USE_PIT1 && !KINETIS_HAS_PIT_COMMON_IRQ && \
!OSAL_IRQ_IS_VALID_PRIORITY(KINETIS_GPT_PIT1_IRQ_PRIORITY)
#error "Invalid IRQ priority assigned to PIT1"
#endif
#if KINETIS_GPT_USE_PIT2 && !KINETIS_HAS_PIT_COMMON_IRQ && \
!OSAL_IRQ_IS_VALID_PRIORITY(KINETIS_GPT_PIT2_IRQ_PRIORITY)
#error "Invalid IRQ priority assigned to PIT2"
#endif
#if KINETIS_GPT_USE_PIT3 && !KINETIS_HAS_PIT_COMMON_IRQ && \
!OSAL_IRQ_IS_VALID_PRIORITY(KINETIS_GPT_PIT3_IRQ_PRIORITY)
#error "Invalid IRQ priority assigned to PIT3"
#endif
#if KINETIS_HAS_PIT_COMMON_IRQ && \
!OSAL_IRQ_IS_VALID_PRIORITY(KINETIS_GPT_PIT_IRQ_PRIORITY)
#error "Invalid IRQ priority assigned to PIT"
#endif
#if KINETIS_GPT_USE_PIT0 && !defined(KINETIS_PIT0_IRQ_VECTOR) && \
!KINETIS_HAS_PIT_COMMON_IRQ
#error "KINETIS_PIT0_IRQ_VECTOR not defined"
#endif
#if KINETIS_GPT_USE_PIT1 && !defined(KINETIS_PIT1_IRQ_VECTOR) && \
!KINETIS_HAS_PIT_COMMON_IRQ
#error "KINETIS_PIT1_IRQ_VECTOR not defined"
#endif
#if KINETIS_GPT_USE_PIT2 && !defined(KINETIS_PIT2_IRQ_VECTOR) && \
!KINETIS_HAS_PIT_COMMON_IRQ
#error "KINETIS_PIT2_IRQ_VECTOR not defined"
#endif
#if KINETIS_GPT_USE_PIT3 && !defined(KINETIS_PIT3_IRQ_VECTOR) && \
!KINETIS_HAS_PIT_COMMON_IRQ
#error "KINETIS_PIT3_IRQ_VECTOR not defined"
#endif
#if KINETIS_HAS_PIT_COMMON_IRQ && !defined(KINETIS_PIT_IRQ_VECTOR)
#error "KINETIS_PIT_IRQ_VECTOR not defined"
#endif
/*===========================================================================*/
/* Driver data structures and types. */
/*===========================================================================*/
/**
* @brief GPT frequency type.
*/
typedef uint32_t gptfreq_t;
/**
* @brief GPT counter type.
*/
typedef uint32_t gptcnt_t;
/**
* @brief Driver configuration structure.
* @note It could be empty on some architectures.
*/
typedef struct {
/**
* @brief Timer clock in Hz.
* @note The low level can use assertions in order to catch invalid
* frequency specifications.
*/
gptfreq_t frequency;
/**
* @brief Timer callback pointer.
* @note This callback is invoked on GPT counter events.
* @note This callback can be set to @p NULL but in that case the
* one-shot mode cannot be used.
*/
gptcallback_t callback;
/* End of the mandatory fields.*/
} GPTConfig;
/**
* @brief Structure representing a GPT driver.
*/
struct GPTDriver {
/**
* @brief Driver state.
*/
gptstate_t state;
/**
* @brief Current configuration data.
*/
const GPTConfig *config;
#if defined(GPT_DRIVER_EXT_FIELDS)
GPT_DRIVER_EXT_FIELDS
#endif
/* End of the mandatory fields.*/
/**
* @brief Timer base clock.
*/
uint32_t clock;
/**
* @brief Channel structure in PIT registers block.
*/
struct PIT_CHANNEL *channel;
};
/*===========================================================================*/
/* Driver macros. */
/*===========================================================================*/
/**
* @brief Changes the interval of GPT peripheral.
* @details This function changes the interval of a running GPT unit.
* @pre The GPT unit must be running in continuous mode.
* @post The GPT unit interval is changed to the new value.
* @note The function has effect at the next cycle start.
*
* @param[in] gptp pointer to a @p GPTDriver object
* @param[in] interval new cycle time in timer ticks
*
* @notapi
*/
#define gpt_lld_change_interval(gptp, interval) \
((gptp)->channel->LDVAL = (uint32_t)( \
( (gptp)->clock / (gptp)->config->frequency ) * \
( interval ) ))
/**
* @brief Returns the interval of GPT peripheral.
* @pre The GPT unit must be running in continuous mode.
*
* @param[in] gptp pointer to a @p GPTDriver object
* @return The current interval.
*
* @notapi
*/
#define gpt_lld_get_interval(gptp) \
((uint32_t)( ( (uint64_t)(gptp)->channel->LDVAL * (gptp)->config->frequency ) / \
( (uint32_t)(gptp)->clock ) ))
/**
* @brief Returns the counter value of GPT peripheral.
* @pre The GPT unit must be running in continuous mode.
* @note The nature of the counter is not defined, it may count upward
* or downward, it could be continuously running or not.
*
* @param[in] gptp pointer to a @p GPTDriver object
* @return The current counter value.
*
* @notapi
*/
#define gpt_lld_get_counter(gptp) ((gptcnt_t)(gptp)->pit->CHANNEL[gptp->channel].CVAL)
/*===========================================================================*/
/* External declarations. */
/*===========================================================================*/
#if KINETIS_GPT_USE_PIT0 && !defined(__DOXYGEN__)
extern GPTDriver GPTD1;
#endif
#if KINETIS_GPT_USE_PIT1 && !defined(__DOXYGEN__)
extern GPTDriver GPTD2;
#endif
#if KINETIS_GPT_USE_PIT2 && !defined(__DOXYGEN__)
extern GPTDriver GPTD3;
#endif
#if KINETIS_GPT_USE_PIT3 && !defined(__DOXYGEN__)
extern GPTDriver GPTD4;
#endif
#ifdef __cplusplus
extern "C" {
#endif
void gpt_lld_init(void);
void gpt_lld_start(GPTDriver *gptp);
void gpt_lld_stop(GPTDriver *gptp);
void gpt_lld_start_timer(GPTDriver *gptp, gptcnt_t period);
void gpt_lld_stop_timer(GPTDriver *gptp);
void gpt_lld_polled_delay(GPTDriver *gptp, gptcnt_t interval);
#ifdef __cplusplus
}
#endif
#endif /* HAL_USE_GPT */
#endif /* HAL_GPT_LLD_H_ */
/** @} */

View File

@ -0,0 +1,98 @@
/*
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 PITv1/hal_st_lld.c
* @brief ST Driver subsystem low level driver code.
*
* @addtogroup ST
* @{
*/
#include "hal.h"
#if (OSAL_ST_MODE != OSAL_ST_MODE_NONE) || defined(__DOXYGEN__)
/*===========================================================================*/
/* Driver local definitions. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver exported variables. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver local types. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver local variables and types. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver local functions. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver interrupt handlers. */
/*===========================================================================*/
#if (OSAL_ST_MODE == OSAL_ST_MODE_PERIODIC) || defined(__DOXYGEN__)
/**
* @brief System Timer vector.
* @details This interrupt is used for system tick in periodic mode.
*
* @isr
*/
OSAL_IRQ_HANDLER(SysTick_Handler) {
OSAL_IRQ_PROLOGUE();
osalSysLockFromISR();
osalOsTimerHandlerI();
osalSysUnlockFromISR();
OSAL_IRQ_EPILOGUE();
}
#endif /* OSAL_ST_MODE == OSAL_ST_MODE_PERIODIC */
/*===========================================================================*/
/* Driver exported functions. */
/*===========================================================================*/
/**
* @brief Low level ST driver initialization.
*
* @notapi
*/
void st_lld_init(void) {
#if OSAL_ST_MODE == OSAL_ST_MODE_PERIODIC
/* Periodic systick mode, the Cortex-Mx internal systick timer is used
in this mode.*/
SysTick->LOAD = (KINETIS_SYSCLK_FREQUENCY / OSAL_ST_FREQUENCY) - 1;
SysTick->VAL = 0;
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk |
SysTick_CTRL_ENABLE_Msk |
SysTick_CTRL_TICKINT_Msk;
/* IRQ enabled.*/
nvicSetSystemHandlerPriority(HANDLER_SYSTICK, KINETIS_ST_IRQ_PRIORITY);
#endif /* OSAL_ST_MODE == OSAL_ST_MODE_PERIODIC */
}
#endif /* OSAL_ST_MODE != OSAL_ST_MODE_NONE */
/** @} */

View File

@ -0,0 +1,156 @@
/*
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 PITv1/hal_st_lld.h
* @brief ST Driver subsystem low level driver header.
* @details This header is designed to be include-able without having to
* include other files from the HAL.
*
* @addtogroup ST
* @{
*/
#ifndef HAL_ST_LLD_H_
#define HAL_ST_LLD_H_
#include "mcuconf.h"
/*===========================================================================*/
/* Driver constants. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver pre-compile time settings. */
/*===========================================================================*/
/**
* @name Configuration options
* @{
*/
/**
* @brief SysTick timer IRQ priority.
*/
#if !defined(KINETIS_ST_IRQ_PRIORITY) || defined(__DOXYGEN__)
#define KINETIS_ST_IRQ_PRIORITY 8
#endif
/** @} */
/*===========================================================================*/
/* Derived constants and error checks. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver data structures and types. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver macros. */
/*===========================================================================*/
/*===========================================================================*/
/* External declarations. */
/*===========================================================================*/
#ifdef __cplusplus
extern "C" {
#endif
void st_lld_init(void);
#ifdef __cplusplus
}
#endif
/*===========================================================================*/
/* Driver inline functions. */
/*===========================================================================*/
/**
* @brief Returns the time counter value.
*
* @return The counter value.
*
* @notapi
*/
static inline systime_t st_lld_get_counter(void) {
return (systime_t)0;
}
/**
* @brief Starts the alarm.
* @note Makes sure that no spurious alarms are triggered after
* this call.
*
* @param[in] time the time to be set for the first alarm
*
* @notapi
*/
static inline void st_lld_start_alarm(systime_t time) {
(void)time;
}
/**
* @brief Stops the alarm interrupt.
*
* @notapi
*/
static inline void st_lld_stop_alarm(void) {
}
/**
* @brief Sets the alarm time.
*
* @param[in] time the time to be set for the next alarm
*
* @notapi
*/
static inline void st_lld_set_alarm(systime_t time) {
(void)time;
}
/**
* @brief Returns the current alarm time.
*
* @return The currently set alarm time.
*
* @notapi
*/
static inline systime_t st_lld_get_alarm(void) {
return (systime_t)0;
}
/**
* @brief Determines if the alarm is active.
*
* @return The alarm status.
* @retval false if the alarm is not active.
* @retval true is the alarm is active
*
* @notapi
*/
static inline bool st_lld_is_alarm_active(void) {
return false;
}
#endif /* HAL_ST_LLD_H_ */
/** @} */

View File

@ -0,0 +1,9 @@
ifeq ($(USE_SMART_BUILD),yes)
ifneq ($(findstring HAL_USE_SERIAL TRUE,$(HALCONF)),)
PLATFORMSRC_CONTRIB += ${CHIBIOS_CONTRIB}/os/hal/ports/KINETIS/LLD/UARTv1/hal_serial_lld.c
endif
else
PLATFORMSRC_CONTRIB += ${CHIBIOS_CONTRIB}/os/hal/ports/KINETIS/LLD/UARTv1/hal_serial_lld.c
endif
PLATFORMINC_CONTRIB += ${CHIBIOS_CONTRIB}/os/hal/ports/KINETIS/LLD/UARTv1

View File

@ -0,0 +1,693 @@
/*
ChibiOS - Copyright (C) 2013-2015 Fabio Utzig
Copyright (C) 2017 Wim Lewis
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 UARTv1/hal_serial_lld.c
* @brief Kinetis KL2x Serial Driver subsystem low level driver source.
*
* @addtogroup SERIAL
* @{
*/
#include "osal.h"
#include "hal.h"
#if HAL_USE_SERIAL || defined(__DOXYGEN__)
/*===========================================================================*/
/* Driver local definitions. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver exported variables. */
/*===========================================================================*/
/**
* @brief SD1 driver identifier.
*/
#if KINETIS_SERIAL_USE_UART0 || defined(__DOXYGEN__)
SerialDriver SD1;
#endif
#if KINETIS_SERIAL_USE_UART1 || defined(__DOXYGEN__)
SerialDriver SD2;
#endif
#if KINETIS_SERIAL_USE_UART2 || defined(__DOXYGEN__)
SerialDriver SD3;
#endif
#if KINETIS_SERIAL_USE_UART3 || defined(__DOXYGEN__)
SerialDriver SD4;
#endif
#if KINETIS_SERIAL_USE_UART4 || defined(__DOXYGEN__)
SerialDriver SD5;
#endif
#if KINETIS_SERIAL_USE_UART5 || defined(__DOXYGEN__)
SerialDriver SD6;
#endif
/*===========================================================================*/
/* Driver local variables and types. */
/*===========================================================================*/
/**
* @brief Driver default configuration.
*/
static const SerialConfig default_config = {
38400
};
/*===========================================================================*/
/* Driver local functions. */
/*===========================================================================*/
/**
* @brief Error handling routine.
*
* @param[in] sdp pointer to a @p SerialDriver object
* @param[in] isr UART s1 register value
*/
static void set_error(SerialDriver *sdp, uint8_t s1) {
eventflags_t sts = 0;
if (s1 & UARTx_S1_OR)
sts |= SD_OVERRUN_ERROR;
if (s1 & UARTx_S1_PF)
sts |= SD_PARITY_ERROR;
if (s1 & UARTx_S1_FE)
sts |= SD_FRAMING_ERROR;
if (s1 & UARTx_S1_NF)
sts |= SD_NOISE_ERROR;
osalSysLockFromISR();
chnAddFlagsI(sdp, sts);
osalSysUnlockFromISR();
}
/**
* @brief Common error IRQ handler.
*
* @param[in] sdp communication channel associated to the UART
*/
static void serve_error_interrupt(SerialDriver *sdp) {
UART_w_TypeDef *u = &(sdp->uart);
uint8_t s1 = *(u->s1_p);
/* Clearing on K20x, K60x, and KL2x/UART>0 is done by reading S1 and
* then reading D.*/
if(s1 & UARTx_S1_IDLE) {
(void)*(u->d_p);
}
if(s1 & (UARTx_S1_OR | UARTx_S1_NF | UARTx_S1_FE | UARTx_S1_PF)) {
set_error(sdp, s1);
(void)*(u->d_p);
}
}
#if defined(KL2x) && KINETIS_SERIAL_USE_UART0
static void serve_error_interrupt_uart0(void) {
SerialDriver *sdp = &SD1;
UART_w_TypeDef *u = &(sdp->uart);
uint8_t s1 = *(u->s1_p);
/* S1 bits are write-1-to-clear for UART0 on KL2x. */
if(s1 & UARTx_S1_IDLE) {
*(u->s1_p) |= UARTx_S1_IDLE;
}
if(s1 & (UARTx_S1_OR | UARTx_S1_NF | UARTx_S1_FE | UARTx_S1_PF)) {
set_error(sdp, s1);
*(u->s1_p) |= UARTx_S1_OR | UARTx_S1_NF | UARTx_S1_FE | UARTx_S1_PF;
}
}
#endif /* KL2x && KINETIS_SERIAL_USE_UART0 */
/**
* @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] sdp communication channel associated to the UART
*/
static void serve_interrupt(SerialDriver *sdp) {
UART_w_TypeDef *u = &(sdp->uart);
uint8_t s1 = *(u->s1_p);
if (s1 & UARTx_S1_RDRF) {
osalSysLockFromISR();
if (iqIsEmptyI(&sdp->iqueue))
chnAddFlagsI(sdp, CHN_INPUT_AVAILABLE);
if (iqPutI(&sdp->iqueue, *(u->d_p)) < Q_OK)
chnAddFlagsI(sdp, SD_OVERRUN_ERROR);
osalSysUnlockFromISR();
}
if (s1 & UARTx_S1_TDRE) {
msg_t b;
osalSysLockFromISR();
b = oqGetI(&sdp->oqueue);
osalSysUnlockFromISR();
if (b < Q_OK) {
osalSysLockFromISR();
chnAddFlagsI(sdp, CHN_OUTPUT_EMPTY);
osalSysUnlockFromISR();
*(u->c2_p) &= ~UARTx_C2_TIE;
} else {
*(u->d_p) = b;
}
}
#if defined(KL2x) && KINETIS_SERIAL_USE_UART0
if (sdp == &SD1) {
serve_error_interrupt_uart0();
return;
}
#endif
serve_error_interrupt(sdp);
}
/**
* @brief Attempts a TX preload
*/
static void preload(SerialDriver *sdp) {
UART_w_TypeDef *u = &(sdp->uart);
if (*(u->s1_p) & UARTx_S1_TDRE) {
msg_t b = oqGetI(&sdp->oqueue);
if (b < Q_OK) {
chnAddFlagsI(sdp, CHN_OUTPUT_EMPTY);
return;
}
*(u->d_p) = b;
*(u->c2_p) |= UARTx_C2_TIE;
}
}
/**
* @brief Driver output notification.
*/
static void notify(io_queue_t *qp)
{
preload(qp->q_link);
}
/**
* @brief Common driver initialization, except LP.
*/
static void sd_lld_init_driver(SerialDriver *SDn, UART_TypeDef *UARTn) {
/* Driver initialization.*/
sdObjectInit(SDn, NULL, notify);
SDn->uart.bdh_p = &(UARTn->BDH);
SDn->uart.bdl_p = &(UARTn->BDL);
SDn->uart.c1_p = &(UARTn->C1);
SDn->uart.c2_p = &(UARTn->C2);
SDn->uart.c3_p = &(UARTn->C3);
SDn->uart.c4_p = &(UARTn->C4);
SDn->uart.s1_p = (volatile uint8_t *)&(UARTn->S1);
SDn->uart.s2_p = &(UARTn->S2);
SDn->uart.d_p = &(UARTn->D);
SDn->uart.uart_p = UARTn;
}
/**
* @brief Common UART configuration.
*
*/
static void configure_uart(SerialDriver *sdp, const SerialConfig *config) {
UART_w_TypeDef *uart = &(sdp->uart);
uint32_t divisor;
/* Discard any incoming data. */
while (*(uart->s1_p) & UARTx_S1_RDRF) {
(void)*(uart->d_p);
}
/* Disable UART while configuring */
*(uart->c2_p) &= ~(UARTx_C2_RE | UARTx_C2_TE);
/* The clock sources for various UARTs can be different. */
divisor=KINETIS_BUSCLK_FREQUENCY;
#if defined(KL2x)
#if KINETIS_SERIAL_USE_UART0
if (sdp == &SD1) {
/* UART0 can be clocked from several sources on KL2x. */
divisor = KINETIS_UART0_CLOCK_FREQ;
/* FIXME: change fixed OSR = 16 to dynamic value based on baud */
/* Note: OSR only works on KL2x/UART0; further UARTs have fixed 16. */
*(uart->c4_p) = UARTx_C4_OSR(16 - 1);
}
#endif /* KINETIS_SERIAL_USE_UART0 */
#elif defined(K20x) || defined(K60x) || defined(MK66F18) /* KL2x */
/* UARTs 0 and 1 are clocked from SYSCLK, others from BUSCLK on K20x and K60x. */
#if KINETIS_SERIAL_USE_UART0
if(sdp == &SD1)
divisor = KINETIS_SYSCLK_FREQUENCY;
#endif /* KINETIS_SERIAL_USE_UART0 */
#if KINETIS_SERIAL_USE_UART1
if(sdp == &SD2)
divisor = KINETIS_SYSCLK_FREQUENCY;
#endif /* KINETIS_SERIAL_USE_UART1 */
#else /* K20x */
#error Baud rate selection not implemented for this MCU type
#endif /* K20x */
divisor = (divisor * 2 + 1) / config->sc_speed;
*(uart->bdh_p) = UARTx_BDH_SBR(divisor >> 13) | (*(uart->bdh_p) & ~UARTx_BDH_SBR_MASK);
*(uart->bdl_p) = UARTx_BDL_SBR(divisor >> 5);
#if defined(K20x) || defined(K60x)
*(uart->c4_p) = UARTx_C4_BRFA(divisor) | (*(uart->c4_p) & ~UARTx_C4_BRFA_MASK);
#endif /* K20x, K60x */
/* Line settings. */
*(uart->c1_p) = 0;
/* Enable error event interrupts (overrun, noise, framing, parity) */
*(uart->c3_p) = UARTx_C3_ORIE | UARTx_C3_NEIE | UARTx_C3_FEIE | UARTx_C3_PEIE;
/* Enable the peripheral; including receive interrupts. */
*(uart->c2_p) |= UARTx_C2_RE | UARTx_C2_RIE | UARTx_C2_TE;
}
/*===========================================================================*/
/* Driver interrupt handlers. */
/*===========================================================================*/
#if KINETIS_SERIAL_USE_UART0 || defined(__DOXYGEN__)
OSAL_IRQ_HANDLER(KINETIS_SERIAL0_IRQ_VECTOR) {
OSAL_IRQ_PROLOGUE();
serve_interrupt(&SD1);
OSAL_IRQ_EPILOGUE();
}
#endif
#if KINETIS_SERIAL_USE_UART1 || defined(__DOXYGEN__)
OSAL_IRQ_HANDLER(KINETIS_SERIAL1_IRQ_VECTOR) {
OSAL_IRQ_PROLOGUE();
serve_interrupt(&SD2);
OSAL_IRQ_EPILOGUE();
}
#endif
#if KINETIS_SERIAL_USE_UART2 || defined(__DOXYGEN__)
OSAL_IRQ_HANDLER(KINETIS_SERIAL2_IRQ_VECTOR) {
OSAL_IRQ_PROLOGUE();
serve_interrupt(&SD3);
OSAL_IRQ_EPILOGUE();
}
#endif
#if KINETIS_SERIAL_USE_UART3 || defined(__DOXYGEN__)
OSAL_IRQ_HANDLER(KINETIS_SERIAL3_IRQ_VECTOR) {
OSAL_IRQ_PROLOGUE();
serve_interrupt(&SD4);
OSAL_IRQ_EPILOGUE();
}
#endif
#if KINETIS_SERIAL_USE_UART4 || defined(__DOXYGEN__)
OSAL_IRQ_HANDLER(KINETIS_SERIAL4_IRQ_VECTOR) {
OSAL_IRQ_PROLOGUE();
serve_interrupt(&SD5);
OSAL_IRQ_EPILOGUE();
}
#endif
#if KINETIS_SERIAL_USE_UART5 || defined(__DOXYGEN__)
OSAL_IRQ_HANDLER(KINETIS_SERIAL5_IRQ_VECTOR) {
OSAL_IRQ_PROLOGUE();
serve_interrupt(&SD6);
OSAL_IRQ_EPILOGUE();
}
#endif
#if KINETIS_HAS_SERIAL_ERROR_IRQ
#if KINETIS_SERIAL_USE_UART0 || defined(__DOXYGEN__)
OSAL_IRQ_HANDLER(KINETIS_SERIAL0_ERROR_IRQ_VECTOR) {
OSAL_IRQ_PROLOGUE();
#if defined(KL2x)
serve_error_interrupt_uart0();
#else
serve_error_interrupt(&SD1);
#endif
OSAL_IRQ_EPILOGUE();
}
#endif
#if KINETIS_SERIAL_USE_UART1 || defined(__DOXYGEN__)
OSAL_IRQ_HANDLER(KINETIS_SERIAL1_ERROR_IRQ_VECTOR) {
OSAL_IRQ_PROLOGUE();
serve_error_interrupt(&SD2);
OSAL_IRQ_EPILOGUE();
}
#endif
#if KINETIS_SERIAL_USE_UART2 || defined(__DOXYGEN__)
OSAL_IRQ_HANDLER(KINETIS_SERIAL2_ERROR_IRQ_VECTOR) {
OSAL_IRQ_PROLOGUE();
serve_error_interrupt(&SD3);
OSAL_IRQ_EPILOGUE();
}
#endif
#if KINETIS_SERIAL_USE_UART3 || defined(__DOXYGEN__)
OSAL_IRQ_HANDLER(KINETIS_SERIAL3_ERROR_IRQ_VECTOR) {
OSAL_IRQ_PROLOGUE();
serve_error_interrupt(&SD4);
OSAL_IRQ_EPILOGUE();
}
#endif
#if KINETIS_SERIAL_USE_UART4 || defined(__DOXYGEN__)
OSAL_IRQ_HANDLER(KINETIS_SERIAL4_ERROR_IRQ_VECTOR) {
OSAL_IRQ_PROLOGUE();
serve_error_interrupt(&SD5);
OSAL_IRQ_EPILOGUE();
}
#endif
#if KINETIS_SERIAL_USE_UART5 || defined(__DOXYGEN__)
OSAL_IRQ_HANDLER(KINETIS_SERIAL5_ERROR_IRQ_VECTOR) {
OSAL_IRQ_PROLOGUE();
serve_error_interrupt(&SD6);
OSAL_IRQ_EPILOGUE();
}
#endif
#endif /* KINETIS_HAS_SERIAL_ERROR_IRQ */
/*===========================================================================*/
/* Driver exported functions. */
/*===========================================================================*/
/**
* @brief Low level serial driver initialization.
*
* @notapi
*/
void sd_lld_init(void) {
#if KINETIS_SERIAL_USE_UART0
/* Driver initialization.*/
#if ! KINETIS_SERIAL0_IS_LPUART
sd_lld_init_driver(&SD1, (UART_TypeDef *)UART0);
#else /* ! KINETIS_SERIAL0_IS_LPUART */
/* little endian! */
sdObjectInit(&SD1, NULL, notify);
SD1.uart.bdh_p = ((uint8_t *)&(LPUART0->BAUD)) + 1; /* BDH: BAUD, byte 3 */
SD1.uart.bdl_p = ((uint8_t *)&(LPUART0->BAUD)) + 0; /* BDL: BAUD, byte 4 */
SD1.uart.c1_p = ((uint8_t *)&(LPUART0->CTRL)) + 0; /* C1: CTRL, byte 4 */
SD1.uart.c2_p = ((uint8_t *)&(LPUART0->CTRL)) + 2; /* C2: CTRL, byte 2 */
SD1.uart.c3_p = ((uint8_t *)&(LPUART0->CTRL)) + 3; /* C3: CTRL, byte 1 */
SD1.uart.c4_p = ((uint8_t *)&(LPUART0->BAUD)) + 3; /* C4: BAUD, byte 1 */
SD1.uart.s1_p = ((uint8_t *)&(LPUART0->STAT)) + 2; /* S1: STAT, byte 2 */
SD1.uart.s2_p = ((uint8_t *)&(LPUART0->STAT)) + 3; /* S2: STAT, byte 1 */
SD1.uart.d_p = ((uint8_t *)&(LPUART0->DATA)) + 0; /* D: DATA, byte 4 */
#endif /* ! KINETIS_SERIAL0_IS_LPUART */
#if KINETIS_SERIAL0_IS_UARTLP
SD1.uart.c4_p = &(UART0->C4); /* fix up misconfigured C4 register */
SD1.uart.uartlp_p = UART0;
SD1.uart.uart_p = NULL;
#elif KINETIS_SERIAL0_IS_LPUART
SD1.uart.lpuart_p = LPUART0;
SD1.uart.uart_p = NULL;
#else /* KINETIS_SERIAL0_IS_LPUART */
SD1.uart.uart_p = UART0;
#endif /* KINETIS_SERIAL0_IS_LPUART */
#endif /* KINETIS_SERIAL_USE_UART0 */
#if KINETIS_SERIAL_USE_UART1
/* Driver initialization.*/
#if ! KINETIS_SERIAL1_IS_LPUART
sd_lld_init_driver(&SD2, UART1);
#else /* ! KINETIS_SERIAL1_IS_LPUART */
/* little endian! */
sdObjectInit(&SD2, NULL, notify);
SD2.uart.bdh_p = ((uint8_t *)&(LPUART1->BAUD)) + 1; /* BDH: BAUD, byte 3 */
SD2.uart.bdl_p = ((uint8_t *)&(LPUART1->BAUD)) + 0; /* BDL: BAUD, byte 4 */
SD2.uart.c1_p = ((uint8_t *)&(LPUART1->CTRL)) + 0; /* C1: CTRL, byte 4 */
SD2.uart.c2_p = ((uint8_t *)&(LPUART1->CTRL)) + 2; /* C2: CTRL, byte 2 */
SD2.uart.c3_p = ((uint8_t *)&(LPUART1->CTRL)) + 3; /* C3: CTRL, byte 1 */
SD2.uart.c4_p = ((uint8_t *)&(LPUART1->BAUD)) + 3; /* C4: BAUD, byte 1 */
SD2.uart.s1_p = ((uint8_t *)&(LPUART1->STAT)) + 2; /* S1: STAT, byte 2 */
SD2.uart.s2_p = ((uint8_t *)&(LPUART1->STAT)) + 3; /* S2: STAT, byte 1 */
SD2.uart.d_p = ((uint8_t *)&(LPUART1->DATA)) + 0; /* D: DATA, byte 4 */
SD2.uart.lpuart_p = LPUART1;
SD2.uart.uart_p = NULL;
#endif /* ! KINETIS_SERIAL1_IS_LPUART */
#endif /* KINETIS_SERIAL_USE_UART1 */
#if KINETIS_SERIAL_USE_UART2
sd_lld_init_driver(&SD3, UART2);
#endif /* KINETIS_SERIAL_USE_UART2 */
#if KINETIS_SERIAL_USE_UART3
sd_lld_init_driver(&SD4, UART3);
#endif /* KINETIS_SERIAL_USE_UART3 */
#if KINETIS_SERIAL_USE_UART4
sd_lld_init_driver(&SD5, UART4);
#endif /* KINETIS_SERIAL_USE_UART4 */
#if KINETIS_SERIAL_USE_UART5
sd_lld_init_driver(&SD6, UART5);
#endif /* KINETIS_SERIAL_USE_UART5 */
}
/**
* @brief Low level serial driver configuration and (re)start.
*
* @param[in] sdp pointer to a @p SerialDriver object
* @param[in] config the architecture-dependent serial driver configuration.
* If this parameter is set to @p NULL then a default
* configuration is used.
*
* @notapi
*/
void sd_lld_start(SerialDriver *sdp, const SerialConfig *config) {
if (config == NULL)
config = &default_config;
if (sdp->state == SD_STOP) {
/* Enables the peripheral.*/
#if KINETIS_SERIAL_USE_UART0
if (sdp == &SD1) {
#if KINETIS_SERIAL0_IS_LPUART
SIM->SCGC5 |= SIM_SCGC5_LPUART0;
SIM->SOPT2 =
(SIM->SOPT2 & ~SIM_SOPT2_LPUART0SRC_MASK) |
SIM_SOPT2_LPUART0SRC(KINETIS_UART0_CLOCK_SRC);
#else /* KINETIS_SERIAL0_IS_LPUART */
SIM->SCGC4 |= SIM_SCGC4_UART0;
#endif /* KINETIS_SERIAL0_IS_LPUART */
#if KINETIS_SERIAL0_IS_UARTLP
SIM->SOPT2 =
(SIM->SOPT2 & ~SIM_SOPT2_UART0SRC_MASK) |
SIM_SOPT2_UART0SRC(KINETIS_UART0_CLOCK_SRC);
#endif /* KINETIS_SERIAL0_IS_UARTLP */
configure_uart(sdp, config);
#if KINETIS_HAS_SERIAL_ERROR_IRQ
nvicEnableVector(UART0Status_IRQn, KINETIS_SERIAL_UART0_PRIORITY);
nvicEnableVector(UART0Error_IRQn, KINETIS_SERIAL_UART0_PRIORITY);
#else /* KINETIS_HAS_SERIAL_ERROR_IRQ */
#if KINETIS_SERIAL0_IS_LPUART
nvicEnableVector(LPUART0_IRQn, KINETIS_SERIAL_UART0_PRIORITY);
#else /* KINETIS_SERIAL0_IS_LPUART */
nvicEnableVector(UART0_IRQn, KINETIS_SERIAL_UART0_PRIORITY);
#endif /* KINETIS_SERIAL0_IS_LPUART */
#endif /* KINETIS_HAS_SERIAL_ERROR_IRQ */
}
#endif /* KINETIS_SERIAL_USE_UART0 */
#if KINETIS_SERIAL_USE_UART1
if (sdp == &SD2) {
#if KINETIS_SERIAL1_IS_LPUART
SIM->SCGC5 |= SIM_SCGC5_LPUART1;
SIM->SOPT2 =
(SIM->SOPT2 & ~SIM_SOPT2_LPUART1SRC_MASK) |
SIM_SOPT2_LPUART1SRC(KINETIS_UART1_CLOCK_SRC);
#else /* KINETIS_SERIAL1_IS_LPUART */
SIM->SCGC4 |= SIM_SCGC4_UART1;
#endif /* KINETIS_SERIAL1_IS_LPUART */
configure_uart(sdp, config);
#if KINETIS_HAS_SERIAL_ERROR_IRQ
nvicEnableVector(UART1Status_IRQn, KINETIS_SERIAL_UART1_PRIORITY);
nvicEnableVector(UART1Error_IRQn, KINETIS_SERIAL_UART0_PRIORITY);
#else /* KINETIS_HAS_SERIAL_ERROR_IRQ */
#if KINETIS_SERIAL1_IS_LPUART
nvicEnableVector(LPUART1_IRQn, KINETIS_SERIAL_UART1_PRIORITY);
#else /* KINETIS_SERIAL1_IS_LPUART */
nvicEnableVector(UART1_IRQn, KINETIS_SERIAL_UART1_PRIORITY);
#endif /* KINETIS_SERIAL1_IS_LPUART */
#endif /* KINETIS_HAS_SERIAL_ERROR_IRQ */
}
#endif /* KINETIS_SERIAL_USE_UART1 */
#if KINETIS_SERIAL_USE_UART2
if (sdp == &SD3) {
SIM->SCGC4 |= SIM_SCGC4_UART2;
configure_uart(sdp, config);
#if KINETIS_HAS_SERIAL_ERROR_IRQ
nvicEnableVector(UART2Status_IRQn, KINETIS_SERIAL_UART2_PRIORITY);
nvicEnableVector(UART2Error_IRQn, KINETIS_SERIAL_UART0_PRIORITY);
#else /* KINETIS_HAS_SERIAL_ERROR_IRQ */
nvicEnableVector(UART2_IRQn, KINETIS_SERIAL_UART2_PRIORITY);
#endif /* KINETIS_HAS_SERIAL_ERROR_IRQ */
}
#endif /* KINETIS_SERIAL_USE_UART2 */
#if KINETIS_SERIAL_USE_UART3
if (sdp == &SD4) {
SIM->SCGC4 |= SIM_SCGC4_UART3;
configure_uart(sdp, config);
nvicEnableVector(UART3Status_IRQn, KINETIS_SERIAL_UART3_PRIORITY);
nvicEnableVector(UART3Error_IRQn, KINETIS_SERIAL_UART3_PRIORITY);
}
#endif /* KINETIS_SERIAL_USE_UART3 */
#if KINETIS_SERIAL_USE_UART4
if (sdp == &SD5) {
SIM->SCGC1 |= SIM_SCGC1_UART4;
configure_uart(sdp, config);
nvicEnableVector(UART4Status_IRQn, KINETIS_SERIAL_UART4_PRIORITY);
nvicEnableVector(UART4Error_IRQn, KINETIS_SERIAL_UART4_PRIORITY);
}
#endif /* KINETIS_SERIAL_USE_UART4 */
#if KINETIS_SERIAL_USE_UART5
if (sdp == &SD6) {
SIM->SCGC1 |= SIM_SCGC1_UART5;
configure_uart(sdp, config);
nvicEnableVector(UART5Status_IRQn, KINETIS_SERIAL_UART5_PRIORITY);
nvicEnableVector(UART5Error_IRQn, KINETIS_SERIAL_UART5_PRIORITY);
}
#endif /* KINETIS_SERIAL_USE_UART5 */
}
/* Configures the peripheral.*/
}
/**
* @brief Low level serial driver stop.
* @details De-initializes the USART, stops the associated clock, resets the
* interrupt vector.
*
* @param[in] sdp pointer to a @p SerialDriver object
*
* @notapi
*/
void sd_lld_stop(SerialDriver *sdp) {
if (sdp->state == SD_READY) {
/* TODO: Resets the peripheral.*/
#if KINETIS_SERIAL_USE_UART0
if (sdp == &SD1) {
#if KINETIS_HAS_SERIAL_ERROR_IRQ
nvicDisableVector(UART0Status_IRQn);
nvicDisableVector(UART0Error_IRQn);
#else /* KINETIS_HAS_SERIAL_ERROR_IRQ */
#if KINETIS_SERIAL0_IS_LPUART
nvicDisableVector(LPUART0_IRQn);
#else /* KINETIS_SERIAL0_IS_LPUART */
nvicDisableVector(UART0_IRQn);
#endif /* KINETIS_SERIAL0_IS_LPUART */
#endif /* KINETIS_HAS_SERIAL_ERROR_IRQ */
#if KINETIS_SERIAL0_IS_LPUART
SIM->SCGC5 &= ~SIM_SCGC5_LPUART0;
#else /* KINETIS_SERIAL0_IS_LPUART */
SIM->SCGC4 &= ~SIM_SCGC4_UART0;
#endif /* KINETIS_SERIAL0_IS_LPUART */
}
#endif
#if KINETIS_SERIAL_USE_UART1
if (sdp == &SD2) {
#if KINETIS_HAS_SERIAL_ERROR_IRQ
nvicDisableVector(UART1Status_IRQn);
nvicDisableVector(UART1Error_IRQn);
#else /* KINETIS_HAS_SERIAL_ERROR_IRQ */
#if KINETIS_SERIAL1_IS_LPUART
nvicDisableVector(LPUART1_IRQn);
#else /* KINETIS_SERIAL1_IS_LPUART */
nvicDisableVector(UART1_IRQn);
#endif /* KINETIS_SERIAL1_IS_LPUART */
#endif /* KINETIS_HAS_SERIAL_ERROR_IRQ */
#if KINETIS_SERIAL1_IS_LPUART
SIM->SCGC5 &= ~SIM_SCGC5_LPUART1;
#else /* KINETIS_SERIAL1_IS_LPUART */
SIM->SCGC4 &= ~SIM_SCGC4_UART1;
#endif /* KINETIS_SERIAL1_IS_LPUART */
}
#endif
#if KINETIS_SERIAL_USE_UART2
if (sdp == &SD3) {
#if KINETIS_HAS_SERIAL_ERROR_IRQ
nvicDisableVector(UART2Status_IRQn);
nvicDisableVector(UART2Error_IRQn);
#else /* KINETIS_HAS_SERIAL_ERROR_IRQ */
nvicDisableVector(UART2_IRQn);
#endif /* KINETIS_HAS_SERIAL_ERROR_IRQ */
SIM->SCGC4 &= ~SIM_SCGC4_UART2;
}
#endif
#if KINETIS_SERIAL_USE_UART3
if (sdp == &SD4) {
nvicDisableVector(UART3Status_IRQn);
nvicDisableVector(UART3Error_IRQn);
SIM->SCGC4 &= ~SIM_SCGC4_UART3;
}
#endif
#if KINETIS_SERIAL_USE_UART4
if (sdp == &SD5) {
nvicDisableVector(UART4Status_IRQn);
nvicDisableVector(UART4Error_IRQn);
SIM->SCGC1 &= ~SIM_SCGC1_UART4;
}
#endif
#if KINETIS_SERIAL_USE_UART5
if (sdp == &SD6) {
nvicDisableVector(UART5Status_IRQn);
nvicDisableVector(UART5Error_IRQn);
SIM->SCGC1 &= ~SIM_SCGC1_UART5;
}
#endif
}
}
#endif /* HAL_USE_SERIAL */
/** @} */

View File

@ -0,0 +1,287 @@
/*
ChibiOS - Copyright (C) 2013-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 UARTv1/hal_serial_lld.h
* @brief Kinetis KL2x Serial Driver subsystem low level driver header.
*
* @addtogroup SERIAL
* @{
*/
#ifndef HAL_SERIAL_LLD_H_
#define HAL_SERIAL_LLD_H_
#if HAL_USE_SERIAL || defined(__DOXYGEN__)
/*===========================================================================*/
/* Driver constants. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver pre-compile time settings. */
/*===========================================================================*/
/**
* @name Configuration options
* @{
*/
/**
* @brief SD1 driver enable switch.
* @details If set to @p TRUE the support for SD1 is included.
*/
#if !defined(KINETIS_SERIAL_USE_UART0) || defined(__DOXYGEN__)
#define KINETIS_SERIAL_USE_UART0 FALSE
#endif
/**
* @brief SD2 driver enable switch.
* @details If set to @p TRUE the support for SD2 is included.
*/
#if !defined(KINETIS_SERIAL_USE_UART1) || defined(__DOXYGEN__)
#define KINETIS_SERIAL_USE_UART1 FALSE
#endif
/**
* @brief SD3 driver enable switch.
* @details If set to @p TRUE the support for SD3 is included.
*/
#if !defined(KINETIS_SERIAL_USE_UART2) || defined(__DOXYGEN__)
#define KINETIS_SERIAL_USE_UART2 FALSE
#endif
/**
* @brief SD4 driver enable switch.
* @details If set to @p TRUE the support for SD4 is included.
*/
#if !defined(KINETIS_SERIAL_USE_UART3) || defined(__DOXYGEN__)
#define KINETIS_SERIAL_USE_UART3 FALSE
#endif
/**
* @brief SD5 driver enable switch.
* @details If set to @p TRUE the support for SD5 is included.
*/
#if !defined(KINETIS_SERIAL_USE_UART4) || defined(__DOXYGEN__)
#define KINETIS_SERIAL_USE_UART4 FALSE
#endif
/**
* @brief SD6 driver enable switch.
* @details If set to @p TRUE the support for SD6 is included.
*/
#if !defined(KINETIS_SERIAL_USE_UART5) || defined(__DOXYGEN__)
#define KINETIS_SERIAL_USE_UART5 FALSE
#endif
/**
* @brief UART0 interrupt priority level setting.
*/
#if !defined(KINETIS_SERIAL_UART0_PRIORITY) || defined(__DOXYGEN__)
#define KINETIS_SERIAL_UART0_PRIORITY 12
#endif
/**
* @brief UART1 interrupt priority level setting.
*/
#if !defined(KINETIS_SERIAL_UART1_PRIORITY) || defined(__DOXYGEN__)
#define KINETIS_SERIAL_UART1_PRIORITY 12
#endif
/**
* @brief UART2 interrupt priority level setting.
*/
#if !defined(KINETIS_SERIAL_UART2_PRIORITY) || defined(__DOXYGEN__)
#define KINETIS_SERIAL_UART2_PRIORITY 12
#endif
/**
* @brief UART3 interrupt priority level setting.
*/
#if !defined(KINETIS_SERIAL_UART3_PRIORITY) || defined(__DOXYGEN__)
#define KINETIS_SERIAL_UART3_PRIORITY 12
#endif
/**
* @brief UART4 interrupt priority level setting.
*/
#if !defined(KINETIS_SERIAL_UART4_PRIORITY) || defined(__DOXYGEN__)
#define KINETIS_SERIAL_UART4_PRIORITY 12
#endif
/**
* @brief UART5 interrupt priority level setting.
*/
#if !defined(KINETIS_SERIAL_UART5_PRIORITY) || defined(__DOXYGEN__)
#define KINETIS_SERIAL_UART5_PRIORITY 12
#endif
/**
* @brief UART0 clock source.
*/
#if !defined(KINETIS_UART0_CLOCK_SRC) || defined(__DOXYGEN__)
#define KINETIS_UART0_CLOCK_SRC 1 /* MCGFLLCLK clock, or MCGPLLCLK/2; or IRC48M */
#endif
/**
* @brief UART1 clock source.
*/
#if !defined(KINETIS_UART1_CLOCK_SRC) || defined(__DOXYGEN__)
#define KINETIS_UART1_CLOCK_SRC 1 /* IRC48M */
#endif
/** @} */
/*===========================================================================*/
/* Derived constants and error checks. */
/*===========================================================================*/
/** @brief error checks */
#if KINETIS_SERIAL_USE_UART0 && !KINETIS_HAS_SERIAL0
#error "UART0 not present in the selected device"
#endif
#if KINETIS_SERIAL_USE_UART1 && !KINETIS_HAS_SERIAL1
#error "UART1 not present in the selected device"
#endif
#if KINETIS_SERIAL_USE_UART2 && !KINETIS_HAS_SERIAL2
#error "UART2 not present in the selected device"
#endif
#if KINETIS_SERIAL_USE_UART3 && !KINETIS_HAS_SERIAL3
#error "UART3 not present in the selected device"
#endif
#if KINETIS_SERIAL_USE_UART4 && !KINETIS_HAS_SERIAL4
#error "UART4 not present in the selected device"
#endif
#if KINETIS_SERIAL_USE_UART5 && !KINETIS_HAS_SERIAL5
#error "UART5 not present in the selected device"
#endif
#if !(KINETIS_SERIAL_USE_UART0 || KINETIS_SERIAL_USE_UART1 || \
KINETIS_SERIAL_USE_UART2 || KINETIS_SERIAL_USE_UART3 || \
KINETIS_SERIAL_USE_UART4 || KINETIS_SERIAL_USE_UART5)
#error "Serial driver activated but no UART peripheral assigned"
#endif
/*===========================================================================*/
/* Driver data structures and types. */
/*===========================================================================*/
/**
* @brief Generic Serial Driver configuration structure.
* @details An instance of this structure must be passed to @p sdStart()
* in order to configure and start a serial driver operations.
* @note Implementations may extend this structure to contain more,
* architecture dependent, fields.
*/
typedef struct {
/**
* @brief Bit rate.
*/
uint32_t sc_speed;
/* End of the mandatory fields.*/
} SerialConfig;
/**
* @brief Generic UART register structure.
* @note Individual UART register blocks (even within the same chip) can differ.
*/
typedef struct {
volatile uint8_t* bdh_p;
volatile uint8_t* bdl_p;
volatile uint8_t* c1_p;
volatile uint8_t* c2_p;
volatile uint8_t* c3_p;
volatile uint8_t* c4_p;
volatile uint8_t* s1_p;
volatile uint8_t* s2_p;
volatile uint8_t* d_p;
UART_TypeDef *uart_p;
#if KINETIS_SERIAL_USE_UART0 && KINETIS_SERIAL0_IS_UARTLP
UARTLP_TypeDef *uartlp_p;
#endif /* KINETIS_SERIAL_USE_UART0 && KINETIS_SERIAL0_IS_UARTLP */
#if (KINETIS_SERIAL_USE_UART0 && KINETIS_SERIAL0_IS_LPUART) \
|| (KINETIS_SERIAL_USE_UART1 && KINETIS_SERIAL1_IS_LPUART)
LPUART_TypeDef *lpuart_p;
#endif /* KINETIS_SERIAL_USE_UART0 && KINETIS_SERIAL0_IS_LPUART */
} UART_w_TypeDef;
/**
* @brief @p SerialDriver specific data.
*/
#define _serial_driver_data \
_base_asynchronous_channel_data \
/* Driver state.*/ \
sdstate_t state; \
/* Input queue.*/ \
input_queue_t iqueue; \
/* Output queue.*/ \
output_queue_t oqueue; \
/* Input circular buffer.*/ \
uint8_t ib[SERIAL_BUFFERS_SIZE]; \
/* Output circular buffer.*/ \
uint8_t ob[SERIAL_BUFFERS_SIZE]; \
/* End of the mandatory fields.*/ \
/* Pointer to the UART registers block.*/ \
UART_w_TypeDef uart;
/*===========================================================================*/
/* Driver macros. */
/*===========================================================================*/
/*===========================================================================*/
/* External declarations. */
/*===========================================================================*/
#if KINETIS_SERIAL_USE_UART0 && !defined(__DOXYGEN__)
extern SerialDriver SD1;
#endif
#if KINETIS_SERIAL_USE_UART1 && !defined(__DOXYGEN__)
extern SerialDriver SD2;
#endif
#if KINETIS_SERIAL_USE_UART2 && !defined(__DOXYGEN__)
extern SerialDriver SD3;
#endif
#if KINETIS_SERIAL_USE_UART3 && !defined(__DOXYGEN__)
extern SerialDriver SD4;
#endif
#if KINETIS_SERIAL_USE_UART4 && !defined(__DOXYGEN__)
extern SerialDriver SD5;
#endif
#if KINETIS_SERIAL_USE_UART5 && !defined(__DOXYGEN__)
extern SerialDriver SD6;
#endif
#ifdef __cplusplus
extern "C" {
#endif
void sd_lld_init(void);
void sd_lld_start(SerialDriver *sdp, const SerialConfig *config);
void sd_lld_stop(SerialDriver *sdp);
#ifdef __cplusplus
}
#endif
#endif /* HAL_USE_SERIAL */
#endif /* HAL_SERIAL_LLD_H_ */
/** @} */

View File

@ -0,0 +1,9 @@
ifeq ($(USE_SMART_BUILD),yes)
ifneq ($(findstring HAL_USE_USB TRUE,$(HALCONF)),)
PLATFORMSRC_CONTRIB += ${CHIBIOS_CONTRIB}/os/hal/ports/KINETIS/LLD/USBHSv1/hal_usb_lld.c
endif
else
PLATFORMSRC_CONTRIB += ${CHIBIOS_CONTRIB}/os/hal/ports/KINETIS/LLD/USBHSv1/hal_usb_lld.c
endif
PLATFORMINC_CONTRIB += ${CHIBIOS_CONTRIB}/os/hal/ports/KINETIS/LLD/USBHSv1

View File

@ -0,0 +1,873 @@
/*
ChibiOS - Copyright (C) 2015 RedoX https://github.com/RedoXyde/
(C) 2015-2016 flabbergast <s3+flabbergast@sdfeu.org>
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 USBHSv1/hal_usb_lld.c
* @brief KINETIS USB subsystem low level driver source.
*
* @addtogroup USB
* @{
*/
#include <string.h>
#include "hal.h"
#if HAL_USE_USB || defined(__DOXYGEN__)
/*===========================================================================*/
/* Driver local definitions. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver exported variables. */
/*===========================================================================*/
/** @brief USB0 driver identifier.*/
#if KINETIS_USB_USE_USB0 || defined(__DOXYGEN__)
USBDriver USBD1;
#endif
/*===========================================================================*/
/* Driver local variables and types. */
/*===========================================================================*/
/**
* @brief IN EP0 state.
*/
USBInEndpointState ep0in;
/**
* @brief OUT EP0 state.
*/
USBOutEndpointState ep0out;
/**
* @brief Buffer for the EP0 setup packets.
*/
static uint8_t ep0setup_buffer[8];
/**
* @brief EP0 initialization structure.
*/
static const USBEndpointConfig ep0config = {
USB_EP_MODE_TYPE_CTRL,
_usb_ep0setup,
_usb_ep0in,
_usb_ep0out,
64,
64,
&ep0in,
&ep0out,
1,
ep0setup_buffer
};
/*
* Buffer Descriptor Table (BDT)
*/
/*
* Buffer Descriptor (BD)
* */
typedef struct {
uint32_t desc;
uint8_t* addr;
} bd_t;
/*
* Buffer Descriptor fields - p.889
*/
#define BDT_OWN 0x80
#define BDT_DATA 0x40
#define BDT_KEEP 0x20
#define BDT_NINC 0x10
#define BDT_DTS 0x08
#define BDT_STALL 0x04
#define BDT_DESC(bc, data) (BDT_OWN | BDT_DTS | ((data&0x1)<<6) | ((bc) << 16))
/*
* BDT PID - p.891
*/
#define BDT_PID_OUT 0x01
#define BDT_PID_IN 0x09
#define BDT_PID_SETUP 0x0D
#define BDT_TOK_PID(n) (((n)>>2)&0xF)
/*
* BDT index fields
*/
#define DATA0 0
#define DATA1 1
#define RX 0
#define TX 1
#define EVEN 0
#define ODD 1
#define BDT_INDEX(endpoint, tx, odd) (((endpoint)<<2) | ((tx)<<1) | (odd))
/*
* Get RX-ed/TX-ed bytes count from BDT entry
*/
#define BDT_BC(n) (((n)>>16)&0x3FF)
/* The USB-FS needs 2 BDT entry per endpoint direction
* that adds to: 2*2*16 BDT entries for 16 bi-directional EP
*/
static volatile bd_t _bdt[(KINETIS_USB_ENDPOINTS)*2*2] __attribute__((aligned(512)));
/* FIXME later with dyn alloc
* 16 EP
* 2 directions per EP
* 2 buffer per direction
* => 64 buffers
*/
static uint8_t _usbb[KINETIS_USB_ENDPOINTS*4][64] __attribute__((aligned(4)));
static volatile uint8_t _usbbn=0;
uint8_t* usb_alloc(uint8_t size)
{
(void)size;
if(_usbbn < (KINETIS_USB_ENDPOINTS)*4)
return _usbb[_usbbn++];
while(1); /* Should not happen, ever */
}
/*===========================================================================*/
/* Driver local functions. */
/*===========================================================================*/
/* Called from locked ISR. */
void usb_packet_transmit(USBDriver *usbp, usbep_t ep, size_t n)
{
const USBEndpointConfig *epc = usbp->epc[ep];
USBInEndpointState *isp = epc->in_state;
bd_t *bd = (bd_t *)&_bdt[BDT_INDEX(ep, TX, isp->odd_even)];
if (n > (size_t)epc->in_maxsize)
n = (size_t)epc->in_maxsize;
/* Copy from buf to _usbb[] */
size_t i=0;
for(i=0;i<n;i++)
bd->addr[i] = isp->txbuf[i];
/* Update the Buffer status */
bd->desc = BDT_DESC(n, isp->data_bank);
/* Toggle the odd and data bits for next TX */
isp->data_bank ^= DATA1;
isp->odd_even ^= ODD;
}
/* Called from locked ISR. */
void usb_packet_receive(USBDriver *usbp, usbep_t ep, size_t n)
{
const USBEndpointConfig *epc = usbp->epc[ep];
USBOutEndpointState *osp = epc->out_state;
bd_t *bd = (bd_t *)&_bdt[BDT_INDEX(ep, RX, osp->odd_even)];
if (n > (size_t)epc->out_maxsize)
n = (size_t)epc->out_maxsize;
/* Copy from _usbb[] to buf */
size_t i=0;
for(i=0;i<n;i++)
osp->rxbuf[i] = bd->addr[i];
/* Update the Buffer status
* Set current buffer to same DATA bank and then toggle.
* Since even/odd buffers are ping-pong and setup re-initialized them
* this should work correctly */
bd->desc = BDT_DESC(epc->out_maxsize, osp->data_bank);
osp->data_bank ^= DATA1;
usb_lld_start_out(usbp, ep);
}
/*===========================================================================*/
/* Driver interrupt handlers. */
/*============================================================================*/
#if KINETIS_USB_USE_USB0 || defined(__DOXYGEN__)
/**
* @brief USB interrupt handler.
*
* @isr
*/
OSAL_IRQ_HANDLER(KINETIS_USB_IRQ_VECTOR) {
USBDriver *usbp = &USBD1;
uint8_t istat = USB0->ISTAT;
OSAL_IRQ_PROLOGUE();
/* 04 - Bit2 - Start Of Frame token received */
if(istat & USBx_ISTAT_SOFTOK) {
_usb_isr_invoke_sof_cb(usbp);
USB0->ISTAT = USBx_ISTAT_SOFTOK;
}
/* 08 - Bit3 - Token processing completed */
while(istat & USBx_ISTAT_TOKDNE) {
uint8_t stat = USB0->STAT;
uint8_t ep = stat >> 4;
if(ep > KINETIS_USB_ENDPOINTS) {
OSAL_IRQ_EPILOGUE();
return;
}
const USBEndpointConfig *epc = usbp->epc[ep];
/* Get the correct BDT entry */
uint8_t odd_even = (stat & USBx_STAT_ODD_MASK) >> USBx_STAT_ODD_SHIFT;
uint8_t tx_rx = (stat & USBx_STAT_TX_MASK) >> USBx_STAT_TX_SHIFT;
bd_t *bd = (bd_t*)&_bdt[BDT_INDEX(ep,tx_rx,odd_even)];
/* Update the ODD/EVEN state for RX */
if(tx_rx == RX && epc->out_state != NULL)
epc->out_state->odd_even = odd_even;
switch(BDT_TOK_PID(bd->desc))
{
case BDT_PID_SETUP: // SETUP
{
/* Clear receiving in the chibios state machine */
(usbp)->receiving &= ~1;
/* Call SETUP function (ChibiOS core), which prepares
* for send or receive and releases the buffer
*/
_usb_isr_invoke_setup_cb(usbp, ep);
/* When a setup packet is received, tx is suspended,
* so it needs to be resumed here.
*/
USB0->CTL &= ~USBx_CTL_TXSUSPENDTOKENBUSY;
} break;
case BDT_PID_IN: // IN
{
if(epc->in_state == NULL)
break;
/* Special case for SetAddress for EP0 */
if(ep == 0 && (((uint16_t)usbp->setup[0]<<8)|usbp->setup[1]) == 0x0500)
{
usbp->address = usbp->setup[2];
usb_lld_set_address(usbp);
_usb_isr_invoke_event_cb(usbp, USB_EVENT_ADDRESS);
usbp->state = USB_SELECTED;
}
uint16_t txed = BDT_BC(bd->desc);
epc->in_state->txcnt += txed;
if(epc->in_state->txcnt < epc->in_state->txsize)
{
epc->in_state->txbuf += txed;
osalSysLockFromISR();
usb_packet_transmit(usbp,ep,epc->in_state->txsize - epc->in_state->txcnt);
osalSysUnlockFromISR();
}
else
{
if(epc->in_cb != NULL)
_usb_isr_invoke_in_cb(usbp,ep);
}
} break;
case BDT_PID_OUT: // OUT
{
if(epc->out_state == NULL)
break;
uint16_t rxed = BDT_BC(bd->desc);
osalSysLockFromISR();
usb_packet_receive(usbp,ep,rxed);
osalSysUnlockFromISR();
if(rxed)
{
epc->out_state->rxbuf += rxed;
/* Update transaction data */
epc->out_state->rxcnt += rxed;
epc->out_state->rxsize -= rxed;
epc->out_state->rxpkts -= 1;
/* The transaction is completed if the specified number of packets
has been received or the current packet is a short packet.*/
if ((rxed < epc->out_maxsize) || (epc->out_state->rxpkts == 0))
{
if(epc->out_cb != NULL)
_usb_isr_invoke_out_cb(usbp, ep);
}
}
} break;
default:
break;
}
USB0->ISTAT = USBx_ISTAT_TOKDNE;
istat = USB0->ISTAT;
}
/* 01 - Bit0 - Valid USB Reset received */
if(istat & USBx_ISTAT_USBRST) {
_usb_reset(usbp);
USB0->ISTAT = USBx_ISTAT_USBRST;
OSAL_IRQ_EPILOGUE();
return;
}
/* 80 - Bit7 - STALL handshake received */
if(istat & USBx_ISTAT_STALL) {
USB0->ISTAT = USBx_ISTAT_STALL;
}
/* 02 - Bit1 - ERRSTAT condition triggered */
if(istat & USBx_ISTAT_ERROR) {
uint8_t err = USB0->ERRSTAT;
USB0->ERRSTAT = err;
USB0->ISTAT = USBx_ISTAT_ERROR;
}
/* 10 - Bit4 - Constant IDLE on USB bus detected */
if(istat & USBx_ISTAT_SLEEP) {
/* This seems to fire a few times before the device is
* configured - need to ignore those occurences somehow. */
/* The other option would be to only activate INTEN_SLEEPEN
* on CONFIGURED event, but that would need to be done in
* user firmware. */
if(usbp->state == USB_ACTIVE) {
_usb_suspend(usbp);
/* Enable interrupt on resume */
USB0->INTEN |= USBx_INTEN_RESUMEEN;
}
// low-power version (check!):
// enable wakeup interrupt on resume USB signaling
// (check that it was a wakeup int with USBx_USBTRC0_USB_RESUME_INT)
//? USB0->USBTRC0 |= USBx_USBTRC0_USBRESMEN
// suspend the USB module
//? USB0->USBCTRL |= USBx_USBCTRL_SUSP;
USB0->ISTAT = USBx_ISTAT_SLEEP;
}
/* 20 - Bit5 - Resume - Only allowed in sleep=suspend mode */
if(istat & USBx_ISTAT_RESUME) {
/* Disable interrupt on resume (should be disabled
* during normal operation according to datasheet). */
USB0->INTEN &= ~USBx_INTEN_RESUMEEN;
// low power version (check!):
// desuspend the USB module
//? USB0->USBCTRL &= ~USBx_USBCTRL_SUSP;
// maybe also
//? USB0->CTL = USBx_CTL_USBENSOFEN;
_usb_wakeup(usbp);
USB0->ISTAT = USBx_ISTAT_RESUME;
}
/* 40 - Bit6 - ATTACH - used */
OSAL_IRQ_EPILOGUE();
}
#endif /* KINETIS_USB_USE_USB0 */
/*===========================================================================*/
/* Driver exported functions. */
/*===========================================================================*/
/**
* @brief Low level USB driver initialization.
*
* @notapi
*/
void usb_lld_init(void) {
/* Driver initialization.*/
usbObjectInit(&USBD1);
#if KINETIS_USB_USE_USB0
/* Set USB clock source to MCGPLLCLK, MCGFLLCLK, USB1 PFD, or IRC48M */
SIM->SOPT2 |= SIM_SOPT2_USBSRC;
#if defined(K20x5) || defined(K20x7) || defined(MK66F18)
#if KINETIS_MCG_MODE == KINETIS_MCG_MODE_FEI
/* MCGOUTCLK is the SYSCLK frequency, so don't divide for USB clock */
SIM->CLKDIV2 = SIM_CLKDIV2_USBDIV(0);
#elif KINETIS_MCG_MODE == KINETIS_MCG_MODE_PEE
#if !defined(MK66F18)
/* Note: We don't need this for MK66F18, we can use IRC48M clock for USB */
#define KINETIS_USBCLK_FREQUENCY 48000000UL
uint32_t i,j;
for(i=0; i < 2; i++) {
for(j=0; j < 8; j++) {
if((KINETIS_PLLCLK_FREQUENCY * (i+1)) == (KINETIS_USBCLK_FREQUENCY*(j+1))) {
SIM->CLKDIV2 = i | SIM_CLKDIV2_USBDIV(j);
goto usbfrac_match_found;
}
}
}
usbfrac_match_found:
osalDbgAssert(i<2 && j <8,"USB Init error");
#endif
#else /* KINETIS_MCG_MODE == KINETIS_MCG_MODE_PEE */
#error USB clock setting not implemented for this KINETIS_MCG_MODE
#endif /* KINETIS_MCG_MODE == ... */
#if defined(MK66F18)
/* Switch from default MCGPLLCLK to IRC48M for USB */
SIM->CLKDIV2 = SIM_CLKDIV2_USBDIV(0);
SIM->SOPT2 |= SIM_SOPT2_PLLFLLSEL_SET(3);
#endif
#elif defined(KL25) || defined (KL26) || defined(KL27)
/* No extra clock dividers for USB clock */
#else /* defined(KL25) || defined (KL26) || defined(KL27) */
#error USB driver not implemented for your MCU type
#endif
#endif /* KINETIS_USB_USE_USB0 */
}
/**
* @brief Configures and activates the USB peripheral.
*
* @param[in] usbp pointer to the @p USBDriver object
*
* @notapi
*/
void usb_lld_start(USBDriver *usbp) {
if (usbp->state == USB_STOP) {
#if KINETIS_USB_USE_USB0
if (&USBD1 == usbp) {
/* Clear BDT */
uint8_t i;
for(i=0;i<KINETIS_USB_ENDPOINTS;i++) {
_bdt[i].desc=0;
_bdt[i].addr=0;
}
#if defined(MK66F18)
/* Disable the USB current limiter */
SIM->USBPHYCTL |= SIM_USBPHYCTL_USBDISILIM;
#endif
/* Enable Clock */
#if KINETIS_USB0_IS_USBOTG
SIM->SCGC4 |= SIM_SCGC4_USBOTG;
#else /* KINETIS_USB0_IS_USBOTG */
SIM->SCGC4 |= SIM_SCGC4_USBFS;
#endif /* KINETIS_USB0_IS_USBOTG */
#if KINETIS_HAS_USB_CLOCK_RECOVERY
USB0->CLK_RECOVER_IRC_EN |= USBx_CLK_RECOVER_IRC_EN_IRC_EN;
USB0->CLK_RECOVER_CTRL |= USBx_CLK_RECOVER_CTRL_CLOCK_RECOVER_EN;
#endif /* KINETIS_HAS_USB_CLOCK_RECOVERY */
/* Reset USB module, wait for completion */
USB0->USBTRC0 |= USBx_USBTRC0_USBRESET;
while ((USB0->USBTRC0 & USBx_USBTRC0_USBRESET));
/* Set BDT Address */
USB0->BDTPAGE1 = ((uint32_t)_bdt) >> 8;
USB0->BDTPAGE2 = ((uint32_t)_bdt) >> 16;
USB0->BDTPAGE3 = ((uint32_t)_bdt) >> 24;
/* Clear all ISR flags */
USB0->ISTAT = 0xFF;
USB0->ERRSTAT = 0xFF;
#if KINETIS_USB0_IS_USBOTG
USB0->OTGISTAT = 0xFF;
#endif /* KINETIS_USB0_IS_USBOTG */
USB0->USBTRC0 |= 0x40; //a hint was given that this is an undocumented interrupt bit
/* Enable USB */
USB0->CTL = USBx_CTL_ODDRST | USBx_CTL_USBENSOFEN;
USB0->USBCTRL = 0;
/* Enable reset interrupt */
USB0->INTEN |= USBx_INTEN_USBRSTEN;
/* Enable interrupt in NVIC */
#if KINETIS_USB0_IS_USBOTG
nvicEnableVector(USB_OTG_IRQn, KINETIS_USB_USB0_IRQ_PRIORITY);
#else /* KINETIS_USB0_IS_USBOTG */
nvicEnableVector(USB_IRQn, KINETIS_USB_USB0_IRQ_PRIORITY);
#endif /* KINETIS_USB0_IS_USBOTG */
}
#endif /* KINETIS_USB_USE_USB0 */
}
}
/**
* @brief Deactivates the USB peripheral.
*
* @param[in] usbp pointer to the @p USBDriver object
*
* @notapi
*/
void usb_lld_stop(USBDriver *usbp) {
/* TODO: If in ready state then disables the USB clock.*/
if (usbp->state == USB_STOP) {
#if KINETIS_USB_USE_USB0
if (&USBD1 == usbp) {
#if KINETIS_USB0_IS_USBOTG
nvicDisableVector(USB_OTG_IRQn);
#else /* KINETIS_USB0_IS_USBOTG */
nvicDisableVector(USB_IRQn);
#endif /* KINETIS_USB0_IS_USBOTG */
}
#endif /* KINETIS_USB_USE_USB0 */
}
}
/**
* @brief USB low level reset routine.
*
* @param[in] usbp pointer to the @p USBDriver object
*
* @notapi
*/
void usb_lld_reset(USBDriver *usbp) {
// FIXME, dyn alloc
_usbbn = 0;
#if KINETIS_USB_USE_USB0
/* Reset BDT ODD/EVEN bits */
USB0->CTL = USBx_CTL_ODDRST;
/* EP0 initialization.*/
usbp->epc[0] = &ep0config;
usb_lld_init_endpoint(usbp, 0);
/* Clear all pending interrupts */
USB0->ERRSTAT = 0xFF;
USB0->ISTAT = 0xFF;
/* Set the address to zero during enumeration */
usbp->address = 0;
USB0->ADDR = 0;
/* Enable other interrupts */
USB0->ERREN = 0xFF;
USB0->INTEN = USBx_INTEN_TOKDNEEN |
USBx_INTEN_SOFTOKEN |
USBx_INTEN_STALLEN |
USBx_INTEN_ERROREN |
USBx_INTEN_USBRSTEN |
USBx_INTEN_SLEEPEN;
/* "is this necessary?", Paul from PJRC */
USB0->CTL = USBx_CTL_USBENSOFEN;
#endif /* KINETIS_USB_USE_USB0 */
}
/**
* @brief Sets the USB address.
*
* @param[in] usbp pointer to the @p USBDriver object
*
* @notapi
*/
void usb_lld_set_address(USBDriver *usbp) {
#if KINETIS_USB_USE_USB0
USB0->ADDR = usbp->address&0x7F;
#endif /* KINETIS_USB_USE_USB0 */
}
/**
* @brief Enables an endpoint.
*
* @param[in] usbp pointer to the @p USBDriver object
* @param[in] ep endpoint number
*
* @notapi
*/
void usb_lld_init_endpoint(USBDriver *usbp, usbep_t ep) {
if(ep > KINETIS_USB_ENDPOINTS)
return;
const USBEndpointConfig *epc = usbp->epc[ep];
uint8_t mask=0;
if(epc->out_state != NULL)
{
/* OUT Endpoint */
epc->out_state->odd_even = EVEN;
epc->out_state->data_bank = DATA0;
/* RXe */
_bdt[BDT_INDEX(ep, RX, EVEN)].desc = BDT_DESC(epc->out_maxsize, DATA0);
_bdt[BDT_INDEX(ep, RX, EVEN)].addr = usb_alloc(epc->out_maxsize);
/* RXo */
_bdt[BDT_INDEX(ep, RX, ODD)].desc = BDT_DESC(epc->out_maxsize, DATA1);
_bdt[BDT_INDEX(ep, RX, ODD)].addr = usb_alloc(epc->out_maxsize);
/* Enable OUT direction */
mask |= USBx_ENDPTn_EPRXEN;
}
if(epc->in_state != NULL)
{
/* IN Endpoint */
epc->in_state->odd_even = EVEN;
epc->in_state->data_bank = DATA0;
/* TXe, not used yet */
_bdt[BDT_INDEX(ep, TX, EVEN)].desc = 0;
_bdt[BDT_INDEX(ep, TX, EVEN)].addr = usb_alloc(epc->in_maxsize);
/* TXo, not used yet */
_bdt[BDT_INDEX(ep, TX, ODD)].desc = 0;
_bdt[BDT_INDEX(ep, TX, ODD)].addr = usb_alloc(epc->in_maxsize);
/* Enable IN direction */
mask |= USBx_ENDPTn_EPTXEN;
}
/* EPHSHK should be set for CTRL, BULK, INTR not for ISOC*/
if((epc->ep_mode & USB_EP_MODE_TYPE) != USB_EP_MODE_TYPE_ISOC)
mask |= USBx_ENDPTn_EPHSHK;
/* Endpoint is not a CTRL endpoint, disable SETUP transfers */
if((epc->ep_mode & USB_EP_MODE_TYPE) != USB_EP_MODE_TYPE_CTRL)
mask |= USBx_ENDPTn_EPCTLDIS;
#if KINETIS_USB_USE_USB0
USB0->ENDPT[ep].V = mask;
#endif /* KINETIS_USB_USE_USB0 */
}
/**
* @brief Disables all the active endpoints except the endpoint zero.
*
* @param[in] usbp pointer to the @p USBDriver object
*
* @notapi
*/
void usb_lld_disable_endpoints(USBDriver *usbp) {
(void)usbp;
uint8_t i;
#if KINETIS_USB_USE_USB0
for(i=1;i<KINETIS_USB_ENDPOINTS;i++)
USB0->ENDPT[i].V = 0;
#endif /* KINETIS_USB_USE_USB0 */
}
/**
* @brief Returns the status of an OUT endpoint.
*
* @param[in] usbp pointer to the @p USBDriver object
* @param[in] ep endpoint number
* @return The endpoint status.
* @retval EP_STATUS_DISABLED The endpoint is not active.
* @retval EP_STATUS_STALLED The endpoint is stalled.
* @retval EP_STATUS_ACTIVE The endpoint is active.
*
* @notapi
*/
usbepstatus_t usb_lld_get_status_out(USBDriver *usbp, usbep_t ep) {
(void)usbp;
#if KINETIS_USB_USE_USB0
if(ep > USB_MAX_ENDPOINTS)
return EP_STATUS_DISABLED;
if(!(USB0->ENDPT[ep].V & (USBx_ENDPTn_EPRXEN)))
return EP_STATUS_DISABLED;
else if(USB0->ENDPT[ep].V & USBx_ENDPTn_EPSTALL)
return EP_STATUS_STALLED;
return EP_STATUS_ACTIVE;
#endif /* KINETIS_USB_USE_USB0 */
}
/**
* @brief Returns the status of an IN endpoint.
*
* @param[in] usbp pointer to the @p USBDriver object
* @param[in] ep endpoint number
* @return The endpoint status.
* @retval EP_STATUS_DISABLED The endpoint is not active.
* @retval EP_STATUS_STALLED The endpoint is stalled.
* @retval EP_STATUS_ACTIVE The endpoint is active.
*
* @notapi
*/
usbepstatus_t usb_lld_get_status_in(USBDriver *usbp, usbep_t ep) {
(void)usbp;
if(ep > USB_MAX_ENDPOINTS)
return EP_STATUS_DISABLED;
#if KINETIS_USB_USE_USB0
if(!(USB0->ENDPT[ep].V & (USBx_ENDPTn_EPTXEN)))
return EP_STATUS_DISABLED;
else if(USB0->ENDPT[ep].V & USBx_ENDPTn_EPSTALL)
return EP_STATUS_STALLED;
return EP_STATUS_ACTIVE;
#endif /* KINETIS_USB_USE_USB0 */
}
/**
* @brief Reads a setup packet from the dedicated packet buffer.
* @details This function must be invoked in the context of the @p setup_cb
* callback in order to read the received setup packet.
* @pre In order to use this function the endpoint must have been
* initialized as a control endpoint.
* @post The endpoint is ready to accept another packet.
*
* @param[in] usbp pointer to the @p USBDriver object
* @param[in] ep endpoint number
* @param[out] buf buffer where to copy the packet data
*
* @notapi
*/
void usb_lld_read_setup(USBDriver *usbp, usbep_t ep, uint8_t *buf) {
/* Get the BDT entry */
USBOutEndpointState *os = usbp->epc[ep]->out_state;
bd_t *bd = (bd_t*)&_bdt[BDT_INDEX(ep, RX, os->odd_even)];
/* Copy the 8 bytes of data */
uint8_t n;
for (n = 0; n < 8; n++) {
buf[n] = bd->addr[n];
}
/* Release the buffer
* Setup packet is always DATA0
* Release the current DATA0 buffer
*/
bd->desc = BDT_DESC(usbp->epc[ep]->out_maxsize,DATA0);
/* If DATA1 was expected, then the states are out of sync.
* So reset the other buffer too, and set it as DATA1.
* This should not happen in normal cases, but is possible in
* error situations. NOTE: it's possible that this is too late
* and the next packet has already been received and dropped, but
* there's nothing that we can do about that anymore at this point.
*/
if (os->data_bank == DATA1)
{
bd_t *bd_next = (bd_t*)&_bdt[BDT_INDEX(ep, RX, os->odd_even^ODD)];
bd_next->desc = BDT_DESC(usbp->epc[ep]->out_maxsize,DATA1);
}
/* After a SETUP, both in and out are always DATA1 */
usbp->epc[ep]->in_state->data_bank = DATA1;
os->data_bank = DATA1;
}
/**
* @brief Starts a receive operation on an OUT endpoint.
*
* @param[in] usbp pointer to the @p USBDriver object
* @param[in] ep endpoint number
*
* @notapi
*/
void usb_lld_start_out(USBDriver *usbp, usbep_t ep) {
USBOutEndpointState *osp = usbp->epc[ep]->out_state;
/* Transfer initialization.*/
if (osp->rxsize == 0) /* Special case for zero sized packets.*/
osp->rxpkts = 1;
else
osp->rxpkts = (uint16_t)((osp->rxsize + usbp->epc[ep]->out_maxsize - 1) /
usbp->epc[ep]->out_maxsize);
}
/**
* @brief Starts a transmit operation on an IN endpoint.
*
* @param[in] usbp pointer to the @p USBDriver object
* @param[in] ep endpoint number
*
* @note Called from ISR and locked zone.
* @notapi
*/
void usb_lld_start_in(USBDriver *usbp, usbep_t ep) {
if (ep == 0 && usbp->ep0state == USB_EP0_IN_SENDING_STS) {
/* When a status packet is about to be sent on endpoint 0 the
* next packet will be a setup packet, which means that the
* buffer we expect after this should be DATA0, and the following
* DATA1. Since no out packets should be in flight at this time
* it's safe to initialize the buffers according to the expectations
* here.
*/
const USBEndpointConfig* epc = usbp->epc[ep];
bd_t * bd = (bd_t*)&_bdt[BDT_INDEX(ep, RX, epc->out_state->odd_even)];
bd_t *bd_next = (bd_t*)&_bdt[BDT_INDEX(ep, RX, epc->out_state->odd_even^ODD)];
bd->desc = BDT_DESC(usbp->epc[ep]->out_maxsize,DATA1);
bd_next->desc = BDT_DESC(usbp->epc[ep]->out_maxsize,DATA0);
epc->out_state->data_bank = DATA0;
}
usb_packet_transmit(usbp,ep,usbp->epc[ep]->in_state->txsize);
}
/**
* @brief Brings an OUT endpoint in the stalled state.
*
* @param[in] usbp pointer to the @p USBDriver object
* @param[in] ep endpoint number
*
* @notapi
*/
void usb_lld_stall_out(USBDriver *usbp, usbep_t ep) {
(void)usbp;
#if KINETIS_USB_USE_USB0
USB0->ENDPT[ep].V |= USBx_ENDPTn_EPSTALL;
#endif /* KINETIS_USB_USE_USB0 */
}
/**
* @brief Brings an IN endpoint in the stalled state.
*
* @param[in] usbp pointer to the @p USBDriver object
* @param[in] ep endpoint number
*
* @notapi
*/
void usb_lld_stall_in(USBDriver *usbp, usbep_t ep) {
(void)usbp;
#if KINETIS_USB_USE_USB0
USB0->ENDPT[ep].V |= USBx_ENDPTn_EPSTALL;
#endif /* KINETIS_USB_USE_USB0 */
}
/**
* @brief Brings an OUT endpoint in the active state.
*
* @param[in] usbp pointer to the @p USBDriver object
* @param[in] ep endpoint number
*
* @notapi
*/
void usb_lld_clear_out(USBDriver *usbp, usbep_t ep) {
(void)usbp;
#if KINETIS_USB_USE_USB0
USB0->ENDPT[ep].V &= ~USBx_ENDPTn_EPSTALL;
#endif /* KINETIS_USB_USE_USB0 */
}
/**
* @brief Brings an IN endpoint in the active state.
*
* @param[in] usbp pointer to the @p USBDriver object
* @param[in] ep endpoint number
*
* @notapi
*/
void usb_lld_clear_in(USBDriver *usbp, usbep_t ep) {
(void)usbp;
#if KINETIS_USB_USE_USB0
USB0->ENDPT[ep].V &= ~USBx_ENDPTn_EPSTALL;
#endif /* KINETIS_USB_USE_USB0 */
}
#endif /* HAL_USE_USB */
/** @} */

View File

@ -0,0 +1,455 @@
/*
ChibiOS - Copyright (C) 2015 RedoX https://github.com/RedoXyde/
(C) 2015-2016 flabbergast <s3+flabbergast@sdfeu.org>
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 USBHSv1/hal_usb_lld.h
* @brief KINETIS USB subsystem low level driver header.
*
* @addtogroup USB
* @{
*/
#ifndef HAL_USB_LLD_H_
#define HAL_USB_LLD_H_
#if HAL_USE_USB || defined(__DOXYGEN__)
/*===========================================================================*/
/* Driver constants. */
/*===========================================================================*/
/**
* @brief Maximum endpoint address.
*/
#define USB_MAX_ENDPOINTS 15
/**
* @brief Status stage handling method.
*/
#define USB_EP0_STATUS_STAGE USB_EP0_STATUS_STAGE_SW
/**
* @brief Address ack handling
*/
#define USB_SET_ADDRESS_ACK_HANDLING USB_SET_ADDRESS_ACK_SW
/**
* @brief This device requires the address change after the status packet.
*/
#define USB_SET_ADDRESS_MODE USB_LATE_SET_ADDRESS
/*===========================================================================*/
/* Driver pre-compile time settings. */
/*===========================================================================*/
/**
* @brief USB1 driver enable switch.
* @details If set to @p TRUE the support for USB1 is included.
* @note The default is @p TRUE.
*/
#if !defined(KINETIS_USB_USE_USB0) || defined(__DOXYGEN__)
#define KINETIS_USB_USE_USB0 FALSE
#endif
/**
* @brief USB1 interrupt priority level setting.
*/
#if !defined(KINETIS_USB_USB0_IRQ_PRIORITY)|| defined(__DOXYGEN__)
#define KINETIS_USB_USB0_IRQ_PRIORITY 5
#endif
#if !defined(KINETIS_USB_ENDPOINTS) || defined(__DOXYGEN__)
#define KINETIS_USB_ENDPOINTS USB_MAX_ENDPOINTS+1
#endif
/**
* @brief Host wake-up procedure duration.
*/
#if !defined(USB_HOST_WAKEUP_DURATION) || defined(__DOXYGEN__)
#define USB_HOST_WAKEUP_DURATION 2
#endif
/*===========================================================================*/
/* Derived constants and error checks. */
/*===========================================================================*/
#if KINETIS_USB_USE_USB0 && !KINETIS_HAS_USB
#error "USB not present in the selected device"
#endif
#if !KINETIS_USB_USE_USB0
#error "USB driver activated but no USB peripheral assigned"
#endif
#if KINETIS_USB_USE_USB0 && \
!OSAL_IRQ_IS_VALID_PRIORITY(KINETIS_USB_USB0_IRQ_PRIORITY)
#error "Invalid IRQ priority assigned to KINETIS_USB_USB0_IRQ_PRIORITY"
#endif
#if !defined(KINETIS_USB_IRQ_VECTOR)
#error "KINETIS_USB_IRQ_VECTOR not defined"
#endif
#if (USB_HOST_WAKEUP_DURATION < 2) || (USB_HOST_WAKEUP_DURATION > 15)
#error "invalid USB_HOST_WAKEUP_DURATION setting, it must be between 2 and 15"
#endif
/*===========================================================================*/
/* Driver data structures and types. */
/*===========================================================================*/
/**
* @brief Type of an IN endpoint state structure.
*/
typedef struct {
/**
* @brief Requested transmit transfer size.
*/
size_t txsize;
/**
* @brief Transmitted bytes so far.
*/
size_t txcnt;
/**
* @brief Pointer to the transmission linear buffer.
*/
const uint8_t *txbuf;
#if (USB_USE_WAIT == TRUE) || defined(__DOXYGEN__)
/**
* @brief Waiting thread.
*/
thread_reference_t thread;
#endif
/* End of the mandatory fields.*/
/* */
bool odd_even; /* ODD / EVEN */
/* */
bool data_bank; /* DATA0 / DATA1 */
} USBInEndpointState;
/**
* @brief Type of an OUT endpoint state structure.
*/
typedef struct {
/**
* @brief Requested receive transfer size.
*/
size_t rxsize;
/**
* @brief Received bytes so far.
*/
size_t rxcnt;
/**
* @brief Pointer to the receive linear buffer.
*/
uint8_t *rxbuf;
#if (USB_USE_WAIT == TRUE) || defined(__DOXYGEN__)
/**
* @brief Waiting thread.
*/
thread_reference_t thread;
#endif
/* End of the mandatory fields.*/
/**
* @brief Number of packets to receive.
*/
uint16_t rxpkts;
/* */
bool odd_even; /* ODD / EVEN */
/* */
bool data_bank; /* DATA0 / DATA1 */
} USBOutEndpointState;
/**
* @brief Type of an USB endpoint configuration structure.
* @note Platform specific restrictions may apply to endpoints.
*/
typedef struct {
/**
* @brief Type and mode of the endpoint.
*/
uint32_t ep_mode;
/**
* @brief Setup packet notification callback.
* @details This callback is invoked when a setup packet has been
* received.
* @post The application must immediately call @p usbReadPacket() in
* order to access the received packet.
* @note This field is only valid for @p USB_EP_MODE_TYPE_CTRL
* endpoints, it should be set to @p NULL for other endpoint
* types.
*/
usbepcallback_t setup_cb;
/**
* @brief IN endpoint notification callback.
* @details This field must be set to @p NULL if callback is not required.
*/
usbepcallback_t in_cb;
/**
* @brief OUT endpoint notification callback.
* @details This field must be set to @p NULL if callback is not required.
*/
usbepcallback_t out_cb;
/**
* @brief IN endpoint maximum packet size.
* @details This field must be set to zero if the IN endpoint is not used.
*/
uint16_t in_maxsize;
/**
* @brief OUT endpoint maximum packet size.
* @details This field must be set to zero if the OUT endpoint is not used.
*/
uint16_t out_maxsize;
/**
* @brief @p USBEndpointState associated to the IN endpoint.
* @details This field must be set to @p NULL if the IN endpoint is not
* used.
*/
USBInEndpointState *in_state;
/**
* @brief @p USBEndpointState associated to the OUT endpoint.
* @details This field must be set to @p NULL if the OUT endpoint is not
* used.
*/
USBOutEndpointState *out_state;
/* End of the mandatory fields.*/
/**
* @brief Reserved field, not currently used.
* @note Initialize this field to 1 in order to be forward compatible.
*/
uint16_t ep_buffers;
/**
* @brief Pointer to a buffer for setup packets.
* @details Setup packets require a dedicated 8-bytes buffer, set this
* field to @p NULL for non-control endpoints.
*/
uint8_t *setup_buf;
} USBEndpointConfig;
/**
* @brief Type of an USB driver configuration structure.
*/
typedef struct {
/**
* @brief USB events callback.
* @details This callback is invoked when an USB driver event is registered.
*/
usbeventcb_t event_cb;
/**
* @brief Device GET_DESCRIPTOR request callback.
* @note This callback is mandatory and cannot be set to @p NULL.
*/
usbgetdescriptor_t get_descriptor_cb;
/**
* @brief Requests hook callback.
* @details This hook allows to be notified of standard requests or to
* handle non standard requests.
*/
usbreqhandler_t requests_hook_cb;
/**
* @brief Start Of Frame callback.
*/
usbcallback_t sof_cb;
/* End of the mandatory fields.*/
} USBConfig;
/**
* @brief Structure representing an USB driver.
*/
struct USBDriver {
/**
* @brief Driver state.
*/
usbstate_t state;
/**
* @brief Current configuration data.
*/
const USBConfig *config;
/**
* @brief Bit map of the transmitting IN endpoints.
*/
uint16_t transmitting;
/**
* @brief Bit map of the receiving OUT endpoints.
*/
uint16_t receiving;
/**
* @brief Active endpoints configurations.
*/
const USBEndpointConfig *epc[USB_MAX_ENDPOINTS + 1];
/**
* @brief Fields available to user, it can be used to associate an
* application-defined handler to an IN endpoint.
* @note The base index is one, the endpoint zero does not have a
* reserved element in this array.
*/
void *in_params[USB_MAX_ENDPOINTS];
/**
* @brief Fields available to user, it can be used to associate an
* application-defined handler to an OUT endpoint.
* @note The base index is one, the endpoint zero does not have a
* reserved element in this array.
*/
void *out_params[USB_MAX_ENDPOINTS];
/**
* @brief Endpoint 0 state.
*/
usbep0state_t ep0state;
/**
* @brief Next position in the buffer to be transferred through endpoint 0.
*/
uint8_t *ep0next;
/**
* @brief Number of bytes yet to be transferred through endpoint 0.
*/
size_t ep0n;
/**
* @brief Endpoint 0 end transaction callback.
*/
usbcallback_t ep0endcb;
/**
* @brief Setup packet buffer.
*/
uint8_t setup[8];
/**
* @brief Current USB device status.
*/
uint16_t status;
/**
* @brief Assigned USB address.
*/
uint8_t address;
/**
* @brief Current USB device configuration.
*/
uint8_t configuration;
/**
* @brief State of the driver when a suspend happened.
*/
usbstate_t saved_state;
#if defined(USB_DRIVER_EXT_FIELDS)
USB_DRIVER_EXT_FIELDS
#endif
/* End of the mandatory fields.*/
/**
* @brief Pointer to the next address in the packet memory.
*/
uint32_t pmnext;
};
/*===========================================================================*/
/* Driver macros. */
/*===========================================================================*/
/**
* @brief Returns the current frame number.
*
* @param[in] usbp pointer to the @p USBDriver object
* @return The current frame number.
*
* @notapi
*/
#define usb_lld_get_frame_number(usbp) ((USB0->FRMNUMH<<8)|USB0->FRMNUML)
/**
* @brief Returns the exact size of a receive transaction.
* @details The received size can be different from the size specified in
* @p usbStartReceiveI() because the last packet could have a size
* different from the expected one.
* @pre The OUT endpoint must have been configured in transaction mode
* in order to use this function.
*
* @param[in] usbp pointer to the @p USBDriver object
* @param[in] ep endpoint number
* @return Received data size.
*
* @notapi
*/
#define usb_lld_get_transaction_size(usbp, ep) \
((usbp)->epc[ep]->out_state->rxcnt)
/**
* @brief Connects the USB device.
*
* @api
*/
#if !defined(usb_lld_connect_bus)
#define usb_lld_connect_bus(usbp) (USB0->CONTROL |= USBx_CONTROL_DPPULLUPNONOTG)
#endif
/**
* @brief Disconnect the USB device.
*
* @api
*/
#if !defined(usb_lld_disconnect_bus)
/* Writing to USB0->CONTROL causes an unhandled exception when USB module is not clocked. */
#if KINETIS_USB0_IS_USBOTG
#define usb_lld_disconnect_bus(usbp) if(SIM->SCGC4 & SIM_SCGC4_USBOTG) {USB0->CONTROL &= ~USBx_CONTROL_DPPULLUPNONOTG;} else {}
#else /* KINETIS_USB0_IS_USBOTG */
#define usb_lld_disconnect_bus(usbp) if(SIM->SCGC4 & SIM_SCGC4_USBFS) {USB0->CONTROL &= ~USBx_CONTROL_DPPULLUPNONOTG;} else {}
#endif /* KINETIS_USB0_IS_USBOTG */
#endif
/**
* @brief Start of host wake-up procedure.
*
* @notapi
*/
#define usb_lld_wakeup_host(usbp) \
do{ \
USB0->CTL |= USBx_CTL_RESUME; \
osalThreadSleepMilliseconds(USB_HOST_WAKEUP_DURATION); \
USB0->CTL &= ~USBx_CTL_RESUME; \
} while (false)
/*===========================================================================*/
/* External declarations. */
/*===========================================================================*/
#if KINETIS_USB_USE_USB0 && !defined(__DOXYGEN__)
extern USBDriver USBD1;
#endif
#ifdef __cplusplus
extern "C" {
#endif
void usb_lld_init(void);
void usb_lld_start(USBDriver *usbp);
void usb_lld_stop(USBDriver *usbp);
void usb_lld_reset(USBDriver *usbp);
void usb_lld_set_address(USBDriver *usbp);
void usb_lld_init_endpoint(USBDriver *usbp, usbep_t ep);
void usb_lld_disable_endpoints(USBDriver *usbp);
usbepstatus_t usb_lld_get_status_in(USBDriver *usbp, usbep_t ep);
usbepstatus_t usb_lld_get_status_out(USBDriver *usbp, usbep_t ep);
void usb_lld_read_setup(USBDriver *usbp, usbep_t ep, uint8_t *buf);
void usb_lld_start_out(USBDriver *usbp, usbep_t ep);
void usb_lld_start_in(USBDriver *usbp, usbep_t ep);
void usb_lld_stall_out(USBDriver *usbp, usbep_t ep);
void usb_lld_stall_in(USBDriver *usbp, usbep_t ep);
void usb_lld_clear_out(USBDriver *usbp, usbep_t ep);
void usb_lld_clear_in(USBDriver *usbp, usbep_t ep);
#ifdef __cplusplus
}
#endif
#endif /* HAL_USE_USB */
#endif /* HAL_USB_LLD_H_ */
/** @} */