mirror of
https://github.com/portapack-mayhem/mayhem-firmware.git
synced 2026-09-11 17:19:29 +00:00
ChibiOS 2.6.8, until I can figure out where to get it from git.
This commit is contained in:
+184
@@ -0,0 +1,184 @@
|
||||
/*
|
||||
ChibiOS/RT - Copyright (C) 2006-2013 Giovanni Di Sirio
|
||||
|
||||
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 STM32/GPIOv1/pal_lld.c
|
||||
* @brief STM32F1xx GPIO low level driver code.
|
||||
*
|
||||
* @addtogroup PAL
|
||||
* @{
|
||||
*/
|
||||
|
||||
#include "ch.h"
|
||||
#include "hal.h"
|
||||
|
||||
#if HAL_USE_PAL || defined(__DOXYGEN__)
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local definitions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
#if STM32_HAS_GPIOG
|
||||
#define APB2_EN_MASK (RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN | \
|
||||
RCC_APB2ENR_IOPCEN | RCC_APB2ENR_IOPDEN | \
|
||||
RCC_APB2ENR_IOPEEN | RCC_APB2ENR_IOPFEN | \
|
||||
RCC_APB2ENR_IOPGEN | RCC_APB2ENR_AFIOEN)
|
||||
#elif STM32_HAS_GPIOE
|
||||
#define APB2_EN_MASK (RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN | \
|
||||
RCC_APB2ENR_IOPCEN | RCC_APB2ENR_IOPDEN | \
|
||||
RCC_APB2ENR_IOPEEN | RCC_APB2ENR_AFIOEN)
|
||||
#else
|
||||
#define APB2_EN_MASK (RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN | \
|
||||
RCC_APB2ENR_IOPCEN | RCC_APB2ENR_IOPDEN | \
|
||||
RCC_APB2ENR_AFIOEN)
|
||||
#endif
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver exported variables. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local variables and types. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver interrupt handlers. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver exported functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief STM32 I/O ports configuration.
|
||||
* @details Ports A-D(E, F, G) clocks enabled, AFIO clock enabled.
|
||||
*
|
||||
* @param[in] config the STM32 ports configuration
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void _pal_lld_init(const PALConfig *config) {
|
||||
|
||||
/*
|
||||
* Enables the GPIO related clocks.
|
||||
*/
|
||||
rccEnableAPB2(APB2_EN_MASK, FALSE);
|
||||
|
||||
/*
|
||||
* Initial GPIO setup.
|
||||
*/
|
||||
GPIOA->ODR = config->PAData.odr;
|
||||
GPIOA->CRH = config->PAData.crh;
|
||||
GPIOA->CRL = config->PAData.crl;
|
||||
GPIOB->ODR = config->PBData.odr;
|
||||
GPIOB->CRH = config->PBData.crh;
|
||||
GPIOB->CRL = config->PBData.crl;
|
||||
GPIOC->ODR = config->PCData.odr;
|
||||
GPIOC->CRH = config->PCData.crh;
|
||||
GPIOC->CRL = config->PCData.crl;
|
||||
GPIOD->ODR = config->PDData.odr;
|
||||
GPIOD->CRH = config->PDData.crh;
|
||||
GPIOD->CRL = config->PDData.crl;
|
||||
#if STM32_HAS_GPIOE || defined(__DOXYGEN__)
|
||||
GPIOE->ODR = config->PEData.odr;
|
||||
GPIOE->CRH = config->PEData.crh;
|
||||
GPIOE->CRL = config->PEData.crl;
|
||||
#if STM32_HAS_GPIOF || defined(__DOXYGEN__)
|
||||
GPIOF->ODR = config->PFData.odr;
|
||||
GPIOF->CRH = config->PFData.crh;
|
||||
GPIOF->CRL = config->PFData.crl;
|
||||
#if STM32_HAS_GPIOG || defined(__DOXYGEN__)
|
||||
GPIOG->ODR = config->PGData.odr;
|
||||
GPIOG->CRH = config->PGData.crh;
|
||||
GPIOG->CRL = config->PGData.crl;
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Pads mode setup.
|
||||
* @details This function programs a pads group belonging to the same port
|
||||
* with the specified mode.
|
||||
* @note @p PAL_MODE_UNCONNECTED is implemented as push pull output at 2MHz.
|
||||
* @note Writing on pads programmed as pull-up or pull-down has the side
|
||||
* effect to modify the resistor setting because the output latched
|
||||
* data is used for the resistor selection.
|
||||
*
|
||||
* @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) {
|
||||
static const uint8_t cfgtab[] = {
|
||||
4, /* PAL_MODE_RESET, implemented as input.*/
|
||||
2, /* PAL_MODE_UNCONNECTED, implemented as push pull output 2MHz.*/
|
||||
4, /* PAL_MODE_INPUT */
|
||||
8, /* PAL_MODE_INPUT_PULLUP */
|
||||
8, /* PAL_MODE_INPUT_PULLDOWN */
|
||||
0, /* PAL_MODE_INPUT_ANALOG */
|
||||
3, /* PAL_MODE_OUTPUT_PUSHPULL, 50MHz.*/
|
||||
7, /* PAL_MODE_OUTPUT_OPENDRAIN, 50MHz.*/
|
||||
8, /* Reserved.*/
|
||||
8, /* Reserved.*/
|
||||
8, /* Reserved.*/
|
||||
8, /* Reserved.*/
|
||||
8, /* Reserved.*/
|
||||
8, /* Reserved.*/
|
||||
8, /* Reserved.*/
|
||||
8, /* Reserved.*/
|
||||
0xB, /* PAL_MODE_STM32_ALTERNATE_PUSHPULL, 50MHz.*/
|
||||
0xF, /* PAL_MODE_STM32_ALTERNATE_OPENDRAIN, 50MHz.*/
|
||||
};
|
||||
uint32_t mh, ml, crh, crl, cfg;
|
||||
unsigned i;
|
||||
|
||||
if (mode == PAL_MODE_INPUT_PULLUP)
|
||||
port->BSRR = mask;
|
||||
else if (mode == PAL_MODE_INPUT_PULLDOWN)
|
||||
port->BRR = mask;
|
||||
cfg = cfgtab[mode];
|
||||
mh = ml = crh = crl = 0;
|
||||
for (i = 0; i < 8; i++) {
|
||||
ml <<= 4;
|
||||
mh <<= 4;
|
||||
crl <<= 4;
|
||||
crh <<= 4;
|
||||
if ((mask & 0x0080) == 0)
|
||||
ml |= 0xf;
|
||||
else
|
||||
crl |= cfg;
|
||||
if ((mask & 0x8000) == 0)
|
||||
mh |= 0xf;
|
||||
else
|
||||
crh |= cfg;
|
||||
mask <<= 1;
|
||||
}
|
||||
port->CRH = (port->CRH & mh) | crh;
|
||||
port->CRL = (port->CRL & ml) | crl;
|
||||
}
|
||||
|
||||
#endif /* HAL_USE_PAL */
|
||||
|
||||
/** @} */
|
||||
+334
@@ -0,0 +1,334 @@
|
||||
/*
|
||||
ChibiOS/RT - Copyright (C) 2006-2013 Giovanni Di Sirio
|
||||
|
||||
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 STM32/GPIOv1/pal_lld.h
|
||||
* @brief STM32F1xx GPIO low level driver header.
|
||||
*
|
||||
* @addtogroup PAL
|
||||
* @{
|
||||
*/
|
||||
|
||||
#ifndef _PAL_LLD_H_
|
||||
#define _PAL_LLD_H_
|
||||
|
||||
#if HAL_USE_PAL || defined(__DOXYGEN__)
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Unsupported modes and specific modes */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @name STM32-specific I/O mode flags
|
||||
* @{
|
||||
*/
|
||||
/**
|
||||
* @brief STM32 specific alternate push-pull output mode.
|
||||
*/
|
||||
#define PAL_MODE_STM32_ALTERNATE_PUSHPULL 16
|
||||
|
||||
/**
|
||||
* @brief STM32 specific alternate open-drain output mode.
|
||||
*/
|
||||
#define PAL_MODE_STM32_ALTERNATE_OPENDRAIN 17
|
||||
/** @} */
|
||||
|
||||
/*===========================================================================*/
|
||||
/* I/O Ports Types and constants. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief GPIO port setup info.
|
||||
*/
|
||||
typedef struct {
|
||||
/** Initial value for ODR register.*/
|
||||
uint32_t odr;
|
||||
/** Initial value for CRL register.*/
|
||||
uint32_t crl;
|
||||
/** Initial value for CRH register.*/
|
||||
uint32_t crh;
|
||||
} stm32_gpio_setup_t;
|
||||
|
||||
/**
|
||||
* @brief STM32 GPIO static initializer.
|
||||
* @details An instance of this structure must be passed to @p palInit() at
|
||||
* system startup time in order to initialize the digital I/O
|
||||
* subsystem. This represents only the initial setup, specific pads
|
||||
* or whole ports can be reprogrammed at later time.
|
||||
*/
|
||||
typedef struct {
|
||||
/** @brief Port A setup data.*/
|
||||
stm32_gpio_setup_t PAData;
|
||||
/** @brief Port B setup data.*/
|
||||
stm32_gpio_setup_t PBData;
|
||||
/** @brief Port C setup data.*/
|
||||
stm32_gpio_setup_t PCData;
|
||||
/** @brief Port D setup data.*/
|
||||
stm32_gpio_setup_t PDData;
|
||||
#if STM32_HAS_GPIOE || defined(__DOXYGEN__)
|
||||
/** @brief Port E setup data.*/
|
||||
stm32_gpio_setup_t PEData;
|
||||
#if STM32_HAS_GPIOF || defined(__DOXYGEN__)
|
||||
/** @brief Port F setup data.*/
|
||||
stm32_gpio_setup_t PFData;
|
||||
#if STM32_HAS_GPIOG || defined(__DOXYGEN__)
|
||||
/** @brief Port G setup data.*/
|
||||
stm32_gpio_setup_t PGData;
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
} PALConfig;
|
||||
|
||||
/**
|
||||
* @brief Width, in bits, of an I/O port.
|
||||
*/
|
||||
#define PAL_IOPORTS_WIDTH 16
|
||||
|
||||
/**
|
||||
* @brief Whole port mask.
|
||||
* @details This macro specifies all the valid bits into a port.
|
||||
*/
|
||||
#define PAL_WHOLE_PORT ((ioportmask_t)0xFFFF)
|
||||
|
||||
/**
|
||||
* @brief Digital I/O port sized unsigned type.
|
||||
*/
|
||||
typedef uint32_t ioportmask_t;
|
||||
|
||||
/**
|
||||
* @brief Digital I/O modes.
|
||||
*/
|
||||
typedef uint32_t iomode_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;
|
||||
|
||||
/*===========================================================================*/
|
||||
/* I/O Ports Identifiers. */
|
||||
/* The low level driver wraps the definitions already present in the STM32 */
|
||||
/* firmware library. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief GPIO port A identifier.
|
||||
*/
|
||||
#if STM32_HAS_GPIOA || defined(__DOXYGEN__)
|
||||
#define IOPORT1 GPIOA
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief GPIO port B identifier.
|
||||
*/
|
||||
#if STM32_HAS_GPIOB || defined(__DOXYGEN__)
|
||||
#define IOPORT2 GPIOB
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief GPIO port C identifier.
|
||||
*/
|
||||
#if STM32_HAS_GPIOC || defined(__DOXYGEN__)
|
||||
#define IOPORT3 GPIOC
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief GPIO port D identifier.
|
||||
*/
|
||||
#if STM32_HAS_GPIOD || defined(__DOXYGEN__)
|
||||
#define IOPORT4 GPIOD
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief GPIO port E identifier.
|
||||
*/
|
||||
#if STM32_HAS_GPIOE || defined(__DOXYGEN__)
|
||||
#define IOPORT5 GPIOE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief GPIO port F identifier.
|
||||
*/
|
||||
#if STM32_HAS_GPIOF || defined(__DOXYGEN__)
|
||||
#define IOPORT6 GPIOF
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief GPIO port G identifier.
|
||||
*/
|
||||
#if STM32_HAS_GPIOG || defined(__DOXYGEN__)
|
||||
#define IOPORT7 GPIOG
|
||||
#endif
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Implementation, some of the following macros could be implemented as */
|
||||
/* functions, if so please put them in pal_lld.c. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief GPIO ports subsystem initialization.
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
#define pal_lld_init(config) _pal_lld_init(config)
|
||||
|
||||
/**
|
||||
* @brief Reads an I/O port.
|
||||
* @details This function is implemented by reading the GPIO IDR register, the
|
||||
* implementation has no side effects.
|
||||
* @note This function is not meant to be invoked directly by the application
|
||||
* code.
|
||||
*
|
||||
* @param[in] port port identifier
|
||||
* @return The port bits.
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
#define pal_lld_readport(port) ((port)->IDR)
|
||||
|
||||
/**
|
||||
* @brief Reads the output latch.
|
||||
* @details This function is implemented by reading the GPIO ODR register, the
|
||||
* implementation has no side effects.
|
||||
* @note This function is not meant to be invoked directly by the application
|
||||
* code.
|
||||
*
|
||||
* @param[in] port port identifier
|
||||
* @return The latched logical states.
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
#define pal_lld_readlatch(port) ((port)->ODR)
|
||||
|
||||
/**
|
||||
* @brief Writes on a I/O port.
|
||||
* @details This function is implemented by writing the GPIO ODR register, the
|
||||
* implementation has no side effects.
|
||||
* @note Writing on pads programmed as pull-up or pull-down has the side
|
||||
* effect to modify the resistor setting because the output latched
|
||||
* data is used for the resistor selection.
|
||||
*
|
||||
* @param[in] port port identifier
|
||||
* @param[in] bits bits to be written on the specified port
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
#define pal_lld_writeport(port, bits) ((port)->ODR = (bits))
|
||||
|
||||
/**
|
||||
* @brief Sets a bits mask on a I/O port.
|
||||
* @details This function is implemented by writing the GPIO BSRR register, the
|
||||
* implementation has no side effects.
|
||||
* @note Writing on pads programmed as pull-up or pull-down has the side
|
||||
* effect to modify the resistor setting because the output latched
|
||||
* data is used for the resistor selection.
|
||||
*
|
||||
* @param[in] port port identifier
|
||||
* @param[in] bits bits to be ORed on the specified port
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
#define pal_lld_setport(port, bits) ((port)->BSRR = (bits))
|
||||
|
||||
/**
|
||||
* @brief Clears a bits mask on a I/O port.
|
||||
* @details This function is implemented by writing the GPIO BRR register, the
|
||||
* implementation has no side effects.
|
||||
* @note Writing on pads programmed as pull-up or pull-down has the side
|
||||
* effect to modify the resistor setting because the output latched
|
||||
* data is used for the resistor selection.
|
||||
*
|
||||
* @param[in] port port identifier
|
||||
* @param[in] bits bits to be cleared on the specified port
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
#define pal_lld_clearport(port, bits) ((port)->BRR = (bits))
|
||||
|
||||
/**
|
||||
* @brief Writes a group of bits.
|
||||
* @details This function is implemented by writing the GPIO BSRR register, the
|
||||
* implementation has no side effects.
|
||||
* @note Writing on pads programmed as pull-up or pull-down has the side
|
||||
* effect to modify the resistor setting because the output latched
|
||||
* data is used for the resistor selection.
|
||||
*
|
||||
* @param[in] port port identifier
|
||||
* @param[in] mask group mask
|
||||
* @param[in] offset the 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) \
|
||||
((port)->BSRR = ((~(bits) & (mask)) << (16 + (offset))) | \
|
||||
(((bits) & (mask)) << (offset)))
|
||||
|
||||
/**
|
||||
* @brief Pads group mode setup.
|
||||
* @details This function programs a pads group belonging to the same port
|
||||
* with the specified mode.
|
||||
* @note Writing on pads programmed as pull-up or pull-down has the side
|
||||
* effect to modify the resistor setting because the output latched
|
||||
* data is used for the resistor selection.
|
||||
*
|
||||
* @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 Writes a logical state on an output pad.
|
||||
* @note Writing on pads programmed as pull-up or pull-down has the side
|
||||
* effect to modify the resistor setting because the output latched
|
||||
* data is used for the resistor selection.
|
||||
*
|
||||
* @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_writegroup(port, 1, pad, bit)
|
||||
|
||||
extern const PALConfig pal_default_config;
|
||||
|
||||
#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);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* HAL_USE_PAL */
|
||||
|
||||
#endif /* _PAL_LLD_H_ */
|
||||
|
||||
/** @} */
|
||||
+261
@@ -0,0 +1,261 @@
|
||||
/*
|
||||
ChibiOS/RT - Copyright (C) 2006-2013 Giovanni Di Sirio
|
||||
|
||||
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 STM32/GPIOv2/pal_lld.c
|
||||
* @brief STM32L1xx/STM32F2xx/STM32F4xx GPIO low level driver code.
|
||||
*
|
||||
* @addtogroup PAL
|
||||
* @{
|
||||
*/
|
||||
|
||||
#include "ch.h"
|
||||
#include "hal.h"
|
||||
|
||||
#if HAL_USE_PAL || defined(__DOXYGEN__)
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local definitions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
#if defined(STM32L1XX)
|
||||
#define AHB_EN_MASK (RCC_AHBENR_GPIOAEN | RCC_AHBENR_GPIOBEN | \
|
||||
RCC_AHBENR_GPIOCEN | RCC_AHBENR_GPIODEN | \
|
||||
RCC_AHBENR_GPIOEEN | RCC_AHBENR_GPIOHEN)
|
||||
#define AHB_LPEN_MASK AHB_EN_MASK
|
||||
|
||||
#elif defined(STM32F030) || defined(STM32F0XX_MD)
|
||||
#define AHB_EN_MASK (RCC_AHBENR_GPIOAEN | RCC_AHBENR_GPIOBEN | \
|
||||
RCC_AHBENR_GPIOCEN | RCC_AHBENR_GPIODEN | \
|
||||
RCC_AHBENR_GPIOFEN)
|
||||
|
||||
#elif defined(STM32F0XX_LD)
|
||||
#define AHB_EN_MASK (RCC_AHBENR_GPIOAEN | RCC_AHBENR_GPIOBEN | \
|
||||
RCC_AHBENR_GPIOCEN | RCC_AHBENR_GPIOFEN)
|
||||
|
||||
#elif defined(STM32F2XX)
|
||||
#define AHB1_EN_MASK (RCC_AHB1ENR_GPIOAEN | RCC_AHB1ENR_GPIOBEN | \
|
||||
RCC_AHB1ENR_GPIOCEN | RCC_AHB1ENR_GPIODEN | \
|
||||
RCC_AHB1ENR_GPIOEEN | RCC_AHB1ENR_GPIOFEN | \
|
||||
RCC_AHB1ENR_GPIOGEN | RCC_AHB1ENR_GPIOHEN | \
|
||||
RCC_AHB1ENR_GPIOIEN)
|
||||
#define AHB1_LPEN_MASK AHB1_EN_MASK
|
||||
|
||||
#elif defined(STM32F30X) || defined(STM32F37X)
|
||||
#define AHB_EN_MASK (RCC_AHBENR_GPIOAEN | RCC_AHBENR_GPIOBEN | \
|
||||
RCC_AHBENR_GPIOCEN | RCC_AHBENR_GPIODEN | \
|
||||
RCC_AHBENR_GPIOEEN | RCC_AHBENR_GPIOFEN)
|
||||
|
||||
#elif defined(STM32F4XX)
|
||||
#if STM32_HAS_GPIOF && STM32_HAS_GPIOG && STM32_HAS_GPIOI
|
||||
#define AHB1_EN_MASK (RCC_AHB1ENR_GPIOAEN | RCC_AHB1ENR_GPIOBEN | \
|
||||
RCC_AHB1ENR_GPIOCEN | RCC_AHB1ENR_GPIODEN | \
|
||||
RCC_AHB1ENR_GPIOEEN | RCC_AHB1ENR_GPIOFEN | \
|
||||
RCC_AHB1ENR_GPIOGEN | RCC_AHB1ENR_GPIOHEN | \
|
||||
RCC_AHB1ENR_GPIOIEN)
|
||||
#else
|
||||
#define AHB1_EN_MASK (RCC_AHB1ENR_GPIOAEN | RCC_AHB1ENR_GPIOBEN | \
|
||||
RCC_AHB1ENR_GPIOCEN | RCC_AHB1ENR_GPIODEN | \
|
||||
RCC_AHB1ENR_GPIOEEN)
|
||||
#endif /* STM32_HAS_GPIOF && STM32_HAS_GPIOG && STM32_HAS_GPIOI */
|
||||
#define AHB1_LPEN_MASK AHB1_EN_MASK
|
||||
|
||||
#else
|
||||
#error "missing or unsupported platform for GPIOv2 PAL driver"
|
||||
#endif
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver exported variables. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local variables and types. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
static void initgpio(GPIO_TypeDef *gpiop, const stm32_gpio_setup_t *config) {
|
||||
|
||||
gpiop->OTYPER = config->otyper;
|
||||
gpiop->OSPEEDR = config->ospeedr;
|
||||
gpiop->PUPDR = config->pupdr;
|
||||
gpiop->ODR = config->odr;
|
||||
gpiop->AFRL = config->afrl;
|
||||
gpiop->AFRH = config->afrh;
|
||||
gpiop->MODER = config->moder;
|
||||
}
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver interrupt handlers. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver exported functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief STM32 I/O ports configuration.
|
||||
* @details Ports A-D(E, F, G, H) clocks enabled.
|
||||
*
|
||||
* @param[in] config the STM32 ports configuration
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void _pal_lld_init(const PALConfig *config) {
|
||||
|
||||
/*
|
||||
* Enables the GPIO related clocks.
|
||||
*/
|
||||
#if defined(STM32L1XX)
|
||||
rccEnableAHB(AHB_EN_MASK, TRUE);
|
||||
RCC->AHBLPENR |= AHB_LPEN_MASK;
|
||||
#elif defined(STM32F0XX)
|
||||
rccEnableAHB(AHB_EN_MASK, TRUE);
|
||||
#elif defined(STM32F30X) || defined(STM32F37X)
|
||||
rccEnableAHB(AHB_EN_MASK, TRUE);
|
||||
#elif defined(STM32F2XX) || defined(STM32F4XX)
|
||||
RCC->AHB1ENR |= AHB1_EN_MASK;
|
||||
RCC->AHB1LPENR |= AHB1_LPEN_MASK;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Initial GPIO setup.
|
||||
*/
|
||||
#if STM32_HAS_GPIOA
|
||||
initgpio(GPIOA, &config->PAData);
|
||||
#endif
|
||||
#if STM32_HAS_GPIOB
|
||||
initgpio(GPIOB, &config->PBData);
|
||||
#endif
|
||||
#if STM32_HAS_GPIOC
|
||||
initgpio(GPIOC, &config->PCData);
|
||||
#endif
|
||||
#if STM32_HAS_GPIOD
|
||||
initgpio(GPIOD, &config->PDData);
|
||||
#endif
|
||||
#if STM32_HAS_GPIOE
|
||||
initgpio(GPIOE, &config->PEData);
|
||||
#endif
|
||||
#if STM32_HAS_GPIOF
|
||||
initgpio(GPIOF, &config->PFData);
|
||||
#endif
|
||||
#if STM32_HAS_GPIOG
|
||||
initgpio(GPIOG, &config->PGData);
|
||||
#endif
|
||||
#if STM32_HAS_GPIOH
|
||||
initgpio(GPIOH, &config->PHData);
|
||||
#endif
|
||||
#if STM32_HAS_GPIOI
|
||||
initgpio(GPIOI, &config->PIData);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Pads mode setup.
|
||||
* @details This function programs a pads group belonging to the same port
|
||||
* with the specified mode.
|
||||
* @note @p PAL_MODE_UNCONNECTED is implemented as push pull at minimum
|
||||
* speed.
|
||||
*
|
||||
* @param[in] port the port identifier
|
||||
* @param[in] mask the group mask
|
||||
* @param[in] mode the mode
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
#if 1
|
||||
void _pal_lld_setgroupmode(ioportid_t port,
|
||||
ioportmask_t mask,
|
||||
iomode_t mode) {
|
||||
|
||||
uint32_t moder = (mode & PAL_STM32_MODE_MASK) >> 0;
|
||||
uint32_t otyper = (mode & PAL_STM32_OTYPE_MASK) >> 2;
|
||||
uint32_t ospeedr = (mode & PAL_STM32_OSPEED_MASK) >> 3;
|
||||
uint32_t pupdr = (mode & PAL_STM32_PUDR_MASK) >> 5;
|
||||
uint32_t altr = (mode & PAL_STM32_ALTERNATE_MASK) >> 7;
|
||||
uint32_t bit = 0;
|
||||
while (TRUE) {
|
||||
if ((mask & 1) != 0) {
|
||||
uint32_t altrmask, m1, m2, m4;
|
||||
|
||||
altrmask = altr << ((bit & 7) * 4);
|
||||
m4 = 15 << ((bit & 7) * 4);
|
||||
if (bit < 8)
|
||||
port->AFRL = (port->AFRL & ~m4) | altrmask;
|
||||
else
|
||||
port->AFRH = (port->AFRH & ~m4) | altrmask;
|
||||
m1 = 1 << bit;
|
||||
port->OTYPER = (port->OTYPER & ~m1) | otyper;
|
||||
m2 = 3 << (bit * 2);
|
||||
port->OSPEEDR = (port->OSPEEDR & ~m2) | ospeedr;
|
||||
port->PUPDR = (port->PUPDR & ~m2) | pupdr;
|
||||
port->MODER = (port->MODER & ~m2) | moder;
|
||||
}
|
||||
mask >>= 1;
|
||||
if (!mask)
|
||||
return;
|
||||
otyper <<= 1;
|
||||
ospeedr <<= 2;
|
||||
pupdr <<= 2;
|
||||
moder <<= 2;
|
||||
bit++;
|
||||
}
|
||||
}
|
||||
#else
|
||||
void _pal_lld_setgroupmode(ioportid_t port,
|
||||
ioportmask_t mask,
|
||||
iomode_t mode) {
|
||||
uint32_t afrm, moderm, pupdrm, otyperm, ospeedrm;
|
||||
uint32_t m1 = (uint32_t)mask;
|
||||
uint32_t m2 = 0;
|
||||
uint32_t m4l = 0;
|
||||
uint32_t m4h = 0;
|
||||
uint32_t bit = 0;
|
||||
do {
|
||||
if ((mask & 1) != 0) {
|
||||
m2 |= 3 << bit;
|
||||
if (bit < 16)
|
||||
m4l |= 15 << ((bit & 14) * 2);
|
||||
else
|
||||
m4h |= 15 << ((bit & 14) * 2);
|
||||
}
|
||||
bit += 2;
|
||||
mask >>= 1;
|
||||
} while (mask);
|
||||
|
||||
afrm = ((mode & PAL_STM32_ALTERNATE_MASK) >> 7) * 0x1111;
|
||||
port->AFRL = (port->AFRL & ~m4l) | (afrm & m4l);
|
||||
port->AFRH = (port->AFRH & ~m4h) | (afrm & m4h);
|
||||
|
||||
ospeedrm = ((mode & PAL_STM32_OSPEED_MASK) >> 3) * 0x5555;
|
||||
port->OSPEEDR = (port->OSPEEDR & ~m2) | (ospeedrm & m2);
|
||||
|
||||
otyperm = ((mode & PAL_STM32_OTYPE_MASK) >> 2) * 0xffff;
|
||||
port->OTYPER = (port->OTYPER & ~m1) | (otyperm & m1);
|
||||
|
||||
pupdrm = ((mode & PAL_STM32_PUDR_MASK) >> 5) * 0x5555;
|
||||
port->PUPDR = (port->PUPDR & ~m2) | (pupdrm & m2);
|
||||
|
||||
moderm = ((mode & PAL_STM32_MODE_MASK) >> 0) * 0x5555;
|
||||
port->MODER = (port->MODER & ~m2) | (moderm & m2);
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* HAL_USE_PAL */
|
||||
|
||||
/** @} */
|
||||
+461
@@ -0,0 +1,461 @@
|
||||
/*
|
||||
ChibiOS/RT - Copyright (C) 2006-2013 Giovanni Di Sirio
|
||||
|
||||
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 STM32/GPIOv2/pal_lld.h
|
||||
* @brief STM32L1xx/STM32F2xx/STM32F4xx GPIO low level driver header.
|
||||
*
|
||||
* @addtogroup PAL
|
||||
* @{
|
||||
*/
|
||||
|
||||
#ifndef _PAL_LLD_H_
|
||||
#define _PAL_LLD_H_
|
||||
|
||||
#if HAL_USE_PAL || defined(__DOXYGEN__)
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Unsupported modes and specific modes */
|
||||
/*===========================================================================*/
|
||||
|
||||
#undef PAL_MODE_RESET
|
||||
#undef PAL_MODE_UNCONNECTED
|
||||
#undef PAL_MODE_INPUT
|
||||
#undef PAL_MODE_INPUT_PULLUP
|
||||
#undef PAL_MODE_INPUT_PULLDOWN
|
||||
#undef PAL_MODE_INPUT_ANALOG
|
||||
#undef PAL_MODE_OUTPUT_PUSHPULL
|
||||
#undef PAL_MODE_OUTPUT_OPENDRAIN
|
||||
|
||||
/**
|
||||
* @name STM32-specific I/O mode flags
|
||||
* @{
|
||||
*/
|
||||
#define PAL_STM32_MODE_MASK (3 << 0)
|
||||
#define PAL_STM32_MODE_INPUT (0 << 0)
|
||||
#define PAL_STM32_MODE_OUTPUT (1 << 0)
|
||||
#define PAL_STM32_MODE_ALTERNATE (2 << 0)
|
||||
#define PAL_STM32_MODE_ANALOG (3 << 0)
|
||||
|
||||
#define PAL_STM32_OTYPE_MASK (1 << 2)
|
||||
#define PAL_STM32_OTYPE_PUSHPULL (0 << 2)
|
||||
#define PAL_STM32_OTYPE_OPENDRAIN (1 << 2)
|
||||
|
||||
#define PAL_STM32_OSPEED_MASK (3 << 3)
|
||||
#define PAL_STM32_OSPEED_LOWEST (0 << 3)
|
||||
#if defined(STM32F0XX) || defined(STM32F30X) || defined(STM32F37X)
|
||||
#define PAL_STM32_OSPEED_MID (1 << 3)
|
||||
#else
|
||||
#define PAL_STM32_OSPEED_MID1 (1 << 3)
|
||||
#define PAL_STM32_OSPEED_MID2 (2 << 3)
|
||||
#endif
|
||||
#define PAL_STM32_OSPEED_HIGHEST (3 << 3)
|
||||
|
||||
#define PAL_STM32_PUDR_MASK (3 << 5)
|
||||
#define PAL_STM32_PUDR_FLOATING (0 << 5)
|
||||
#define PAL_STM32_PUDR_PULLUP (1 << 5)
|
||||
#define PAL_STM32_PUDR_PULLDOWN (2 << 5)
|
||||
|
||||
#define PAL_STM32_ALTERNATE_MASK (15 << 7)
|
||||
#define PAL_STM32_ALTERNATE(n) ((n) << 7)
|
||||
|
||||
/**
|
||||
* @brief Alternate function.
|
||||
*
|
||||
* @param[in] n alternate function selector
|
||||
*/
|
||||
#define PAL_MODE_ALTERNATE(n) (PAL_STM32_MODE_ALTERNATE | \
|
||||
PAL_STM32_ALTERNATE(n))
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name Standard I/O mode flags
|
||||
* @{
|
||||
*/
|
||||
/**
|
||||
* @brief This mode is implemented as input.
|
||||
*/
|
||||
#define PAL_MODE_RESET PAL_STM32_MODE_INPUT
|
||||
|
||||
/**
|
||||
* @brief This mode is implemented as input with pull-up.
|
||||
*/
|
||||
#define PAL_MODE_UNCONNECTED PAL_MODE_INPUT_PULLUP
|
||||
|
||||
/**
|
||||
* @brief Regular input high-Z pad.
|
||||
*/
|
||||
#define PAL_MODE_INPUT PAL_STM32_MODE_INPUT
|
||||
|
||||
/**
|
||||
* @brief Input pad with weak pull up resistor.
|
||||
*/
|
||||
#define PAL_MODE_INPUT_PULLUP (PAL_STM32_MODE_INPUT | \
|
||||
PAL_STM32_PUDR_PULLUP)
|
||||
|
||||
/**
|
||||
* @brief Input pad with weak pull down resistor.
|
||||
*/
|
||||
#define PAL_MODE_INPUT_PULLDOWN (PAL_STM32_MODE_INPUT | \
|
||||
PAL_STM32_PUDR_PULLDOWN)
|
||||
|
||||
/**
|
||||
* @brief Analog input mode.
|
||||
*/
|
||||
#define PAL_MODE_INPUT_ANALOG PAL_STM32_MODE_ANALOG
|
||||
|
||||
/**
|
||||
* @brief Push-pull output pad.
|
||||
*/
|
||||
#define PAL_MODE_OUTPUT_PUSHPULL (PAL_STM32_MODE_OUTPUT | \
|
||||
PAL_STM32_OTYPE_PUSHPULL)
|
||||
|
||||
/**
|
||||
* @brief Open-drain output pad.
|
||||
*/
|
||||
#define PAL_MODE_OUTPUT_OPENDRAIN (PAL_STM32_MODE_OUTPUT | \
|
||||
PAL_STM32_OTYPE_OPENDRAIN)
|
||||
/** @} */
|
||||
|
||||
/*===========================================================================*/
|
||||
/* I/O Ports Types and constants. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief STM32 GPIO registers block.
|
||||
*/
|
||||
typedef struct {
|
||||
|
||||
volatile uint32_t MODER;
|
||||
volatile uint32_t OTYPER;
|
||||
volatile uint32_t OSPEEDR;
|
||||
volatile uint32_t PUPDR;
|
||||
volatile uint32_t IDR;
|
||||
volatile uint32_t ODR;
|
||||
volatile union {
|
||||
uint32_t W;
|
||||
struct {
|
||||
uint16_t set;
|
||||
uint16_t clear;
|
||||
} H;
|
||||
} BSRR;
|
||||
volatile uint32_t LCKR;
|
||||
volatile uint32_t AFRL;
|
||||
volatile uint32_t AFRH;
|
||||
} GPIO_TypeDef;
|
||||
|
||||
/**
|
||||
* @brief GPIO port setup info.
|
||||
*/
|
||||
typedef struct {
|
||||
/** Initial value for MODER register.*/
|
||||
uint32_t moder;
|
||||
/** Initial value for OTYPER register.*/
|
||||
uint32_t otyper;
|
||||
/** Initial value for OSPEEDR register.*/
|
||||
uint32_t ospeedr;
|
||||
/** Initial value for PUPDR register.*/
|
||||
uint32_t pupdr;
|
||||
/** Initial value for ODR register.*/
|
||||
uint32_t odr;
|
||||
/** Initial value for AFRL register.*/
|
||||
uint32_t afrl;
|
||||
/** Initial value for AFRH register.*/
|
||||
uint32_t afrh;
|
||||
} stm32_gpio_setup_t;
|
||||
|
||||
/**
|
||||
* @brief STM32 GPIO static initializer.
|
||||
* @details An instance of this structure must be passed to @p palInit() at
|
||||
* system startup time in order to initialize the digital I/O
|
||||
* subsystem. This represents only the initial setup, specific pads
|
||||
* or whole ports can be reprogrammed at later time.
|
||||
*/
|
||||
typedef struct {
|
||||
#if STM32_HAS_GPIOA || defined(__DOXYGEN__)
|
||||
/** @brief Port A setup data.*/
|
||||
stm32_gpio_setup_t PAData;
|
||||
#endif
|
||||
#if STM32_HAS_GPIOB || defined(__DOXYGEN__)
|
||||
/** @brief Port B setup data.*/
|
||||
stm32_gpio_setup_t PBData;
|
||||
#endif
|
||||
#if STM32_HAS_GPIOC || defined(__DOXYGEN__)
|
||||
/** @brief Port C setup data.*/
|
||||
stm32_gpio_setup_t PCData;
|
||||
#endif
|
||||
#if STM32_HAS_GPIOD || defined(__DOXYGEN__)
|
||||
/** @brief Port D setup data.*/
|
||||
stm32_gpio_setup_t PDData;
|
||||
#endif
|
||||
#if STM32_HAS_GPIOE || defined(__DOXYGEN__)
|
||||
/** @brief Port E setup data.*/
|
||||
stm32_gpio_setup_t PEData;
|
||||
#endif
|
||||
#if STM32_HAS_GPIOF || defined(__DOXYGEN__)
|
||||
/** @brief Port F setup data.*/
|
||||
stm32_gpio_setup_t PFData;
|
||||
#endif
|
||||
#if STM32_HAS_GPIOG || defined(__DOXYGEN__)
|
||||
/** @brief Port G setup data.*/
|
||||
stm32_gpio_setup_t PGData;
|
||||
#endif
|
||||
#if STM32_HAS_GPIOH || defined(__DOXYGEN__)
|
||||
/** @brief Port H setup data.*/
|
||||
stm32_gpio_setup_t PHData;
|
||||
#endif
|
||||
#if STM32_HAS_GPIOI || defined(__DOXYGEN__)
|
||||
/** @brief Port I setup data.*/
|
||||
stm32_gpio_setup_t PIData;
|
||||
#endif
|
||||
} PALConfig;
|
||||
|
||||
/**
|
||||
* @brief Width, in bits, of an I/O port.
|
||||
*/
|
||||
#define PAL_IOPORTS_WIDTH 16
|
||||
|
||||
/**
|
||||
* @brief Whole port mask.
|
||||
* @details This macro specifies all the valid bits into a port.
|
||||
*/
|
||||
#define PAL_WHOLE_PORT ((ioportmask_t)0xFFFF)
|
||||
|
||||
/**
|
||||
* @brief Digital I/O port sized unsigned type.
|
||||
*/
|
||||
typedef uint32_t ioportmask_t;
|
||||
|
||||
/**
|
||||
* @brief Digital I/O modes.
|
||||
*/
|
||||
typedef uint32_t iomode_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;
|
||||
|
||||
/*===========================================================================*/
|
||||
/* I/O Ports Identifiers. */
|
||||
/* The low level driver wraps the definitions already present in the STM32 */
|
||||
/* firmware library. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief GPIO port A identifier.
|
||||
*/
|
||||
#if STM32_HAS_GPIOA || defined(__DOXYGEN__)
|
||||
#define IOPORT1 GPIOA
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief GPIO port B identifier.
|
||||
*/
|
||||
#if STM32_HAS_GPIOB || defined(__DOXYGEN__)
|
||||
#define IOPORT2 GPIOB
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief GPIO port C identifier.
|
||||
*/
|
||||
#if STM32_HAS_GPIOC || defined(__DOXYGEN__)
|
||||
#define IOPORT3 GPIOC
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief GPIO port D identifier.
|
||||
*/
|
||||
#if STM32_HAS_GPIOD || defined(__DOXYGEN__)
|
||||
#define IOPORT4 GPIOD
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief GPIO port E identifier.
|
||||
*/
|
||||
#if STM32_HAS_GPIOE || defined(__DOXYGEN__)
|
||||
#define IOPORT5 GPIOE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief GPIO port F identifier.
|
||||
*/
|
||||
#if STM32_HAS_GPIOF || defined(__DOXYGEN__)
|
||||
#define IOPORT6 GPIOF
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief GPIO port G identifier.
|
||||
*/
|
||||
#if STM32_HAS_GPIOG || defined(__DOXYGEN__)
|
||||
#define IOPORT7 GPIOG
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief GPIO port H identifier.
|
||||
*/
|
||||
#if STM32_HAS_GPIOH || defined(__DOXYGEN__)
|
||||
#define IOPORT8 GPIOH
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief GPIO port I identifier.
|
||||
*/
|
||||
#if STM32_HAS_GPIOI || defined(__DOXYGEN__)
|
||||
#define IOPORT9 GPIOI
|
||||
#endif
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Implementation, some of the following macros could be implemented as */
|
||||
/* functions, if so please put them in pal_lld.c. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief GPIO ports subsystem initialization.
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
#define pal_lld_init(config) _pal_lld_init(config)
|
||||
|
||||
/**
|
||||
* @brief Reads an I/O port.
|
||||
* @details This function is implemented by reading the GPIO IDR register, the
|
||||
* implementation has no side effects.
|
||||
* @note This function is not meant to be invoked directly by the application
|
||||
* code.
|
||||
*
|
||||
* @param[in] port port identifier
|
||||
* @return The port bits.
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
#define pal_lld_readport(port) ((port)->IDR)
|
||||
|
||||
/**
|
||||
* @brief Reads the output latch.
|
||||
* @details This function is implemented by reading the GPIO ODR register, the
|
||||
* implementation has no side effects.
|
||||
* @note This function is not meant to be invoked directly by the application
|
||||
* code.
|
||||
*
|
||||
* @param[in] port port identifier
|
||||
* @return The latched logical states.
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
#define pal_lld_readlatch(port) ((port)->ODR)
|
||||
|
||||
/**
|
||||
* @brief Writes on a I/O port.
|
||||
* @details This function is implemented by writing the GPIO ODR register, the
|
||||
* implementation has no side effects.
|
||||
*
|
||||
* @param[in] port port identifier
|
||||
* @param[in] bits bits to be written on the specified port
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
#define pal_lld_writeport(port, bits) ((port)->ODR = (bits))
|
||||
|
||||
/**
|
||||
* @brief Sets a bits mask on a I/O port.
|
||||
* @details This function is implemented by writing the GPIO BSRR register, the
|
||||
* implementation has no side effects.
|
||||
*
|
||||
* @param[in] port port identifier
|
||||
* @param[in] bits bits to be ORed on the specified port
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
#define pal_lld_setport(port, bits) ((port)->BSRR.H.set = (uint16_t)(bits))
|
||||
|
||||
/**
|
||||
* @brief Clears a bits mask on a I/O port.
|
||||
* @details This function is implemented by writing the GPIO BSRR register, the
|
||||
* implementation has no side effects.
|
||||
*
|
||||
* @param[in] port port identifier
|
||||
* @param[in] bits bits to be cleared on the specified port
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
#define pal_lld_clearport(port, bits) ((port)->BSRR.H.clear = (uint16_t)(bits))
|
||||
|
||||
/**
|
||||
* @brief Writes a group of bits.
|
||||
* @details This function is implemented by writing the GPIO BSRR register, the
|
||||
* implementation has no side effects.
|
||||
*
|
||||
* @param[in] port port identifier
|
||||
* @param[in] mask group mask
|
||||
* @param[in] offset the 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) \
|
||||
((port)->BSRR.W = ((~(bits) & (mask)) << (16 + (offset))) | \
|
||||
(((bits) & (mask)) << (offset)))
|
||||
|
||||
/**
|
||||
* @brief Pads group mode setup.
|
||||
* @details This function programs a pads group belonging to the same port
|
||||
* with the specified mode.
|
||||
*
|
||||
* @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 Writes a logical state on an output pad.
|
||||
*
|
||||
* @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_writegroup(port, 1, pad, bit)
|
||||
|
||||
extern const PALConfig pal_default_config;
|
||||
|
||||
#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);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* HAL_USE_PAL */
|
||||
|
||||
#endif /* _PAL_LLD_H_ */
|
||||
|
||||
/** @} */
|
||||
+914
@@ -0,0 +1,914 @@
|
||||
/*
|
||||
ChibiOS/RT - Copyright (C) 2006-2013 Giovanni Di Sirio
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
/*
|
||||
Concepts and parts of this file have been contributed by Uladzimir Pylinsky
|
||||
aka barthess.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file STM32/I2Cv1/i2c_lld.c
|
||||
* @brief STM32 I2C subsystem low level driver source.
|
||||
*
|
||||
* @addtogroup I2C
|
||||
* @{
|
||||
*/
|
||||
|
||||
#include "ch.h"
|
||||
#include "hal.h"
|
||||
|
||||
#if HAL_USE_I2C || defined(__DOXYGEN__)
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local definitions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
#define I2C1_RX_DMA_CHANNEL \
|
||||
STM32_DMA_GETCHANNEL(STM32_I2C_I2C1_RX_DMA_STREAM, \
|
||||
STM32_I2C1_RX_DMA_CHN)
|
||||
|
||||
#define I2C1_TX_DMA_CHANNEL \
|
||||
STM32_DMA_GETCHANNEL(STM32_I2C_I2C1_TX_DMA_STREAM, \
|
||||
STM32_I2C1_TX_DMA_CHN)
|
||||
|
||||
#define I2C2_RX_DMA_CHANNEL \
|
||||
STM32_DMA_GETCHANNEL(STM32_I2C_I2C2_RX_DMA_STREAM, \
|
||||
STM32_I2C2_RX_DMA_CHN)
|
||||
|
||||
#define I2C2_TX_DMA_CHANNEL \
|
||||
STM32_DMA_GETCHANNEL(STM32_I2C_I2C2_TX_DMA_STREAM, \
|
||||
STM32_I2C2_TX_DMA_CHN)
|
||||
|
||||
#define I2C3_RX_DMA_CHANNEL \
|
||||
STM32_DMA_GETCHANNEL(STM32_I2C_I2C3_RX_DMA_STREAM, \
|
||||
STM32_I2C3_RX_DMA_CHN)
|
||||
|
||||
#define I2C3_TX_DMA_CHANNEL \
|
||||
STM32_DMA_GETCHANNEL(STM32_I2C_I2C3_TX_DMA_STREAM, \
|
||||
STM32_I2C3_TX_DMA_CHN)
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver constants. */
|
||||
/*===========================================================================*/
|
||||
|
||||
#define I2C_EV5_MASTER_MODE_SELECT \
|
||||
((uint32_t)(((I2C_SR2_MSL | I2C_SR2_BUSY) << 16) | I2C_SR1_SB))
|
||||
|
||||
#define I2C_EV6_MASTER_TRA_MODE_SELECTED \
|
||||
((uint32_t)(((I2C_SR2_MSL | I2C_SR2_BUSY | I2C_SR2_TRA) << 16) | \
|
||||
I2C_SR1_ADDR | I2C_SR1_TXE))
|
||||
|
||||
#define I2C_EV6_MASTER_REC_MODE_SELECTED \
|
||||
((uint32_t)(((I2C_SR2_MSL | I2C_SR2_BUSY)<< 16) | I2C_SR1_ADDR))
|
||||
|
||||
#define I2C_EV8_2_MASTER_BYTE_TRANSMITTED \
|
||||
((uint32_t)(((I2C_SR2_MSL | I2C_SR2_BUSY | I2C_SR2_TRA) << 16) | \
|
||||
I2C_SR1_BTF | I2C_SR1_TXE))
|
||||
|
||||
#define I2C_EV9_MASTER_ADD10 \
|
||||
((uint32_t)(((I2C_SR2_MSL | I2C_SR2_BUSY) << 16) | I2C_SR1_ADD10))
|
||||
|
||||
#define I2C_EV_MASK 0x00FFFFFF
|
||||
|
||||
#define I2C_ERROR_MASK \
|
||||
((uint16_t)(I2C_SR1_BERR | I2C_SR1_ARLO | I2C_SR1_AF | I2C_SR1_OVR | \
|
||||
I2C_SR1_PECERR | I2C_SR1_TIMEOUT | I2C_SR1_SMBALERT))
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver exported variables. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/** @brief I2C1 driver identifier.*/
|
||||
#if STM32_I2C_USE_I2C1 || defined(__DOXYGEN__)
|
||||
I2CDriver I2CD1;
|
||||
#endif
|
||||
|
||||
/** @brief I2C2 driver identifier.*/
|
||||
#if STM32_I2C_USE_I2C2 || defined(__DOXYGEN__)
|
||||
I2CDriver I2CD2;
|
||||
#endif
|
||||
|
||||
/** @brief I2C3 driver identifier.*/
|
||||
#if STM32_I2C_USE_I2C3 || defined(__DOXYGEN__)
|
||||
I2CDriver I2CD3;
|
||||
#endif
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local variables and types. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Wakes up the waiting thread.
|
||||
*
|
||||
* @param[in] i2cp pointer to the @p I2CDriver object
|
||||
* @param[in] msg wakeup message
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
#define wakeup_isr(i2cp, msg) { \
|
||||
chSysLockFromIsr(); \
|
||||
if ((i2cp)->thread != NULL) { \
|
||||
Thread *tp = (i2cp)->thread; \
|
||||
(i2cp)->thread = NULL; \
|
||||
tp->p_u.rdymsg = (msg); \
|
||||
chSchReadyI(tp); \
|
||||
} \
|
||||
chSysUnlockFromIsr(); \
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Aborts an I2C transaction.
|
||||
*
|
||||
* @param[in] i2cp pointer to the @p I2CDriver object
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
static void i2c_lld_abort_operation(I2CDriver *i2cp) {
|
||||
I2C_TypeDef *dp = i2cp->i2c;
|
||||
|
||||
/* Stops the I2C peripheral.*/
|
||||
dp->CR1 = I2C_CR1_SWRST;
|
||||
dp->CR1 = 0;
|
||||
dp->CR2 = 0;
|
||||
dp->SR1 = 0;
|
||||
|
||||
/* Stops the associated DMA streams.*/
|
||||
dmaStreamDisable(i2cp->dmatx);
|
||||
dmaStreamDisable(i2cp->dmarx);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Handling of stalled I2C transactions.
|
||||
*
|
||||
* @param[in] i2cp pointer to the @p I2CDriver object
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
static void i2c_lld_safety_timeout(void *p) {
|
||||
I2CDriver *i2cp = (I2CDriver *)p;
|
||||
|
||||
chSysLockFromIsr();
|
||||
if (i2cp->thread) {
|
||||
Thread *tp = i2cp->thread;
|
||||
i2c_lld_abort_operation(i2cp);
|
||||
i2cp->thread = NULL;
|
||||
tp->p_u.rdymsg = RDY_TIMEOUT;
|
||||
chSchReadyI(tp);
|
||||
}
|
||||
chSysUnlockFromIsr();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set clock speed.
|
||||
*
|
||||
* @param[in] i2cp pointer to the @p I2CDriver object
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
static void i2c_lld_set_clock(I2CDriver *i2cp) {
|
||||
I2C_TypeDef *dp = i2cp->i2c;
|
||||
uint16_t regCCR, clock_div;
|
||||
int32_t clock_speed = i2cp->config->clock_speed;
|
||||
i2cdutycycle_t duty = i2cp->config->duty_cycle;
|
||||
|
||||
chDbgCheck((i2cp != NULL) && (clock_speed > 0) && (clock_speed <= 4000000),
|
||||
"i2c_lld_set_clock");
|
||||
|
||||
/* CR2 Configuration.*/
|
||||
dp->CR2 &= (uint16_t)~I2C_CR2_FREQ;
|
||||
dp->CR2 |= (uint16_t)I2C_CLK_FREQ;
|
||||
|
||||
/* CCR Configuration.*/
|
||||
regCCR = 0;
|
||||
clock_div = I2C_CCR_CCR;
|
||||
|
||||
if (clock_speed <= 100000) {
|
||||
/* Configure clock_div in standard mode.*/
|
||||
chDbgAssert(duty == STD_DUTY_CYCLE,
|
||||
"i2c_lld_set_clock(), #1",
|
||||
"Invalid standard mode duty cycle");
|
||||
|
||||
/* Standard mode clock_div calculate: Tlow/Thigh = 1/1.*/
|
||||
chDbgAssert((STM32_PCLK1 % (clock_speed * 2)) == 0,
|
||||
"i2c_lld_set_clock(), #2",
|
||||
"PCLK1 must be divided without remainder");
|
||||
clock_div = (uint16_t)(STM32_PCLK1 / (clock_speed * 2));
|
||||
|
||||
chDbgAssert(clock_div >= 0x04,
|
||||
"i2c_lld_set_clock(), #3",
|
||||
"Clock divider less then 0x04 not allowed");
|
||||
regCCR |= (clock_div & I2C_CCR_CCR);
|
||||
|
||||
/* Sets the Maximum Rise Time for standard mode.*/
|
||||
dp->TRISE = I2C_CLK_FREQ + 1;
|
||||
}
|
||||
else if (clock_speed <= 400000) {
|
||||
/* Configure clock_div in fast mode.*/
|
||||
chDbgAssert((duty == FAST_DUTY_CYCLE_2) || (duty == FAST_DUTY_CYCLE_16_9),
|
||||
"i2c_lld_set_clock(), #4",
|
||||
"Invalid fast mode duty cycle");
|
||||
|
||||
if (duty == FAST_DUTY_CYCLE_2) {
|
||||
/* Fast mode clock_div calculate: Tlow/Thigh = 2/1.*/
|
||||
chDbgAssert((STM32_PCLK1 % (clock_speed * 3)) == 0,
|
||||
"i2c_lld_set_clock(), #5",
|
||||
"PCLK1 must be divided without remainder");
|
||||
clock_div = (uint16_t)(STM32_PCLK1 / (clock_speed * 3));
|
||||
}
|
||||
else if (duty == FAST_DUTY_CYCLE_16_9) {
|
||||
/* Fast mode clock_div calculate: Tlow/Thigh = 16/9.*/
|
||||
chDbgAssert((STM32_PCLK1 % (clock_speed * 25)) == 0,
|
||||
"i2c_lld_set_clock(), #6",
|
||||
"PCLK1 must be divided without remainder");
|
||||
clock_div = (uint16_t)(STM32_PCLK1 / (clock_speed * 25));
|
||||
regCCR |= I2C_CCR_DUTY;
|
||||
}
|
||||
|
||||
chDbgAssert(clock_div >= 0x01,
|
||||
"i2c_lld_set_clock(), #7",
|
||||
"Clock divider less then 0x04 not allowed");
|
||||
regCCR |= (I2C_CCR_FS | (clock_div & I2C_CCR_CCR));
|
||||
|
||||
/* Sets the Maximum Rise Time for fast mode.*/
|
||||
dp->TRISE = (I2C_CLK_FREQ * 300 / 1000) + 1;
|
||||
}
|
||||
|
||||
chDbgAssert((clock_div <= I2C_CCR_CCR),
|
||||
"i2c_lld_set_clock(), #8", "the selected clock is too low");
|
||||
|
||||
dp->CCR = regCCR;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set operation mode of I2C hardware.
|
||||
*
|
||||
* @param[in] i2cp pointer to the @p I2CDriver object
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
static void i2c_lld_set_opmode(I2CDriver *i2cp) {
|
||||
I2C_TypeDef *dp = i2cp->i2c;
|
||||
i2copmode_t opmode = i2cp->config->op_mode;
|
||||
uint16_t regCR1;
|
||||
|
||||
regCR1 = dp->CR1;
|
||||
switch (opmode) {
|
||||
case OPMODE_I2C:
|
||||
regCR1 &= (uint16_t)~(I2C_CR1_SMBUS|I2C_CR1_SMBTYPE);
|
||||
break;
|
||||
case OPMODE_SMBUS_DEVICE:
|
||||
regCR1 |= I2C_CR1_SMBUS;
|
||||
regCR1 &= (uint16_t)~(I2C_CR1_SMBTYPE);
|
||||
break;
|
||||
case OPMODE_SMBUS_HOST:
|
||||
regCR1 |= (I2C_CR1_SMBUS|I2C_CR1_SMBTYPE);
|
||||
break;
|
||||
}
|
||||
dp->CR1 = regCR1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief I2C shared ISR code.
|
||||
*
|
||||
* @param[in] i2cp pointer to the @p I2CDriver object
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
static void i2c_lld_serve_event_interrupt(I2CDriver *i2cp) {
|
||||
I2C_TypeDef *dp = i2cp->i2c;
|
||||
uint32_t regSR2 = dp->SR2;
|
||||
uint32_t event = dp->SR1;
|
||||
|
||||
/* Interrupts are disabled just before dmaStreamEnable() because there
|
||||
is no need of interrupts until next transaction begin. All the work is
|
||||
done by the DMA.*/
|
||||
switch (I2C_EV_MASK & (event | (regSR2 << 16))) {
|
||||
case I2C_EV5_MASTER_MODE_SELECT:
|
||||
if ((i2cp->addr >> 8) > 0) {
|
||||
/* 10-bit address: 1 1 1 1 0 X X R/W */
|
||||
dp->DR = 0xF0 | (0x6 & (i2cp->addr >> 8)) | (0x1 & i2cp->addr);
|
||||
} else {
|
||||
dp->DR = i2cp->addr;
|
||||
}
|
||||
break;
|
||||
case I2C_EV9_MASTER_ADD10:
|
||||
/* Set second addr byte (10-bit addressing)*/
|
||||
dp->DR = (0xFF & (i2cp->addr >> 1));
|
||||
break;
|
||||
case I2C_EV6_MASTER_REC_MODE_SELECTED:
|
||||
dp->CR2 &= ~I2C_CR2_ITEVTEN;
|
||||
dmaStreamEnable(i2cp->dmarx);
|
||||
dp->CR2 |= I2C_CR2_LAST; /* Needed in receiver mode. */
|
||||
if (dmaStreamGetTransactionSize(i2cp->dmarx) < 2)
|
||||
dp->CR1 &= ~I2C_CR1_ACK;
|
||||
break;
|
||||
case I2C_EV6_MASTER_TRA_MODE_SELECTED:
|
||||
dp->CR2 &= ~I2C_CR2_ITEVTEN;
|
||||
dmaStreamEnable(i2cp->dmatx);
|
||||
break;
|
||||
case I2C_EV8_2_MASTER_BYTE_TRANSMITTED:
|
||||
/* Catches BTF event after the end of transmission.*/
|
||||
if (dmaStreamGetTransactionSize(i2cp->dmarx) > 0) {
|
||||
/* Starts "read after write" operation, LSB = 1 -> receive.*/
|
||||
i2cp->addr |= 0x01;
|
||||
dp->CR1 |= I2C_CR1_START | I2C_CR1_ACK;
|
||||
return;
|
||||
}
|
||||
dp->CR2 &= ~I2C_CR2_ITEVTEN;
|
||||
dp->CR1 |= I2C_CR1_STOP;
|
||||
wakeup_isr(i2cp, RDY_OK);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
/* Clear ADDR flag. */
|
||||
if (event & (I2C_SR1_ADDR | I2C_SR1_ADD10))
|
||||
(void)dp->SR2;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief DMA RX end IRQ handler.
|
||||
*
|
||||
* @param[in] i2cp pointer to the @p I2CDriver object
|
||||
* @param[in] flags pre-shifted content of the ISR register
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
static void i2c_lld_serve_rx_end_irq(I2CDriver *i2cp, uint32_t flags) {
|
||||
I2C_TypeDef *dp = i2cp->i2c;
|
||||
|
||||
/* DMA errors handling.*/
|
||||
#if defined(STM32_I2C_DMA_ERROR_HOOK)
|
||||
if ((flags & (STM32_DMA_ISR_TEIF | STM32_DMA_ISR_DMEIF)) != 0) {
|
||||
STM32_I2C_DMA_ERROR_HOOK(i2cp);
|
||||
}
|
||||
#else
|
||||
(void)flags;
|
||||
#endif
|
||||
|
||||
dmaStreamDisable(i2cp->dmarx);
|
||||
|
||||
dp->CR2 &= ~I2C_CR2_LAST;
|
||||
dp->CR1 &= ~I2C_CR1_ACK;
|
||||
dp->CR1 |= I2C_CR1_STOP;
|
||||
wakeup_isr(i2cp, RDY_OK);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief DMA TX end IRQ handler.
|
||||
*
|
||||
* @param[in] i2cp pointer to the @p I2CDriver object
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
static void i2c_lld_serve_tx_end_irq(I2CDriver *i2cp, uint32_t flags) {
|
||||
I2C_TypeDef *dp = i2cp->i2c;
|
||||
|
||||
/* DMA errors handling.*/
|
||||
#if defined(STM32_I2C_DMA_ERROR_HOOK)
|
||||
if ((flags & (STM32_DMA_ISR_TEIF | STM32_DMA_ISR_DMEIF)) != 0) {
|
||||
STM32_I2C_DMA_ERROR_HOOK(i2cp);
|
||||
}
|
||||
#else
|
||||
(void)flags;
|
||||
#endif
|
||||
|
||||
dmaStreamDisable(i2cp->dmatx);
|
||||
/* Enables interrupts to catch BTF event meaning transmission part complete.
|
||||
Interrupt handler will decide to generate STOP or to begin receiving part
|
||||
of R/W transaction itself.*/
|
||||
dp->CR2 |= I2C_CR2_ITEVTEN;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief I2C error handler.
|
||||
*
|
||||
* @param[in] i2cp pointer to the @p I2CDriver object
|
||||
* @param[in] sr content of the SR1 register to be decoded
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
static void i2c_lld_serve_error_interrupt(I2CDriver *i2cp, uint16_t sr) {
|
||||
|
||||
/* Clears interrupt flags just to be safe.*/
|
||||
dmaStreamDisable(i2cp->dmatx);
|
||||
dmaStreamDisable(i2cp->dmarx);
|
||||
|
||||
i2cp->errors = I2CD_NO_ERROR;
|
||||
|
||||
if (sr & I2C_SR1_BERR) /* Bus error. */
|
||||
i2cp->errors |= I2CD_BUS_ERROR;
|
||||
|
||||
if (sr & I2C_SR1_ARLO) /* Arbitration lost. */
|
||||
i2cp->errors |= I2CD_ARBITRATION_LOST;
|
||||
|
||||
if (sr & I2C_SR1_AF) { /* Acknowledge fail. */
|
||||
i2cp->i2c->CR2 &= ~I2C_CR2_ITEVTEN;
|
||||
i2cp->i2c->CR1 |= I2C_CR1_STOP; /* Setting stop bit. */
|
||||
i2cp->errors |= I2CD_ACK_FAILURE;
|
||||
}
|
||||
|
||||
if (sr & I2C_SR1_OVR) /* Overrun. */
|
||||
i2cp->errors |= I2CD_OVERRUN;
|
||||
|
||||
if (sr & I2C_SR1_TIMEOUT) /* SMBus Timeout. */
|
||||
i2cp->errors |= I2CD_TIMEOUT;
|
||||
|
||||
if (sr & I2C_SR1_PECERR) /* PEC error. */
|
||||
i2cp->errors |= I2CD_PEC_ERROR;
|
||||
|
||||
if (sr & I2C_SR1_SMBALERT) /* SMBus alert. */
|
||||
i2cp->errors |= I2CD_SMB_ALERT;
|
||||
|
||||
/* If some error has been identified then sends wakes the waiting thread.*/
|
||||
if (i2cp->errors != I2CD_NO_ERROR)
|
||||
wakeup_isr(i2cp, RDY_RESET);
|
||||
}
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver interrupt handlers. */
|
||||
/*===========================================================================*/
|
||||
|
||||
#if STM32_I2C_USE_I2C1 || defined(__DOXYGEN__)
|
||||
/**
|
||||
* @brief I2C1 event interrupt handler.
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
CH_IRQ_HANDLER(I2C1_EV_IRQHandler) {
|
||||
|
||||
CH_IRQ_PROLOGUE();
|
||||
|
||||
i2c_lld_serve_event_interrupt(&I2CD1);
|
||||
|
||||
CH_IRQ_EPILOGUE();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief I2C1 error interrupt handler.
|
||||
*/
|
||||
CH_IRQ_HANDLER(I2C1_ER_IRQHandler) {
|
||||
uint16_t sr = I2CD1.i2c->SR1;
|
||||
|
||||
CH_IRQ_PROLOGUE();
|
||||
|
||||
I2CD1.i2c->SR1 = ~(sr & I2C_ERROR_MASK);
|
||||
i2c_lld_serve_error_interrupt(&I2CD1, sr);
|
||||
|
||||
CH_IRQ_EPILOGUE();
|
||||
}
|
||||
#endif /* STM32_I2C_USE_I2C1 */
|
||||
|
||||
#if STM32_I2C_USE_I2C2 || defined(__DOXYGEN__)
|
||||
/**
|
||||
* @brief I2C2 event interrupt handler.
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
CH_IRQ_HANDLER(I2C2_EV_IRQHandler) {
|
||||
|
||||
CH_IRQ_PROLOGUE();
|
||||
|
||||
i2c_lld_serve_event_interrupt(&I2CD2);
|
||||
|
||||
CH_IRQ_EPILOGUE();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief I2C2 error interrupt handler.
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
CH_IRQ_HANDLER(I2C2_ER_IRQHandler) {
|
||||
uint16_t sr = I2CD2.i2c->SR1;
|
||||
|
||||
CH_IRQ_PROLOGUE();
|
||||
|
||||
I2CD2.i2c->SR1 = ~(sr & I2C_ERROR_MASK);
|
||||
i2c_lld_serve_error_interrupt(&I2CD2, sr);
|
||||
|
||||
CH_IRQ_EPILOGUE();
|
||||
}
|
||||
#endif /* STM32_I2C_USE_I2C2 */
|
||||
|
||||
#if STM32_I2C_USE_I2C3 || defined(__DOXYGEN__)
|
||||
/**
|
||||
* @brief I2C3 event interrupt handler.
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
CH_IRQ_HANDLER(I2C3_EV_IRQHandler) {
|
||||
|
||||
CH_IRQ_PROLOGUE();
|
||||
|
||||
i2c_lld_serve_event_interrupt(&I2CD3);
|
||||
|
||||
CH_IRQ_EPILOGUE();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief I2C3 error interrupt handler.
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
CH_IRQ_HANDLER(I2C3_ER_IRQHandler) {
|
||||
uint16_t sr = I2CD3.i2c->SR1;
|
||||
|
||||
CH_IRQ_PROLOGUE();
|
||||
|
||||
I2CD3.i2c->SR1 = ~(sr & I2C_ERROR_MASK);
|
||||
i2c_lld_serve_error_interrupt(&I2CD3, sr);
|
||||
|
||||
CH_IRQ_EPILOGUE();
|
||||
}
|
||||
#endif /* STM32_I2C_USE_I2C3 */
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver exported functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Low level I2C driver initialization.
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void i2c_lld_init(void) {
|
||||
|
||||
#if STM32_I2C_USE_I2C1
|
||||
i2cObjectInit(&I2CD1);
|
||||
I2CD1.thread = NULL;
|
||||
I2CD1.i2c = I2C1;
|
||||
I2CD1.dmarx = STM32_DMA_STREAM(STM32_I2C_I2C1_RX_DMA_STREAM);
|
||||
I2CD1.dmatx = STM32_DMA_STREAM(STM32_I2C_I2C1_TX_DMA_STREAM);
|
||||
#endif /* STM32_I2C_USE_I2C1 */
|
||||
|
||||
#if STM32_I2C_USE_I2C2
|
||||
i2cObjectInit(&I2CD2);
|
||||
I2CD2.thread = NULL;
|
||||
I2CD2.i2c = I2C2;
|
||||
I2CD2.dmarx = STM32_DMA_STREAM(STM32_I2C_I2C2_RX_DMA_STREAM);
|
||||
I2CD2.dmatx = STM32_DMA_STREAM(STM32_I2C_I2C2_TX_DMA_STREAM);
|
||||
#endif /* STM32_I2C_USE_I2C2 */
|
||||
|
||||
#if STM32_I2C_USE_I2C3
|
||||
i2cObjectInit(&I2CD3);
|
||||
I2CD3.thread = NULL;
|
||||
I2CD3.i2c = I2C3;
|
||||
I2CD3.dmarx = STM32_DMA_STREAM(STM32_I2C_I2C3_RX_DMA_STREAM);
|
||||
I2CD3.dmatx = STM32_DMA_STREAM(STM32_I2C_I2C3_TX_DMA_STREAM);
|
||||
#endif /* STM32_I2C_USE_I2C3 */
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Configures and activates the I2C peripheral.
|
||||
*
|
||||
* @param[in] i2cp pointer to the @p I2CDriver object
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void i2c_lld_start(I2CDriver *i2cp) {
|
||||
I2C_TypeDef *dp = i2cp->i2c;
|
||||
|
||||
i2cp->txdmamode = STM32_DMA_CR_PSIZE_BYTE | STM32_DMA_CR_MSIZE_BYTE |
|
||||
STM32_DMA_CR_MINC | STM32_DMA_CR_DMEIE |
|
||||
STM32_DMA_CR_TEIE | STM32_DMA_CR_TCIE |
|
||||
STM32_DMA_CR_DIR_M2P;
|
||||
i2cp->rxdmamode = STM32_DMA_CR_PSIZE_BYTE | STM32_DMA_CR_MSIZE_BYTE |
|
||||
STM32_DMA_CR_MINC | STM32_DMA_CR_DMEIE |
|
||||
STM32_DMA_CR_TEIE | STM32_DMA_CR_TCIE |
|
||||
STM32_DMA_CR_DIR_P2M;
|
||||
|
||||
/* If in stopped state then enables the I2C and DMA clocks.*/
|
||||
if (i2cp->state == I2C_STOP) {
|
||||
|
||||
#if STM32_I2C_USE_I2C1
|
||||
if (&I2CD1 == i2cp) {
|
||||
bool_t b;
|
||||
|
||||
rccResetI2C1();
|
||||
b = dmaStreamAllocate(i2cp->dmarx,
|
||||
STM32_I2C_I2C1_IRQ_PRIORITY,
|
||||
(stm32_dmaisr_t)i2c_lld_serve_rx_end_irq,
|
||||
(void *)i2cp);
|
||||
chDbgAssert(!b, "i2c_lld_start(), #1", "stream already allocated");
|
||||
b = dmaStreamAllocate(i2cp->dmatx,
|
||||
STM32_I2C_I2C1_IRQ_PRIORITY,
|
||||
(stm32_dmaisr_t)i2c_lld_serve_tx_end_irq,
|
||||
(void *)i2cp);
|
||||
chDbgAssert(!b, "i2c_lld_start(), #2", "stream already allocated");
|
||||
rccEnableI2C1(FALSE);
|
||||
nvicEnableVector(I2C1_EV_IRQn,
|
||||
CORTEX_PRIORITY_MASK(STM32_I2C_I2C1_IRQ_PRIORITY));
|
||||
nvicEnableVector(I2C1_ER_IRQn,
|
||||
CORTEX_PRIORITY_MASK(STM32_I2C_I2C1_IRQ_PRIORITY));
|
||||
|
||||
i2cp->rxdmamode |= STM32_DMA_CR_CHSEL(I2C1_RX_DMA_CHANNEL) |
|
||||
STM32_DMA_CR_PL(STM32_I2C_I2C1_DMA_PRIORITY);
|
||||
i2cp->txdmamode |= STM32_DMA_CR_CHSEL(I2C1_TX_DMA_CHANNEL) |
|
||||
STM32_DMA_CR_PL(STM32_I2C_I2C1_DMA_PRIORITY);
|
||||
}
|
||||
#endif /* STM32_I2C_USE_I2C1 */
|
||||
|
||||
#if STM32_I2C_USE_I2C2
|
||||
if (&I2CD2 == i2cp) {
|
||||
bool_t b;
|
||||
|
||||
rccResetI2C2();
|
||||
b = dmaStreamAllocate(i2cp->dmarx,
|
||||
STM32_I2C_I2C2_IRQ_PRIORITY,
|
||||
(stm32_dmaisr_t)i2c_lld_serve_rx_end_irq,
|
||||
(void *)i2cp);
|
||||
chDbgAssert(!b, "i2c_lld_start(), #3", "stream already allocated");
|
||||
b = dmaStreamAllocate(i2cp->dmatx,
|
||||
STM32_I2C_I2C2_IRQ_PRIORITY,
|
||||
(stm32_dmaisr_t)i2c_lld_serve_tx_end_irq,
|
||||
(void *)i2cp);
|
||||
chDbgAssert(!b, "i2c_lld_start(), #4", "stream already allocated");
|
||||
rccEnableI2C2(FALSE);
|
||||
nvicEnableVector(I2C2_EV_IRQn,
|
||||
CORTEX_PRIORITY_MASK(STM32_I2C_I2C2_IRQ_PRIORITY));
|
||||
nvicEnableVector(I2C2_ER_IRQn,
|
||||
CORTEX_PRIORITY_MASK(STM32_I2C_I2C2_IRQ_PRIORITY));
|
||||
|
||||
i2cp->rxdmamode |= STM32_DMA_CR_CHSEL(I2C2_RX_DMA_CHANNEL) |
|
||||
STM32_DMA_CR_PL(STM32_I2C_I2C2_DMA_PRIORITY);
|
||||
i2cp->txdmamode |= STM32_DMA_CR_CHSEL(I2C2_TX_DMA_CHANNEL) |
|
||||
STM32_DMA_CR_PL(STM32_I2C_I2C2_DMA_PRIORITY);
|
||||
}
|
||||
#endif /* STM32_I2C_USE_I2C2 */
|
||||
|
||||
#if STM32_I2C_USE_I2C3
|
||||
if (&I2CD3 == i2cp) {
|
||||
bool_t b;
|
||||
|
||||
rccResetI2C3();
|
||||
b = dmaStreamAllocate(i2cp->dmarx,
|
||||
STM32_I2C_I2C3_IRQ_PRIORITY,
|
||||
(stm32_dmaisr_t)i2c_lld_serve_rx_end_irq,
|
||||
(void *)i2cp);
|
||||
chDbgAssert(!b, "i2c_lld_start(), #5", "stream already allocated");
|
||||
b = dmaStreamAllocate(i2cp->dmatx,
|
||||
STM32_I2C_I2C3_IRQ_PRIORITY,
|
||||
(stm32_dmaisr_t)i2c_lld_serve_tx_end_irq,
|
||||
(void *)i2cp);
|
||||
chDbgAssert(!b, "i2c_lld_start(), #6", "stream already allocated");
|
||||
rccEnableI2C3(FALSE);
|
||||
nvicEnableVector(I2C3_EV_IRQn,
|
||||
CORTEX_PRIORITY_MASK(STM32_I2C_I2C3_IRQ_PRIORITY));
|
||||
nvicEnableVector(I2C3_ER_IRQn,
|
||||
CORTEX_PRIORITY_MASK(STM32_I2C_I2C3_IRQ_PRIORITY));
|
||||
|
||||
i2cp->rxdmamode |= STM32_DMA_CR_CHSEL(I2C3_RX_DMA_CHANNEL) |
|
||||
STM32_DMA_CR_PL(STM32_I2C_I2C3_DMA_PRIORITY);
|
||||
i2cp->txdmamode |= STM32_DMA_CR_CHSEL(I2C3_TX_DMA_CHANNEL) |
|
||||
STM32_DMA_CR_PL(STM32_I2C_I2C3_DMA_PRIORITY);
|
||||
}
|
||||
#endif /* STM32_I2C_USE_I2C3 */
|
||||
}
|
||||
|
||||
/* I2C registers pointed by the DMA.*/
|
||||
dmaStreamSetPeripheral(i2cp->dmarx, &dp->DR);
|
||||
dmaStreamSetPeripheral(i2cp->dmatx, &dp->DR);
|
||||
|
||||
/* Reset i2c peripheral.*/
|
||||
dp->CR1 = I2C_CR1_SWRST;
|
||||
dp->CR1 = 0;
|
||||
dp->CR2 = I2C_CR2_ITERREN | I2C_CR2_DMAEN;
|
||||
|
||||
/* Setup I2C parameters.*/
|
||||
i2c_lld_set_clock(i2cp);
|
||||
i2c_lld_set_opmode(i2cp);
|
||||
|
||||
/* Ready to go.*/
|
||||
dp->CR1 |= I2C_CR1_PE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Deactivates the I2C peripheral.
|
||||
*
|
||||
* @param[in] i2cp pointer to the @p I2CDriver object
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void i2c_lld_stop(I2CDriver *i2cp) {
|
||||
|
||||
/* If not in stopped state then disables the I2C clock.*/
|
||||
if (i2cp->state != I2C_STOP) {
|
||||
|
||||
/* I2C disable.*/
|
||||
i2c_lld_abort_operation(i2cp);
|
||||
dmaStreamRelease(i2cp->dmatx);
|
||||
dmaStreamRelease(i2cp->dmarx);
|
||||
|
||||
#if STM32_I2C_USE_I2C1
|
||||
if (&I2CD1 == i2cp) {
|
||||
nvicDisableVector(I2C1_EV_IRQn);
|
||||
nvicDisableVector(I2C1_ER_IRQn);
|
||||
rccDisableI2C1(FALSE);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if STM32_I2C_USE_I2C2
|
||||
if (&I2CD2 == i2cp) {
|
||||
nvicDisableVector(I2C2_EV_IRQn);
|
||||
nvicDisableVector(I2C2_ER_IRQn);
|
||||
rccDisableI2C2(FALSE);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if STM32_I2C_USE_I2C3
|
||||
if (&I2CD3 == i2cp) {
|
||||
nvicDisableVector(I2C3_EV_IRQn);
|
||||
nvicDisableVector(I2C3_ER_IRQn);
|
||||
rccDisableI2C3(FALSE);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Receives data via the I2C bus as master.
|
||||
* @details Number of receiving bytes must be more than 1 on STM32F1x. This is
|
||||
* hardware restriction.
|
||||
*
|
||||
* @param[in] i2cp pointer to the @p I2CDriver object
|
||||
* @param[in] addr slave device address
|
||||
* @param[out] rxbuf pointer to the receive buffer
|
||||
* @param[in] rxbytes number of bytes to be received
|
||||
* @param[in] timeout the number of ticks before the operation timeouts,
|
||||
* the following special values are allowed:
|
||||
* - @a TIME_INFINITE no timeout.
|
||||
* .
|
||||
* @return The operation status.
|
||||
* @retval RDY_OK if the function succeeded.
|
||||
* @retval RDY_RESET if one or more I2C errors occurred, the errors can
|
||||
* be retrieved using @p i2cGetErrors().
|
||||
* @retval RDY_TIMEOUT if a timeout occurred before operation end. <b>After a
|
||||
* timeout the driver must be stopped and restarted
|
||||
* because the bus is in an uncertain state</b>.
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
msg_t i2c_lld_master_receive_timeout(I2CDriver *i2cp, i2caddr_t addr,
|
||||
uint8_t *rxbuf, size_t rxbytes,
|
||||
systime_t timeout) {
|
||||
I2C_TypeDef *dp = i2cp->i2c;
|
||||
VirtualTimer vt;
|
||||
|
||||
#if defined(STM32F1XX_I2C)
|
||||
chDbgCheck((rxbytes > 1), "i2c_lld_master_receive_timeout");
|
||||
#endif
|
||||
|
||||
/* Global timeout for the whole operation.*/
|
||||
if (timeout != TIME_INFINITE)
|
||||
chVTSetI(&vt, timeout, i2c_lld_safety_timeout, (void *)i2cp);
|
||||
|
||||
/* Releases the lock from high level driver.*/
|
||||
chSysUnlock();
|
||||
|
||||
/* Initializes driver fields, LSB = 1 -> receive.*/
|
||||
i2cp->addr = (addr << 1) | 0x01;
|
||||
i2cp->errors = 0;
|
||||
|
||||
/* RX DMA setup.*/
|
||||
dmaStreamSetMode(i2cp->dmarx, i2cp->rxdmamode);
|
||||
dmaStreamSetMemory0(i2cp->dmarx, rxbuf);
|
||||
dmaStreamSetTransactionSize(i2cp->dmarx, rxbytes);
|
||||
|
||||
/* Waits until BUSY flag is reset and the STOP from the previous operation
|
||||
is completed, alternatively for a timeout condition.*/
|
||||
while ((dp->SR2 & I2C_SR2_BUSY) || (dp->CR1 & I2C_CR1_STOP)) {
|
||||
chSysLock();
|
||||
if ((timeout != TIME_INFINITE) && !chVTIsArmedI(&vt))
|
||||
return RDY_TIMEOUT;
|
||||
chSysUnlock();
|
||||
}
|
||||
|
||||
/* This lock will be released in high level driver.*/
|
||||
chSysLock();
|
||||
|
||||
/* Atomic check on the timer in order to make sure that a timeout didn't
|
||||
happen outside the critical zone.*/
|
||||
if ((timeout != TIME_INFINITE) && !chVTIsArmedI(&vt))
|
||||
return RDY_TIMEOUT;
|
||||
|
||||
/* Starts the operation.*/
|
||||
dp->CR2 |= I2C_CR2_ITEVTEN;
|
||||
dp->CR1 |= I2C_CR1_START | I2C_CR1_ACK;
|
||||
|
||||
/* Waits for the operation completion or a timeout.*/
|
||||
i2cp->thread = chThdSelf();
|
||||
chSchGoSleepS(THD_STATE_SUSPENDED);
|
||||
if ((timeout != TIME_INFINITE) && chVTIsArmedI(&vt))
|
||||
chVTResetI(&vt);
|
||||
|
||||
return chThdSelf()->p_u.rdymsg;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Transmits data via the I2C bus as master.
|
||||
* @details Number of receiving bytes must be 0 or more than 1 on STM32F1x.
|
||||
* This is hardware restriction.
|
||||
*
|
||||
* @param[in] i2cp pointer to the @p I2CDriver object
|
||||
* @param[in] addr slave device address
|
||||
* @param[in] txbuf pointer to the transmit buffer
|
||||
* @param[in] txbytes number of bytes to be transmitted
|
||||
* @param[out] rxbuf pointer to the receive buffer
|
||||
* @param[in] rxbytes number of bytes to be received
|
||||
* @param[in] timeout the number of ticks before the operation timeouts,
|
||||
* the following special values are allowed:
|
||||
* - @a TIME_INFINITE no timeout.
|
||||
* .
|
||||
* @return The operation status.
|
||||
* @retval RDY_OK if the function succeeded.
|
||||
* @retval RDY_RESET if one or more I2C errors occurred, the errors can
|
||||
* be retrieved using @p i2cGetErrors().
|
||||
* @retval RDY_TIMEOUT if a timeout occurred before operation end. <b>After a
|
||||
* timeout the driver must be stopped and restarted
|
||||
* because the bus is in an uncertain state</b>.
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
msg_t i2c_lld_master_transmit_timeout(I2CDriver *i2cp, i2caddr_t addr,
|
||||
const uint8_t *txbuf, size_t txbytes,
|
||||
uint8_t *rxbuf, size_t rxbytes,
|
||||
systime_t timeout) {
|
||||
I2C_TypeDef *dp = i2cp->i2c;
|
||||
VirtualTimer vt;
|
||||
|
||||
#if defined(STM32F1XX_I2C)
|
||||
chDbgCheck(((rxbytes == 0) || ((rxbytes > 1) && (rxbuf != NULL))),
|
||||
"i2c_lld_master_transmit_timeout");
|
||||
#endif
|
||||
|
||||
/* Global timeout for the whole operation.*/
|
||||
if (timeout != TIME_INFINITE)
|
||||
chVTSetI(&vt, timeout, i2c_lld_safety_timeout, (void *)i2cp);
|
||||
|
||||
/* Releases the lock from high level driver.*/
|
||||
chSysUnlock();
|
||||
|
||||
/* Initializes driver fields, LSB = 0 -> write.*/
|
||||
i2cp->addr = addr << 1;
|
||||
i2cp->errors = 0;
|
||||
|
||||
/* TX DMA setup.*/
|
||||
dmaStreamSetMode(i2cp->dmatx, i2cp->txdmamode);
|
||||
dmaStreamSetMemory0(i2cp->dmatx, txbuf);
|
||||
dmaStreamSetTransactionSize(i2cp->dmatx, txbytes);
|
||||
|
||||
/* RX DMA setup.*/
|
||||
dmaStreamSetMode(i2cp->dmarx, i2cp->rxdmamode);
|
||||
dmaStreamSetMemory0(i2cp->dmarx, rxbuf);
|
||||
dmaStreamSetTransactionSize(i2cp->dmarx, rxbytes);
|
||||
|
||||
/* Waits until BUSY flag is reset and the STOP from the previous operation
|
||||
is completed, alternatively for a timeout condition.*/
|
||||
while ((dp->SR2 & I2C_SR2_BUSY) || (dp->CR1 & I2C_CR1_STOP)) {
|
||||
chSysLock();
|
||||
if ((timeout != TIME_INFINITE) && !chVTIsArmedI(&vt))
|
||||
return RDY_TIMEOUT;
|
||||
chSysUnlock();
|
||||
}
|
||||
|
||||
/* This lock will be released in high level driver.*/
|
||||
chSysLock();
|
||||
|
||||
/* Atomic check on the timer in order to make sure that a timeout didn't
|
||||
happen outside the critical zone.*/
|
||||
if ((timeout != TIME_INFINITE) && !chVTIsArmedI(&vt))
|
||||
return RDY_TIMEOUT;
|
||||
|
||||
/* Starts the operation.*/
|
||||
dp->CR2 |= I2C_CR2_ITEVTEN;
|
||||
dp->CR1 |= I2C_CR1_START;
|
||||
|
||||
/* Waits for the operation completion or a timeout.*/
|
||||
i2cp->thread = chThdSelf();
|
||||
chSchGoSleepS(THD_STATE_SUSPENDED);
|
||||
if ((timeout != TIME_INFINITE) && chVTIsArmedI(&vt))
|
||||
chVTResetI(&vt);
|
||||
|
||||
return chThdSelf()->p_u.rdymsg;
|
||||
}
|
||||
|
||||
#endif /* HAL_USE_I2C */
|
||||
|
||||
/** @} */
|
||||
+463
@@ -0,0 +1,463 @@
|
||||
/*
|
||||
ChibiOS/RT - Copyright (C) 2006-2013 Giovanni Di Sirio
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
/*
|
||||
Concepts and parts of this file have been contributed by Uladzimir Pylinsky
|
||||
aka barthess.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file STM32/I2Cv1/i2c_lld.h
|
||||
* @brief STM32 I2C subsystem low level driver header.
|
||||
*
|
||||
* @addtogroup I2C
|
||||
* @{
|
||||
*/
|
||||
|
||||
#ifndef _I2C_LLD_H_
|
||||
#define _I2C_LLD_H_
|
||||
|
||||
#if HAL_USE_I2C || defined(__DOXYGEN__)
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver constants. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Peripheral clock frequency.
|
||||
*/
|
||||
#define I2C_CLK_FREQ ((STM32_PCLK1) / 1000000)
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver pre-compile time settings. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @name Configuration options
|
||||
* @{
|
||||
*/
|
||||
/**
|
||||
* @brief I2C1 driver enable switch.
|
||||
* @details If set to @p TRUE the support for I2C1 is included.
|
||||
* @note The default is @p FALSE.
|
||||
*/
|
||||
#if !defined(STM32_I2C_USE_I2C1) || defined(__DOXYGEN__)
|
||||
#define STM32_I2C_USE_I2C1 FALSE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief I2C2 driver enable switch.
|
||||
* @details If set to @p TRUE the support for I2C2 is included.
|
||||
* @note The default is @p FALSE.
|
||||
*/
|
||||
#if !defined(STM32_I2C_USE_I2C2) || defined(__DOXYGEN__)
|
||||
#define STM32_I2C_USE_I2C2 FALSE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief I2C3 driver enable switch.
|
||||
* @details If set to @p TRUE the support for I2C3 is included.
|
||||
* @note The default is @p FALSE.
|
||||
*/
|
||||
#if !defined(STM32_I2C_USE_I2C3) || defined(__DOXYGEN__)
|
||||
#define STM32_I2C_USE_I2C3 FALSE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief I2C1 interrupt priority level setting.
|
||||
*/
|
||||
#if !defined(STM32_I2C_I2C1_IRQ_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define STM32_I2C_I2C1_IRQ_PRIORITY 10
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief I2C2 interrupt priority level setting.
|
||||
*/
|
||||
#if !defined(STM32_I2C_I2C2_IRQ_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define STM32_I2C_I2C2_IRQ_PRIORITY 10
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief I2C3 interrupt priority level setting.
|
||||
*/
|
||||
#if !defined(STM32_I2C_I2C3_IRQ_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define STM32_I2C_I2C3_IRQ_PRIORITY 10
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief I2C1 DMA priority (0..3|lowest..highest).
|
||||
* @note The priority level is used for both the TX and RX DMA streams but
|
||||
* because of the streams ordering the RX stream has always priority
|
||||
* over the TX stream.
|
||||
*/
|
||||
#if !defined(STM32_I2C_I2C1_DMA_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define STM32_I2C_I2C1_DMA_PRIORITY 1
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief I2C2 DMA priority (0..3|lowest..highest).
|
||||
* @note The priority level is used for both the TX and RX DMA streams but
|
||||
* because of the streams ordering the RX stream has always priority
|
||||
* over the TX stream.
|
||||
*/
|
||||
#if !defined(STM32_I2C_I2C2_DMA_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define STM32_I2C_I2C2_DMA_PRIORITY 1
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief I2C3 DMA priority (0..3|lowest..highest).
|
||||
* @note The priority level is used for both the TX and RX DMA streams but
|
||||
* because of the streams ordering the RX stream has always priority
|
||||
* over the TX stream.
|
||||
*/
|
||||
#if !defined(STM32_I2C_I2C3_DMA_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define STM32_I2C_I2C3_DMA_PRIORITY 1
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief I2C DMA error hook.
|
||||
* @note The default action for DMA errors is a system halt because DMA
|
||||
* error can only happen because programming errors.
|
||||
*/
|
||||
#if !defined(STM32_I2C_DMA_ERROR_HOOK) || defined(__DOXYGEN__)
|
||||
#define STM32_I2C_DMA_ERROR_HOOK(i2cp) chSysHalt()
|
||||
#endif
|
||||
|
||||
#if STM32_ADVANCED_DMA || defined(__DOXYGEN__)
|
||||
|
||||
/**
|
||||
* @brief DMA stream used for I2C1 RX operations.
|
||||
* @note This option is only available on platforms with enhanced DMA.
|
||||
*/
|
||||
#if !defined(STM32_I2C_I2C1_RX_DMA_STREAM) || defined(__DOXYGEN__)
|
||||
#define STM32_I2C_I2C1_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 0)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief DMA stream used for I2C1 TX operations.
|
||||
* @note This option is only available on platforms with enhanced DMA.
|
||||
*/
|
||||
#if !defined(STM32_I2C_I2C1_TX_DMA_STREAM) || defined(__DOXYGEN__)
|
||||
#define STM32_I2C_I2C1_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 6)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief DMA stream used for I2C2 RX operations.
|
||||
* @note This option is only available on platforms with enhanced DMA.
|
||||
*/
|
||||
#if !defined(STM32_I2C_I2C2_RX_DMA_STREAM) || defined(__DOXYGEN__)
|
||||
#define STM32_I2C_I2C2_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 2)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief DMA stream used for I2C2 TX operations.
|
||||
* @note This option is only available on platforms with enhanced DMA.
|
||||
*/
|
||||
#if !defined(STM32_I2C_I2C2_TX_DMA_STREAM) || defined(__DOXYGEN__)
|
||||
#define STM32_I2C_I2C2_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 7)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief DMA stream used for I2C3 RX operations.
|
||||
* @note This option is only available on platforms with enhanced DMA.
|
||||
*/
|
||||
#if !defined(STM32_I2C_I2C3_RX_DMA_STREAM) || defined(__DOXYGEN__)
|
||||
#define STM32_I2C_I2C3_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 2)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief DMA stream used for I2C3 TX operations.
|
||||
* @note This option is only available on platforms with enhanced DMA.
|
||||
*/
|
||||
#if !defined(STM32_I2C_I2C3_TX_DMA_STREAM) || defined(__DOXYGEN__)
|
||||
#define STM32_I2C_I2C3_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 4)
|
||||
#endif
|
||||
|
||||
#else /* !STM32_ADVANCED_DMA */
|
||||
|
||||
/* Fixed streams for platforms using the old DMA peripheral, the values are
|
||||
valid for both STM32F1xx and STM32L1xx.*/
|
||||
#define STM32_I2C_I2C1_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 7)
|
||||
#define STM32_I2C_I2C1_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 6)
|
||||
#define STM32_I2C_I2C2_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 5)
|
||||
#define STM32_I2C_I2C2_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 4)
|
||||
|
||||
#endif /* !STM32_ADVANCED_DMA*/
|
||||
|
||||
/* Flag for the whole STM32F1XX family. */
|
||||
#if defined(STM32F10X_LD_VL) || defined(STM32F10X_MD_VL) || \
|
||||
defined(STM32F10X_LD) || defined(STM32F10X_MD) || \
|
||||
defined(STM32F10X_HD) || defined(STM32F10X_XL) || \
|
||||
defined(STM32F10X_CL)
|
||||
#define STM32F1XX_I2C
|
||||
#endif
|
||||
/** @} */
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Derived constants and error checks. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/** @brief error checks */
|
||||
#if STM32_I2C_USE_I2C1 && !STM32_HAS_I2C1
|
||||
#error "I2C1 not present in the selected device"
|
||||
#endif
|
||||
|
||||
#if STM32_I2C_USE_I2C2 && !STM32_HAS_I2C2
|
||||
#error "I2C2 not present in the selected device"
|
||||
#endif
|
||||
|
||||
#if STM32_I2C_USE_I2C3 && !STM32_HAS_I2C3
|
||||
#error "I2C3 not present in the selected device"
|
||||
#endif
|
||||
|
||||
#if !STM32_I2C_USE_I2C1 && !STM32_I2C_USE_I2C2 && \
|
||||
!STM32_I2C_USE_I2C3
|
||||
#error "I2C driver activated but no I2C peripheral assigned"
|
||||
#endif
|
||||
|
||||
#if STM32_I2C_USE_I2C1 && \
|
||||
!STM32_DMA_IS_VALID_ID(STM32_I2C_I2C1_RX_DMA_STREAM, \
|
||||
STM32_I2C1_RX_DMA_MSK)
|
||||
#error "invalid DMA stream associated to I2C1 RX"
|
||||
#endif
|
||||
|
||||
#if STM32_I2C_USE_I2C1 && \
|
||||
!STM32_DMA_IS_VALID_ID(STM32_I2C_I2C1_TX_DMA_STREAM, \
|
||||
STM32_I2C1_TX_DMA_MSK)
|
||||
#error "invalid DMA stream associated to I2C1 TX"
|
||||
#endif
|
||||
|
||||
#if STM32_I2C_USE_I2C2 && \
|
||||
!STM32_DMA_IS_VALID_ID(STM32_I2C_I2C2_RX_DMA_STREAM, \
|
||||
STM32_I2C2_RX_DMA_MSK)
|
||||
#error "invalid DMA stream associated to I2C2 RX"
|
||||
#endif
|
||||
|
||||
#if STM32_I2C_USE_I2C2 && \
|
||||
!STM32_DMA_IS_VALID_ID(STM32_I2C_I2C2_TX_DMA_STREAM, \
|
||||
STM32_I2C2_TX_DMA_MSK)
|
||||
#error "invalid DMA stream associated to I2C2 TX"
|
||||
#endif
|
||||
|
||||
#if STM32_I2C_USE_I2C3 && \
|
||||
!STM32_DMA_IS_VALID_ID(STM32_I2C_I2C3_RX_DMA_STREAM, \
|
||||
STM32_I2C3_RX_DMA_MSK)
|
||||
#error "invalid DMA stream associated to I2C3 RX"
|
||||
#endif
|
||||
|
||||
#if STM32_I2C_USE_I2C3 && \
|
||||
!STM32_DMA_IS_VALID_ID(STM32_I2C_I2C3_TX_DMA_STREAM, \
|
||||
STM32_I2C3_TX_DMA_MSK)
|
||||
#error "invalid DMA stream associated to I2C3 TX"
|
||||
#endif
|
||||
|
||||
#if !defined(STM32_DMA_REQUIRED)
|
||||
#define STM32_DMA_REQUIRED
|
||||
#endif
|
||||
|
||||
/* Check clock range. */
|
||||
#if defined(STM32F4XX)
|
||||
#if !(I2C_CLK_FREQ >= 2) && (I2C_CLK_FREQ <= 42)
|
||||
#error "I2C peripheral clock frequency out of range."
|
||||
#endif
|
||||
|
||||
#elif defined(STM32L1XX)
|
||||
#if !(I2C_CLK_FREQ >= 2) && (I2C_CLK_FREQ <= 32)
|
||||
#error "I2C peripheral clock frequency out of range."
|
||||
#endif
|
||||
|
||||
#elif defined(STM32F2XX)
|
||||
#if !(I2C_CLK_FREQ >= 2) && (I2C_CLK_FREQ <= 30)
|
||||
#error "I2C peripheral clock frequency out of range."
|
||||
#endif
|
||||
|
||||
#elif defined(STM32F10X_LD_VL) || defined(STM32F10X_MD_VL) || \
|
||||
defined(STM32F10X_HD_VL)
|
||||
#if !(I2C_CLK_FREQ >= 2) && (I2C_CLK_FREQ <= 24)
|
||||
#error "I2C peripheral clock frequency out of range."
|
||||
#endif
|
||||
|
||||
#elif defined(STM32F10X_LD) || defined(STM32F10X_MD) || \
|
||||
defined(STM32F10X_HD) || defined(STM32F10X_XL) || \
|
||||
defined(STM32F10X_CL)
|
||||
#if !(I2C_CLK_FREQ >= 2) && (I2C_CLK_FREQ <= 36)
|
||||
#error "I2C peripheral clock frequency out of range."
|
||||
#endif
|
||||
#else
|
||||
#error "unspecified, unsupported or invalid STM32 platform"
|
||||
#endif
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver data structures and types. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Type representing I2C address.
|
||||
*/
|
||||
typedef uint16_t i2caddr_t;
|
||||
|
||||
/**
|
||||
* @brief I2C Driver condition flags type.
|
||||
*/
|
||||
typedef uint32_t i2cflags_t;
|
||||
|
||||
/**
|
||||
* @brief Supported modes for the I2C bus.
|
||||
*/
|
||||
typedef enum {
|
||||
OPMODE_I2C = 1,
|
||||
OPMODE_SMBUS_DEVICE = 2,
|
||||
OPMODE_SMBUS_HOST = 3,
|
||||
} i2copmode_t;
|
||||
|
||||
/**
|
||||
* @brief Supported duty cycle modes for the I2C bus.
|
||||
*/
|
||||
typedef enum {
|
||||
STD_DUTY_CYCLE = 1,
|
||||
FAST_DUTY_CYCLE_2 = 2,
|
||||
FAST_DUTY_CYCLE_16_9 = 3,
|
||||
} i2cdutycycle_t;
|
||||
|
||||
/**
|
||||
* @brief Driver configuration structure.
|
||||
*/
|
||||
typedef struct {
|
||||
i2copmode_t op_mode; /**< @brief Specifies the I2C mode. */
|
||||
uint32_t clock_speed; /**< @brief Specifies the clock frequency.
|
||||
@note Must be set to a value lower
|
||||
than 400kHz. */
|
||||
i2cdutycycle_t duty_cycle; /**< @brief Specifies the I2C fast mode
|
||||
duty cycle. */
|
||||
} I2CConfig;
|
||||
|
||||
/**
|
||||
* @brief Type of a structure representing an I2C driver.
|
||||
*/
|
||||
typedef struct I2CDriver I2CDriver;
|
||||
|
||||
/**
|
||||
* @brief Structure representing an I2C driver.
|
||||
*/
|
||||
struct I2CDriver {
|
||||
/**
|
||||
* @brief Driver state.
|
||||
*/
|
||||
i2cstate_t state;
|
||||
/**
|
||||
* @brief Current configuration data.
|
||||
*/
|
||||
const I2CConfig *config;
|
||||
/**
|
||||
* @brief Error flags.
|
||||
*/
|
||||
i2cflags_t errors;
|
||||
#if I2C_USE_MUTUAL_EXCLUSION || defined(__DOXYGEN__)
|
||||
#if CH_USE_MUTEXES || defined(__DOXYGEN__)
|
||||
/**
|
||||
* @brief Mutex protecting the bus.
|
||||
*/
|
||||
Mutex mutex;
|
||||
#elif CH_USE_SEMAPHORES
|
||||
Semaphore semaphore;
|
||||
#endif
|
||||
#endif /* I2C_USE_MUTUAL_EXCLUSION */
|
||||
#if defined(I2C_DRIVER_EXT_FIELDS)
|
||||
I2C_DRIVER_EXT_FIELDS
|
||||
#endif
|
||||
/* End of the mandatory fields.*/
|
||||
/**
|
||||
* @brief Thread waiting for I/O completion.
|
||||
*/
|
||||
Thread *thread;
|
||||
/**
|
||||
* @brief Current slave address without R/W bit.
|
||||
*/
|
||||
i2caddr_t addr;
|
||||
/**
|
||||
* @brief RX DMA mode bit mask.
|
||||
*/
|
||||
uint32_t rxdmamode;
|
||||
/**
|
||||
* @brief TX DMA mode bit mask.
|
||||
*/
|
||||
uint32_t txdmamode;
|
||||
/**
|
||||
* @brief Receive DMA channel.
|
||||
*/
|
||||
const stm32_dma_stream_t *dmarx;
|
||||
/**
|
||||
* @brief Transmit DMA channel.
|
||||
*/
|
||||
const stm32_dma_stream_t *dmatx;
|
||||
/**
|
||||
* @brief Pointer to the I2Cx registers block.
|
||||
*/
|
||||
I2C_TypeDef *i2c;
|
||||
};
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver macros. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Get errors from I2C driver.
|
||||
*
|
||||
* @param[in] i2cp pointer to the @p I2CDriver object
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
#define i2c_lld_get_errors(i2cp) ((i2cp)->errors)
|
||||
|
||||
/*===========================================================================*/
|
||||
/* External declarations. */
|
||||
/*===========================================================================*/
|
||||
|
||||
#if !defined(__DOXYGEN__)
|
||||
#if STM32_I2C_USE_I2C1
|
||||
extern I2CDriver I2CD1;
|
||||
#endif
|
||||
|
||||
#if STM32_I2C_USE_I2C2
|
||||
extern I2CDriver I2CD2;
|
||||
#endif
|
||||
|
||||
#if STM32_I2C_USE_I2C3
|
||||
extern I2CDriver I2CD3;
|
||||
#endif
|
||||
#endif /* !defined(__DOXYGEN__) */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
void i2c_lld_init(void);
|
||||
void i2c_lld_start(I2CDriver *i2cp);
|
||||
void i2c_lld_stop(I2CDriver *i2cp);
|
||||
msg_t i2c_lld_master_transmit_timeout(I2CDriver *i2cp, i2caddr_t addr,
|
||||
const uint8_t *txbuf, size_t txbytes,
|
||||
uint8_t *rxbuf, size_t rxbytes,
|
||||
systime_t timeout);
|
||||
msg_t i2c_lld_master_receive_timeout(I2CDriver *i2cp, i2caddr_t addr,
|
||||
uint8_t *rxbuf, size_t rxbytes,
|
||||
systime_t timeout);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* HAL_USE_I2C */
|
||||
|
||||
#endif /* _I2C_LLD_H_ */
|
||||
|
||||
/** @} */
|
||||
+789
@@ -0,0 +1,789 @@
|
||||
/*
|
||||
ChibiOS/RT - Copyright (C) 2006-2013 Giovanni Di Sirio
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
/*
|
||||
Concepts and parts of this file have been contributed by Uladzimir Pylinsky
|
||||
aka barthess.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file STM32/I2Cv2/i2c_lld.c
|
||||
* @brief STM32 I2C subsystem low level driver source.
|
||||
*
|
||||
* @addtogroup I2C
|
||||
* @{
|
||||
*/
|
||||
|
||||
#include "ch.h"
|
||||
#include "hal.h"
|
||||
|
||||
#if HAL_USE_I2C || defined(__DOXYGEN__)
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local definitions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
#define DMAMODE_COMMON \
|
||||
(STM32_DMA_CR_PSIZE_BYTE | STM32_DMA_CR_MSIZE_BYTE | \
|
||||
STM32_DMA_CR_MINC | STM32_DMA_CR_DMEIE | \
|
||||
STM32_DMA_CR_TEIE | STM32_DMA_CR_TCIE)
|
||||
|
||||
#define I2C1_RX_DMA_CHANNEL \
|
||||
STM32_DMA_GETCHANNEL(STM32_I2C_I2C1_RX_DMA_STREAM, \
|
||||
STM32_I2C1_RX_DMA_CHN)
|
||||
|
||||
#define I2C1_TX_DMA_CHANNEL \
|
||||
STM32_DMA_GETCHANNEL(STM32_I2C_I2C1_TX_DMA_STREAM, \
|
||||
STM32_I2C1_TX_DMA_CHN)
|
||||
|
||||
#define I2C2_RX_DMA_CHANNEL \
|
||||
STM32_DMA_GETCHANNEL(STM32_I2C_I2C2_RX_DMA_STREAM, \
|
||||
STM32_I2C2_RX_DMA_CHN)
|
||||
|
||||
#define I2C2_TX_DMA_CHANNEL \
|
||||
STM32_DMA_GETCHANNEL(STM32_I2C_I2C2_TX_DMA_STREAM, \
|
||||
STM32_I2C2_TX_DMA_CHN)
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver constants. */
|
||||
/*===========================================================================*/
|
||||
|
||||
#define I2C_MASTER_TC \
|
||||
((uint32_t)(I2C_ISR_BUSY | I2C_ISR_TC))
|
||||
|
||||
#define I2C_ERROR_MASK \
|
||||
((uint32_t)(I2C_ISR_BERR | I2C_ISR_ARLO | I2C_ISR_OVR | I2C_ISR_PECERR | \
|
||||
I2C_ISR_TIMEOUT | I2C_ISR_ALERT))
|
||||
|
||||
#define I2C_INT_MASK \
|
||||
((uint32_t)(I2C_ISR_TCR | I2C_ISR_TC | I2C_ISR_STOPF | I2C_ISR_NACKF | \
|
||||
I2C_ISR_ADDR | I2C_ISR_RXNE | I2C_ISR_TXIS))
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver exported variables. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/** @brief I2C1 driver identifier.*/
|
||||
#if STM32_I2C_USE_I2C1 || defined(__DOXYGEN__)
|
||||
I2CDriver I2CD1;
|
||||
#endif
|
||||
|
||||
/** @brief I2C2 driver identifier.*/
|
||||
#if STM32_I2C_USE_I2C2 || defined(__DOXYGEN__)
|
||||
I2CDriver I2CD2;
|
||||
#endif
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local variables. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Wakes up the waiting thread.
|
||||
*
|
||||
* @param[in] i2cp pointer to the @p I2CDriver object
|
||||
* @param[in] msg wakeup message
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
#define wakeup_isr(i2cp, msg) { \
|
||||
chSysLockFromIsr(); \
|
||||
if ((i2cp)->thread != NULL) { \
|
||||
Thread *tp = (i2cp)->thread; \
|
||||
(i2cp)->thread = NULL; \
|
||||
tp->p_u.rdymsg = (msg); \
|
||||
chSchReadyI(tp); \
|
||||
} \
|
||||
chSysUnlockFromIsr(); \
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Aborts an I2C transaction.
|
||||
*
|
||||
* @param[in] i2cp pointer to the @p I2CDriver object
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
static void i2c_lld_abort_operation(I2CDriver *i2cp) {
|
||||
I2C_TypeDef *dp = i2cp->i2c;
|
||||
|
||||
if (dp->CR1 & I2C_CR1_PE) {
|
||||
/* Stops the I2C peripheral.*/
|
||||
dp->CR1 &= ~I2C_CR1_PE;
|
||||
while (dp->CR1 & I2C_CR1_PE)
|
||||
dp->CR1 &= ~I2C_CR1_PE;
|
||||
dp->CR1 |= I2C_CR1_PE;
|
||||
}
|
||||
|
||||
/* Stops the associated DMA streams.*/
|
||||
dmaStreamDisable(i2cp->dmatx);
|
||||
dmaStreamDisable(i2cp->dmarx);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Handling of stalled I2C transactions.
|
||||
*
|
||||
* @param[in] i2cp pointer to the @p I2CDriver object
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
static void i2c_lld_safety_timeout(void *p) {
|
||||
I2CDriver *i2cp = (I2CDriver *)p;
|
||||
|
||||
chSysLockFromIsr();
|
||||
if (i2cp->thread) {
|
||||
Thread *tp = i2cp->thread;
|
||||
i2c_lld_abort_operation(i2cp);
|
||||
i2cp->thread = NULL;
|
||||
tp->p_u.rdymsg = RDY_TIMEOUT;
|
||||
chSchReadyI(tp);
|
||||
}
|
||||
chSysUnlockFromIsr();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief I2C shared ISR code.
|
||||
*
|
||||
* @param[in] i2cp pointer to the @p I2CDriver object
|
||||
* @param[in] isr content of the ISR register to be decoded
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
static void i2c_lld_serve_interrupt(I2CDriver *i2cp, uint32_t isr) {
|
||||
I2C_TypeDef *dp = i2cp->i2c;
|
||||
|
||||
if ((isr & I2C_ISR_TC) && (i2cp->state == I2C_ACTIVE_TX)) {
|
||||
size_t rxbytes;
|
||||
|
||||
/* Make sure no more 'Transfer complete' interrupts.*/
|
||||
dp->CR1 &= ~I2C_CR1_TCIE;
|
||||
|
||||
rxbytes = dmaStreamGetTransactionSize(i2cp->dmarx);
|
||||
if (rxbytes > 0) {
|
||||
i2cp->state = I2C_ACTIVE_RX;
|
||||
|
||||
/* Enable RX DMA */
|
||||
dmaStreamEnable(i2cp->dmarx);
|
||||
|
||||
dp->CR2 &= ~I2C_CR2_NBYTES;
|
||||
dp->CR2 |= rxbytes << 16;
|
||||
|
||||
/* Starts the read operation.*/
|
||||
dp->CR2 |= I2C_CR2_RD_WRN;
|
||||
dp->CR2 |= I2C_CR2_START;
|
||||
}
|
||||
else {
|
||||
/* Nothing to receive - send STOP immediately.*/
|
||||
dp->CR2 |= I2C_CR2_STOP;
|
||||
}
|
||||
}
|
||||
if (isr & I2C_ISR_NACKF) {
|
||||
/* Starts a STOP sequence immediately on error.*/
|
||||
dp->CR2 |= I2C_CR2_STOP;
|
||||
|
||||
i2cp->errors |= I2CD_ACK_FAILURE;
|
||||
}
|
||||
if (isr & I2C_ISR_STOPF) {
|
||||
/* Stops the associated DMA streams.*/
|
||||
dmaStreamDisable(i2cp->dmatx);
|
||||
dmaStreamDisable(i2cp->dmarx);
|
||||
|
||||
if (i2cp->errors) {
|
||||
wakeup_isr(i2cp, RDY_RESET);
|
||||
}
|
||||
else {
|
||||
wakeup_isr(i2cp, RDY_OK);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief DMA RX end IRQ handler.
|
||||
*
|
||||
* @param[in] i2cp pointer to the @p I2CDriver object
|
||||
* @param[in] flags pre-shifted content of the ISR register
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
static void i2c_lld_serve_rx_end_irq(I2CDriver *i2cp, uint32_t flags) {
|
||||
I2C_TypeDef *dp = i2cp->i2c;
|
||||
|
||||
/* DMA errors handling.*/
|
||||
#if defined(STM32_I2C_DMA_ERROR_HOOK)
|
||||
if ((flags & (STM32_DMA_ISR_TEIF | STM32_DMA_ISR_DMEIF)) != 0) {
|
||||
STM32_I2C_DMA_ERROR_HOOK(i2cp);
|
||||
}
|
||||
#else
|
||||
(void)flags;
|
||||
#endif
|
||||
|
||||
dmaStreamDisable(i2cp->dmarx);
|
||||
dp->CR2 |= I2C_CR2_STOP;
|
||||
wakeup_isr(i2cp, RDY_OK);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief DMA TX end IRQ handler.
|
||||
*
|
||||
* @param[in] i2cp pointer to the @p I2CDriver object
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
static void i2c_lld_serve_tx_end_irq(I2CDriver *i2cp, uint32_t flags) {
|
||||
|
||||
/* DMA errors handling.*/
|
||||
#if defined(STM32_I2C_DMA_ERROR_HOOK)
|
||||
if ((flags & (STM32_DMA_ISR_TEIF | STM32_DMA_ISR_DMEIF)) != 0) {
|
||||
STM32_I2C_DMA_ERROR_HOOK(i2cp);
|
||||
}
|
||||
#else
|
||||
(void)flags;
|
||||
#endif
|
||||
|
||||
dmaStreamDisable(i2cp->dmatx);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief I2C error handler.
|
||||
*
|
||||
* @param[in] i2cp pointer to the @p I2CDriver object
|
||||
* @param[in] isr content of the ISR register to be decoded
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
static void i2c_lld_serve_error_interrupt(I2CDriver *i2cp, uint32_t isr) {
|
||||
|
||||
/* Clears interrupt flags just to be safe.*/
|
||||
dmaStreamDisable(i2cp->dmatx);
|
||||
dmaStreamDisable(i2cp->dmarx);
|
||||
|
||||
if (isr & I2C_ISR_BERR)
|
||||
i2cp->errors |= I2CD_BUS_ERROR;
|
||||
|
||||
if (isr & I2C_ISR_ARLO)
|
||||
i2cp->errors |= I2CD_ARBITRATION_LOST;
|
||||
|
||||
if (isr & I2C_ISR_OVR)
|
||||
i2cp->errors |= I2CD_OVERRUN;
|
||||
|
||||
if (isr & I2C_ISR_TIMEOUT)
|
||||
i2cp->errors |= I2CD_TIMEOUT;
|
||||
|
||||
/* If some error has been identified then sends wakes the waiting thread.*/
|
||||
if (i2cp->errors != I2CD_NO_ERROR)
|
||||
wakeup_isr(i2cp, RDY_RESET);
|
||||
}
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver interrupt handlers. */
|
||||
/*===========================================================================*/
|
||||
|
||||
#if STM32_I2C_USE_I2C1 || defined(__DOXYGEN__)
|
||||
#if defined(STM32_I2C1_GLOBAL_HANDLER) || defined(__DOXYGEN__)
|
||||
/**
|
||||
* @brief I2C1 event interrupt handler.
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
CH_IRQ_HANDLER(STM32_I2C1_GLOBAL_HANDLER) {
|
||||
uint32_t isr = I2CD1.i2c->ISR;
|
||||
|
||||
CH_IRQ_PROLOGUE();
|
||||
|
||||
/* Clearing IRQ bits.*/
|
||||
I2CD1.i2c->ICR = isr;
|
||||
|
||||
if (isr & I2C_ERROR_MASK)
|
||||
i2c_lld_serve_error_interrupt(&I2CD1, isr);
|
||||
else if (isr & I2C_INT_MASK)
|
||||
i2c_lld_serve_interrupt(&I2CD1, isr);
|
||||
|
||||
CH_IRQ_EPILOGUE();
|
||||
}
|
||||
|
||||
#elif defined(STM32_I2C1_EVENT_HANDLER) && defined(STM32_I2C1_ERROR_HANDLER)
|
||||
CH_IRQ_HANDLER(STM32_I2C1_EVENT_HANDLER) {
|
||||
uint32_t isr = I2CD1.i2c->ISR;
|
||||
|
||||
CH_IRQ_PROLOGUE();
|
||||
|
||||
/* Clearing IRQ bits.*/
|
||||
I2CD1.i2c->ICR = isr & I2C_INT_MASK;
|
||||
|
||||
i2c_lld_serve_interrupt(&I2CD1, isr);
|
||||
|
||||
CH_IRQ_EPILOGUE();
|
||||
}
|
||||
|
||||
CH_IRQ_HANDLER(STM32_I2C1_ERROR_HANDLER) {
|
||||
uint32_t isr = I2CD1.i2c->ISR;
|
||||
|
||||
CH_IRQ_PROLOGUE();
|
||||
|
||||
/* Clearing IRQ bits.*/
|
||||
I2CD1.i2c->ICR = isr & I2C_ERROR_MASK;
|
||||
|
||||
i2c_lld_serve_error_interrupt(&I2CD1, isr);
|
||||
|
||||
CH_IRQ_EPILOGUE();
|
||||
}
|
||||
|
||||
#else
|
||||
#error "I2C1 interrupt handlers not defined"
|
||||
#endif
|
||||
#endif /* STM32_I2C_USE_I2C1 */
|
||||
|
||||
#if STM32_I2C_USE_I2C2 || defined(__DOXYGEN__)
|
||||
#if defined(STM32_I2C2_GLOBAL_HANDLER) || defined(__DOXYGEN__)
|
||||
/**
|
||||
* @brief I2C2 event interrupt handler.
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
CH_IRQ_HANDLER(STM32_I2C2_GLOBAL_HANDLER) {
|
||||
uint32_t isr = I2CD2.i2c->ISR;
|
||||
|
||||
CH_IRQ_PROLOGUE();
|
||||
|
||||
/* Clearing IRQ bits.*/
|
||||
I2CD2.i2c->ICR = isr;
|
||||
|
||||
if (isr & I2C_ERROR_MASK)
|
||||
i2c_lld_serve_error_interrupt(&I2CD2, isr);
|
||||
else if (isr & I2C_INT_MASK)
|
||||
i2c_lld_serve_interrupt(&I2CD2, isr);
|
||||
|
||||
CH_IRQ_EPILOGUE();
|
||||
}
|
||||
|
||||
#elif defined(STM32_I2C2_EVENT_HANDLER) && defined(STM32_I2C2_ERROR_HANDLER)
|
||||
CH_IRQ_HANDLER(STM32_I2C2_EVENT_HANDLER) {
|
||||
uint32_t isr = I2CD2.i2c->ISR;
|
||||
|
||||
CH_IRQ_PROLOGUE();
|
||||
|
||||
/* Clearing IRQ bits.*/
|
||||
I2CD2.i2c->ICR = isr & I2C_INT_MASK;
|
||||
|
||||
i2c_lld_serve_interrupt(&I2CD2, isr);
|
||||
|
||||
CH_IRQ_EPILOGUE();
|
||||
}
|
||||
|
||||
CH_IRQ_HANDLER(STM32_I2C2_ERROR_HANDLER) {
|
||||
uint32_t isr = I2CD2.i2c->ISR;
|
||||
|
||||
CH_IRQ_PROLOGUE();
|
||||
|
||||
/* Clearing IRQ bits.*/
|
||||
I2CD2.i2c->ICR = isr & I2C_ERROR_MASK;
|
||||
|
||||
i2c_lld_serve_error_interrupt(&I2CD2, isr);
|
||||
|
||||
CH_IRQ_EPILOGUE();
|
||||
}
|
||||
|
||||
#else
|
||||
#error "I2C2 interrupt handlers not defined"
|
||||
#endif
|
||||
#endif /* STM32_I2C_USE_I2C2 */
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver exported functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Low level I2C driver initialization.
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void i2c_lld_init(void) {
|
||||
|
||||
#if STM32_I2C_USE_I2C1
|
||||
i2cObjectInit(&I2CD1);
|
||||
I2CD1.thread = NULL;
|
||||
I2CD1.i2c = I2C1;
|
||||
I2CD1.dmarx = STM32_DMA_STREAM(STM32_I2C_I2C1_RX_DMA_STREAM);
|
||||
I2CD1.dmatx = STM32_DMA_STREAM(STM32_I2C_I2C1_TX_DMA_STREAM);
|
||||
#endif /* STM32_I2C_USE_I2C1 */
|
||||
|
||||
#if STM32_I2C_USE_I2C2
|
||||
i2cObjectInit(&I2CD2);
|
||||
I2CD2.thread = NULL;
|
||||
I2CD2.i2c = I2C2;
|
||||
I2CD2.dmarx = STM32_DMA_STREAM(STM32_I2C_I2C2_RX_DMA_STREAM);
|
||||
I2CD2.dmatx = STM32_DMA_STREAM(STM32_I2C_I2C2_TX_DMA_STREAM);
|
||||
#endif /* STM32_I2C_USE_I2C2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Configures and activates the I2C peripheral.
|
||||
*
|
||||
* @param[in] i2cp pointer to the @p I2CDriver object
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void i2c_lld_start(I2CDriver *i2cp) {
|
||||
I2C_TypeDef *dp = i2cp->i2c;
|
||||
|
||||
i2cp->txdmamode = DMAMODE_COMMON | STM32_DMA_CR_DIR_M2P;
|
||||
i2cp->rxdmamode = DMAMODE_COMMON | STM32_DMA_CR_DIR_P2M;
|
||||
|
||||
/* Make sure I2C peripheral is disabled */
|
||||
dp->CR1 &= ~I2C_CR1_PE;
|
||||
|
||||
/* If in stopped state then enables the I2C and DMA clocks.*/
|
||||
if (i2cp->state == I2C_STOP) {
|
||||
|
||||
#if STM32_I2C_USE_I2C1
|
||||
if (&I2CD1 == i2cp) {
|
||||
bool_t b;
|
||||
|
||||
rccResetI2C1();
|
||||
b = dmaStreamAllocate(i2cp->dmarx,
|
||||
STM32_I2C_I2C1_IRQ_PRIORITY,
|
||||
(stm32_dmaisr_t)i2c_lld_serve_rx_end_irq,
|
||||
(void *)i2cp);
|
||||
chDbgAssert(!b, "i2c_lld_start(), #1", "stream already allocated");
|
||||
b = dmaStreamAllocate(i2cp->dmatx,
|
||||
STM32_I2C_I2C1_IRQ_PRIORITY,
|
||||
(stm32_dmaisr_t)i2c_lld_serve_tx_end_irq,
|
||||
(void *)i2cp);
|
||||
chDbgAssert(!b, "i2c_lld_start(), #2", "stream already allocated");
|
||||
rccEnableI2C1(FALSE);
|
||||
|
||||
#if defined(STM32_I2C1_GLOBAL_NUMBER) || defined(__DOXYGEN__)
|
||||
nvicEnableVector(STM32_I2C1_GLOBAL_NUMBER,
|
||||
CORTEX_PRIORITY_MASK(STM32_I2C_I2C1_IRQ_PRIORITY));
|
||||
#elif defined(STM32_I2C1_EVENT_NUMBER) && defined(STM32_I2C1_ERROR_NUMBER)
|
||||
nvicEnableVector(STM32_I2C1_EVENT_NUMBER,
|
||||
CORTEX_PRIORITY_MASK(STM32_I2C_I2C1_IRQ_PRIORITY));
|
||||
nvicEnableVector(STM32_I2C1_ERROR_NUMBER,
|
||||
CORTEX_PRIORITY_MASK(STM32_I2C_I2C1_IRQ_PRIORITY));
|
||||
#else
|
||||
#error "I2C1 interrupt numbers not defined"
|
||||
#endif
|
||||
|
||||
i2cp->rxdmamode |= STM32_DMA_CR_CHSEL(I2C1_RX_DMA_CHANNEL) |
|
||||
STM32_DMA_CR_PL(STM32_I2C_I2C1_DMA_PRIORITY);
|
||||
i2cp->txdmamode |= STM32_DMA_CR_CHSEL(I2C1_TX_DMA_CHANNEL) |
|
||||
STM32_DMA_CR_PL(STM32_I2C_I2C1_DMA_PRIORITY);
|
||||
}
|
||||
#endif /* STM32_I2C_USE_I2C1 */
|
||||
|
||||
#if STM32_I2C_USE_I2C2
|
||||
if (&I2CD2 == i2cp) {
|
||||
bool_t b;
|
||||
|
||||
rccResetI2C2();
|
||||
b = dmaStreamAllocate(i2cp->dmarx,
|
||||
STM32_I2C_I2C2_IRQ_PRIORITY,
|
||||
(stm32_dmaisr_t)i2c_lld_serve_rx_end_irq,
|
||||
(void *)i2cp);
|
||||
chDbgAssert(!b, "i2c_lld_start(), #3", "stream already allocated");
|
||||
b = dmaStreamAllocate(i2cp->dmatx,
|
||||
STM32_I2C_I2C2_IRQ_PRIORITY,
|
||||
(stm32_dmaisr_t)i2c_lld_serve_tx_end_irq,
|
||||
(void *)i2cp);
|
||||
chDbgAssert(!b, "i2c_lld_start(), #4", "stream already allocated");
|
||||
rccEnableI2C2(FALSE);
|
||||
|
||||
#if defined(STM32_I2C2_GLOBAL_NUMBER) || defined(__DOXYGEN__)
|
||||
nvicEnableVector(STM32_I2C2_GLOBAL_NUMBER,
|
||||
CORTEX_PRIORITY_MASK(STM32_I2C_I2C2_IRQ_PRIORITY));
|
||||
#elif defined(STM32_I2C2_EVENT_NUMBER) && defined(STM32_I2C2_ERROR_NUMBER)
|
||||
nvicEnableVector(STM32_I2C2_EVENT_NUMBER,
|
||||
CORTEX_PRIORITY_MASK(STM32_I2C_I2C2_IRQ_PRIORITY));
|
||||
nvicEnableVector(STM32_I2C2_ERROR_NUMBER,
|
||||
CORTEX_PRIORITY_MASK(STM32_I2C_I2C2_IRQ_PRIORITY));
|
||||
#else
|
||||
#error "I2C2 interrupt numbers not defined"
|
||||
#endif
|
||||
|
||||
i2cp->rxdmamode |= STM32_DMA_CR_CHSEL(I2C2_RX_DMA_CHANNEL) |
|
||||
STM32_DMA_CR_PL(STM32_I2C_I2C2_DMA_PRIORITY);
|
||||
i2cp->txdmamode |= STM32_DMA_CR_CHSEL(I2C2_TX_DMA_CHANNEL) |
|
||||
STM32_DMA_CR_PL(STM32_I2C_I2C2_DMA_PRIORITY);
|
||||
}
|
||||
#endif /* STM32_I2C_USE_I2C2 */
|
||||
}
|
||||
|
||||
/* I2C registers pointed by the DMA.*/
|
||||
dmaStreamSetPeripheral(i2cp->dmarx, &dp->RXDR);
|
||||
dmaStreamSetPeripheral(i2cp->dmatx, &dp->TXDR);
|
||||
|
||||
/* Reset i2c peripheral, the TCIE bit will be handled separately.*/
|
||||
dp->CR1 = i2cp->config->cr1 | I2C_CR1_ERRIE | I2C_CR1_STOPIE |
|
||||
I2C_CR1_NACKIE | I2C_CR1_TXDMAEN | I2C_CR1_RXDMAEN;
|
||||
|
||||
/* Set slave address field (master mode) */
|
||||
dp->CR2 = (i2cp->config->cr2 & ~I2C_CR2_SADD);
|
||||
|
||||
/* Setup I2C parameters.*/
|
||||
dp->TIMINGR = i2cp->config->timingr;
|
||||
|
||||
/* Ready to go.*/
|
||||
dp->CR1 |= I2C_CR1_PE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Deactivates the I2C peripheral.
|
||||
*
|
||||
* @param[in] i2cp pointer to the @p I2CDriver object
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void i2c_lld_stop(I2CDriver *i2cp) {
|
||||
|
||||
/* If not in stopped state then disables the I2C clock.*/
|
||||
if (i2cp->state != I2C_STOP) {
|
||||
|
||||
/* I2C disable.*/
|
||||
i2c_lld_abort_operation(i2cp);
|
||||
dmaStreamRelease(i2cp->dmatx);
|
||||
dmaStreamRelease(i2cp->dmarx);
|
||||
|
||||
#if STM32_I2C_USE_I2C1
|
||||
if (&I2CD1 == i2cp) {
|
||||
#if defined(STM32_I2C1_GLOBAL_NUMBER) || defined(__DOXYGEN__)
|
||||
nvicDisableVector(STM32_I2C1_GLOBAL_NUMBER);
|
||||
#elif defined(STM32_I2C1_EVENT_NUMBER) && defined(STM32_I2C1_ERROR_NUMBER)
|
||||
nvicDisableVector(STM32_I2C1_EVENT_NUMBER);
|
||||
nvicDisableVector(STM32_I2C1_ERROR_NUMBER);
|
||||
#else
|
||||
#error "I2C1 interrupt numbers not defined"
|
||||
#endif
|
||||
|
||||
rccDisableI2C1(FALSE);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if STM32_I2C_USE_I2C2
|
||||
if (&I2CD2 == i2cp) {
|
||||
#if defined(STM32_I2C2_GLOBAL_NUMBER) || defined(__DOXYGEN__)
|
||||
nvicDisableVector(STM32_I2C2_GLOBAL_NUMBER);
|
||||
#elif defined(STM32_I2C2_EVENT_NUMBER) && defined(STM32_I2C2_ERROR_NUMBER)
|
||||
nvicDisableVector(STM32_I2C2_EVENT_NUMBER);
|
||||
nvicDisableVector(STM32_I2C2_ERROR_NUMBER);
|
||||
#else
|
||||
#error "I2C2 interrupt numbers not defined"
|
||||
#endif
|
||||
|
||||
rccDisableI2C2(FALSE);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Receives data via the I2C bus as master.
|
||||
* @details Number of receiving bytes must be more than 1 on STM32F1x. This is
|
||||
* hardware restriction.
|
||||
*
|
||||
* @param[in] i2cp pointer to the @p I2CDriver object
|
||||
* @param[in] addr slave device address
|
||||
* @param[out] rxbuf pointer to the receive buffer
|
||||
* @param[in] rxbytes number of bytes to be received
|
||||
* @param[in] timeout the number of ticks before the operation timeouts,
|
||||
* the following special values are allowed:
|
||||
* - @a TIME_INFINITE no timeout.
|
||||
* .
|
||||
* @return The operation status.
|
||||
* @retval RDY_OK if the function succeeded.
|
||||
* @retval RDY_RESET if one or more I2C errors occurred, the errors can
|
||||
* be retrieved using @p i2cGetErrors().
|
||||
* @retval RDY_TIMEOUT if a timeout occurred before operation end. <b>After a
|
||||
* timeout the driver must be stopped and restarted
|
||||
* because the bus is in an uncertain state</b>.
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
msg_t i2c_lld_master_receive_timeout(I2CDriver *i2cp, i2caddr_t addr,
|
||||
uint8_t *rxbuf, size_t rxbytes,
|
||||
systime_t timeout) {
|
||||
I2C_TypeDef *dp = i2cp->i2c;
|
||||
VirtualTimer vt;
|
||||
uint32_t addr_cr2 = addr & I2C_CR2_SADD;
|
||||
|
||||
chDbgCheck((rxbytes > 0), "i2c_lld_master_receive_timeout");
|
||||
|
||||
/* Resetting error flags for this transfer.*/
|
||||
i2cp->errors = I2CD_NO_ERROR;
|
||||
|
||||
/* Global timeout for the whole operation.*/
|
||||
if (timeout != TIME_INFINITE)
|
||||
chVTSetI(&vt, timeout, i2c_lld_safety_timeout, (void *)i2cp);
|
||||
|
||||
/* Releases the lock from high level driver.*/
|
||||
chSysUnlock();
|
||||
|
||||
/* Waits until BUSY flag is reset and the STOP from the previous operation
|
||||
is completed, alternatively for a timeout condition.*/
|
||||
while (dp->ISR & I2C_ISR_BUSY) {
|
||||
chSysLock();
|
||||
if ((timeout != TIME_INFINITE) && !chVTIsArmedI(&vt))
|
||||
return RDY_TIMEOUT;
|
||||
chSysUnlock();
|
||||
}
|
||||
|
||||
/* This lock will be released in high level driver.*/
|
||||
chSysLock();
|
||||
|
||||
/* Adjust slave address (master mode) for 7-bit address mode */
|
||||
if ((i2cp->config->cr2 & I2C_CR2_ADD10) == 0)
|
||||
addr_cr2 = (addr_cr2 & 0x7f) << 1;
|
||||
|
||||
/* Set slave address field (master mode) */
|
||||
dp->CR2 &= ~(I2C_CR2_SADD | I2C_CR2_NBYTES);
|
||||
dp->CR2 |= (rxbytes << 16) | addr_cr2;
|
||||
|
||||
/* Initializes driver fields */
|
||||
i2cp->errors = 0;
|
||||
|
||||
/* RX DMA setup.*/
|
||||
dmaStreamSetMode(i2cp->dmarx, i2cp->rxdmamode);
|
||||
dmaStreamSetMemory0(i2cp->dmarx, rxbuf);
|
||||
dmaStreamSetTransactionSize(i2cp->dmarx, rxbytes);
|
||||
|
||||
/* Enable RX DMA */
|
||||
dmaStreamEnable(i2cp->dmarx);
|
||||
|
||||
/* Atomic check on the timer in order to make sure that a timeout didn't
|
||||
happen outside the critical zone.*/
|
||||
if ((timeout != TIME_INFINITE) && !chVTIsArmedI(&vt))
|
||||
return RDY_TIMEOUT;
|
||||
|
||||
/* Starts the operation.*/
|
||||
dp->CR2 |= I2C_CR2_RD_WRN;
|
||||
dp->CR2 |= I2C_CR2_START;
|
||||
|
||||
/* Waits for the operation completion or a timeout.*/
|
||||
i2cp->thread = chThdSelf();
|
||||
chSchGoSleepS(THD_STATE_SUSPENDED);
|
||||
if ((timeout != TIME_INFINITE) && chVTIsArmedI(&vt))
|
||||
chVTResetI(&vt);
|
||||
|
||||
return chThdSelf()->p_u.rdymsg;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Transmits data via the I2C bus as master.
|
||||
* @details Number of receiving bytes must be 0 or more than 1 on STM32F1x.
|
||||
* This is hardware restriction.
|
||||
*
|
||||
* @param[in] i2cp pointer to the @p I2CDriver object
|
||||
* @param[in] addr slave device address
|
||||
* @param[in] txbuf pointer to the transmit buffer
|
||||
* @param[in] txbytes number of bytes to be transmitted
|
||||
* @param[out] rxbuf pointer to the receive buffer
|
||||
* @param[in] rxbytes number of bytes to be received
|
||||
* @param[in] timeout the number of ticks before the operation timeouts,
|
||||
* the following special values are allowed:
|
||||
* - @a TIME_INFINITE no timeout.
|
||||
* .
|
||||
* @return The operation status.
|
||||
* @retval RDY_OK if the function succeeded.
|
||||
* @retval RDY_RESET if one or more I2C errors occurred, the errors can
|
||||
* be retrieved using @p i2cGetErrors().
|
||||
* @retval RDY_TIMEOUT if a timeout occurred before operation end. <b>After a
|
||||
* timeout the driver must be stopped and restarted
|
||||
* because the bus is in an uncertain state</b>.
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
msg_t i2c_lld_master_transmit_timeout(I2CDriver *i2cp, i2caddr_t addr,
|
||||
const uint8_t *txbuf, size_t txbytes,
|
||||
uint8_t *rxbuf, size_t rxbytes,
|
||||
systime_t timeout) {
|
||||
I2C_TypeDef *dp = i2cp->i2c;
|
||||
VirtualTimer vt;
|
||||
uint32_t addr_cr2 = addr & I2C_CR2_SADD;
|
||||
|
||||
chDbgCheck(((rxbytes == 0) || ((rxbytes > 0) && (rxbuf != NULL))),
|
||||
"i2c_lld_master_transmit_timeout");
|
||||
|
||||
/* Resetting error flags for this transfer.*/
|
||||
i2cp->errors = I2CD_NO_ERROR;
|
||||
|
||||
/* Global timeout for the whole operation.*/
|
||||
if (timeout != TIME_INFINITE)
|
||||
chVTSetI(&vt, timeout, i2c_lld_safety_timeout, (void *)i2cp);
|
||||
|
||||
/* Releases the lock from high level driver.*/
|
||||
chSysUnlock();
|
||||
|
||||
/* Waits until BUSY flag is reset and the STOP from the previous operation
|
||||
is completed, alternatively for a timeout condition.*/
|
||||
while (dp->ISR & I2C_ISR_BUSY) {
|
||||
chSysLock();
|
||||
if ((timeout != TIME_INFINITE) && !chVTIsArmedI(&vt))
|
||||
return RDY_TIMEOUT;
|
||||
chSysUnlock();
|
||||
}
|
||||
|
||||
/* This lock will be released in high level driver.*/
|
||||
chSysLock();
|
||||
|
||||
/* Adjust slave address (master mode) for 7-bit address mode */
|
||||
if ((i2cp->config->cr2 & I2C_CR2_ADD10) == 0)
|
||||
addr_cr2 = (addr_cr2 & 0x7f) << 1;
|
||||
|
||||
/* Set slave address field (master mode) */
|
||||
dp->CR2 &= ~(I2C_CR2_SADD | I2C_CR2_NBYTES);
|
||||
dp->CR2 |= (txbytes << 16) | addr_cr2;
|
||||
|
||||
/* Initializes driver fields */
|
||||
i2cp->errors = 0;
|
||||
|
||||
/* TX DMA setup.*/
|
||||
dmaStreamSetMode(i2cp->dmatx, i2cp->txdmamode);
|
||||
dmaStreamSetMemory0(i2cp->dmatx, txbuf);
|
||||
dmaStreamSetTransactionSize(i2cp->dmatx, txbytes);
|
||||
|
||||
/* RX DMA setup.*/
|
||||
dmaStreamSetMode(i2cp->dmarx, i2cp->rxdmamode);
|
||||
dmaStreamSetMemory0(i2cp->dmarx, rxbuf);
|
||||
dmaStreamSetTransactionSize(i2cp->dmarx, rxbytes);
|
||||
|
||||
/* Enable TX DMA */
|
||||
dmaStreamEnable(i2cp->dmatx);
|
||||
|
||||
/* Atomic check on the timer in order to make sure that a timeout didn't
|
||||
happen outside the critical zone.*/
|
||||
if ((timeout != TIME_INFINITE) && !chVTIsArmedI(&vt))
|
||||
return RDY_TIMEOUT;
|
||||
|
||||
/* Transmission complete interrupt enabled.*/
|
||||
dp->CR1 |= I2C_CR1_TCIE;
|
||||
|
||||
/* Starts the operation as the very last thing.*/
|
||||
dp->CR2 &= ~I2C_CR2_RD_WRN;
|
||||
dp->CR2 |= I2C_CR2_START;
|
||||
|
||||
/* Waits for the operation completion or a timeout.*/
|
||||
i2cp->thread = chThdSelf();
|
||||
chSchGoSleepS(THD_STATE_SUSPENDED);
|
||||
if ((timeout != TIME_INFINITE) && chVTIsArmedI(&vt))
|
||||
chVTResetI(&vt);
|
||||
|
||||
return chThdSelf()->p_u.rdymsg;
|
||||
}
|
||||
|
||||
#endif /* HAL_USE_I2C */
|
||||
|
||||
/** @} */
|
||||
+338
@@ -0,0 +1,338 @@
|
||||
/*
|
||||
ChibiOS/RT - Copyright (C) 2006-2013 Giovanni Di Sirio
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
/*
|
||||
Concepts and parts of this file have been contributed by Uladzimir Pylinsky
|
||||
aka barthess.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file STM32/I2Cv2/i2c_lld.h
|
||||
* @brief STM32 I2C subsystem low level driver header.
|
||||
*
|
||||
* @addtogroup I2C
|
||||
* @{
|
||||
*/
|
||||
|
||||
#ifndef _I2C_LLD_H_
|
||||
#define _I2C_LLD_H_
|
||||
|
||||
#if HAL_USE_I2C || defined(__DOXYGEN__)
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver constants. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @name TIMINGR register definitions
|
||||
* @{
|
||||
*/
|
||||
#define STM32_TIMINGR_PRESC_MASK (15U << 28)
|
||||
#define STM32_TIMINGR_PRESC(n) ((n) << 28)
|
||||
#define STM32_TIMINGR_SCLDEL_MASK (15U << 20)
|
||||
#define STM32_TIMINGR_SCLDEL(n) ((n) << 20)
|
||||
#define STM32_TIMINGR_SDADEL_MASK (15U << 16)
|
||||
#define STM32_TIMINGR_SDADEL(n) ((n) << 16)
|
||||
#define STM32_TIMINGR_SCLH_MASK (255U << 8)
|
||||
#define STM32_TIMINGR_SCLH(n) ((n) << 8)
|
||||
#define STM32_TIMINGR_SCLL_MASK (255U << 0)
|
||||
#define STM32_TIMINGR_SCLL(n) ((n) << 0)
|
||||
/** @} */
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver pre-compile time settings. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @name Configuration options
|
||||
* @{
|
||||
*/
|
||||
/**
|
||||
* @brief I2C1 driver enable switch.
|
||||
* @details If set to @p TRUE the support for I2C1 is included.
|
||||
* @note The default is @p FALSE.
|
||||
*/
|
||||
#if !defined(STM32_I2C_USE_I2C1) || defined(__DOXYGEN__)
|
||||
#define STM32_I2C_USE_I2C1 FALSE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief I2C2 driver enable switch.
|
||||
* @details If set to @p TRUE the support for I2C2 is included.
|
||||
* @note The default is @p FALSE.
|
||||
*/
|
||||
#if !defined(STM32_I2C_USE_I2C2) || defined(__DOXYGEN__)
|
||||
#define STM32_I2C_USE_I2C2 FALSE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief I2C1 interrupt priority level setting.
|
||||
*/
|
||||
#if !defined(STM32_I2C_I2C1_IRQ_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define STM32_I2C_I2C1_IRQ_PRIORITY 10
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief I2C2 interrupt priority level setting.
|
||||
*/
|
||||
#if !defined(STM32_I2C_I2C2_IRQ_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define STM32_I2C_I2C2_IRQ_PRIORITY 10
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief I2C1 DMA priority (0..3|lowest..highest).
|
||||
* @note The priority level is used for both the TX and RX DMA streams but
|
||||
* because of the streams ordering the RX stream has always priority
|
||||
* over the TX stream.
|
||||
*/
|
||||
#if !defined(STM32_I2C_I2C1_DMA_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define STM32_I2C_I2C1_DMA_PRIORITY 1
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief I2C2 DMA priority (0..3|lowest..highest).
|
||||
* @note The priority level is used for both the TX and RX DMA streams but
|
||||
* because of the streams ordering the RX stream has always priority
|
||||
* over the TX stream.
|
||||
*/
|
||||
#if !defined(STM32_I2C_I2C2_DMA_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define STM32_I2C_I2C2_DMA_PRIORITY 1
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief I2C DMA error hook.
|
||||
* @note The default action for DMA errors is a system halt because DMA
|
||||
* error can only happen because programming errors.
|
||||
*/
|
||||
#if !defined(STM32_I2C_DMA_ERROR_HOOK) || defined(__DOXYGEN__)
|
||||
#define STM32_I2C_DMA_ERROR_HOOK(i2cp) chSysHalt()
|
||||
#endif
|
||||
/** @} */
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Derived constants and error checks. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/* Streams for the DMA peripheral.*/
|
||||
#if defined(STM32F0XX)
|
||||
#define STM32_I2C_I2C1_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 3)
|
||||
#define STM32_I2C_I2C1_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 2)
|
||||
#define STM32_I2C_I2C2_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 5)
|
||||
#define STM32_I2C_I2C2_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 4)
|
||||
|
||||
#elif defined(STM32F30X) || defined(STM32F37X)
|
||||
#define STM32_I2C_I2C1_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 7)
|
||||
#define STM32_I2C_I2C1_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 6)
|
||||
#define STM32_I2C_I2C2_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 5)
|
||||
#define STM32_I2C_I2C2_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 4)
|
||||
|
||||
#else
|
||||
#error "device unsupported by I2Cv2 driver"
|
||||
#endif
|
||||
|
||||
/** @brief error checks */
|
||||
#if STM32_I2C_USE_I2C1 && !STM32_HAS_I2C1
|
||||
#error "I2C1 not present in the selected device"
|
||||
#endif
|
||||
|
||||
#if STM32_I2C_USE_I2C2 && !STM32_HAS_I2C2
|
||||
#error "I2C2 not present in the selected device"
|
||||
#endif
|
||||
|
||||
#if !STM32_I2C_USE_I2C1 && !STM32_I2C_USE_I2C2
|
||||
#error "I2C driver activated but no I2C peripheral assigned"
|
||||
#endif
|
||||
|
||||
#if STM32_I2C_USE_I2C1 && \
|
||||
!STM32_DMA_IS_VALID_ID(STM32_I2C_I2C1_RX_DMA_STREAM, \
|
||||
STM32_I2C1_RX_DMA_MSK)
|
||||
#error "invalid DMA stream associated to I2C1 RX"
|
||||
#endif
|
||||
|
||||
#if STM32_I2C_USE_I2C1 && \
|
||||
!STM32_DMA_IS_VALID_ID(STM32_I2C_I2C1_TX_DMA_STREAM, \
|
||||
STM32_I2C1_TX_DMA_MSK)
|
||||
#error "invalid DMA stream associated to I2C1 TX"
|
||||
#endif
|
||||
|
||||
#if STM32_I2C_USE_I2C2 && \
|
||||
!STM32_DMA_IS_VALID_ID(STM32_I2C_I2C2_RX_DMA_STREAM, \
|
||||
STM32_I2C2_RX_DMA_MSK)
|
||||
#error "invalid DMA stream associated to I2C2 RX"
|
||||
#endif
|
||||
|
||||
#if STM32_I2C_USE_I2C2 && \
|
||||
!STM32_DMA_IS_VALID_ID(STM32_I2C_I2C2_TX_DMA_STREAM, \
|
||||
STM32_I2C2_TX_DMA_MSK)
|
||||
#error "invalid DMA stream associated to I2C2 TX"
|
||||
#endif
|
||||
|
||||
#if !defined(STM32_DMA_REQUIRED)
|
||||
#define STM32_DMA_REQUIRED
|
||||
#endif
|
||||
|
||||
/* Check clock range. */
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver data structures and types. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Type representing I2C address.
|
||||
*/
|
||||
typedef uint16_t i2caddr_t;
|
||||
|
||||
/**
|
||||
* @brief I2C Driver condition flags type.
|
||||
*/
|
||||
typedef uint32_t i2cflags_t;
|
||||
|
||||
/**
|
||||
* @brief Driver configuration structure.
|
||||
*/
|
||||
typedef struct {
|
||||
/**
|
||||
* @brief TIMINGR register initialization.
|
||||
* @note Refer to the STM32 reference manual, the values are affected
|
||||
* by the system clock settings in mcuconf.h.
|
||||
*/
|
||||
uint32_t timingr;
|
||||
/**
|
||||
* @brief CR1 register initialization.
|
||||
* @note Leave to zero unless you know what you are doing.
|
||||
*/
|
||||
uint32_t cr1;
|
||||
/**
|
||||
* @brief CR2 register initialization.
|
||||
* @note Only the ADD10 bit can eventually be specified here.
|
||||
*/
|
||||
uint32_t cr2;
|
||||
} I2CConfig;
|
||||
|
||||
/**
|
||||
* @brief Type of a structure representing an I2C driver.
|
||||
*/
|
||||
typedef struct I2CDriver I2CDriver;
|
||||
|
||||
/**
|
||||
* @brief Structure representing an I2C driver.
|
||||
*/
|
||||
struct I2CDriver{
|
||||
/**
|
||||
* @brief Driver state.
|
||||
*/
|
||||
i2cstate_t state;
|
||||
/**
|
||||
* @brief Current configuration data.
|
||||
*/
|
||||
const I2CConfig *config;
|
||||
/**
|
||||
* @brief Error flags.
|
||||
*/
|
||||
i2cflags_t errors;
|
||||
#if I2C_USE_MUTUAL_EXCLUSION || defined(__DOXYGEN__)
|
||||
#if CH_USE_MUTEXES || defined(__DOXYGEN__)
|
||||
/**
|
||||
* @brief Mutex protecting the bus.
|
||||
*/
|
||||
Mutex mutex;
|
||||
#elif CH_USE_SEMAPHORES
|
||||
Semaphore semaphore;
|
||||
#endif
|
||||
#endif /* I2C_USE_MUTUAL_EXCLUSION */
|
||||
#if defined(I2C_DRIVER_EXT_FIELDS)
|
||||
I2C_DRIVER_EXT_FIELDS
|
||||
#endif
|
||||
/* End of the mandatory fields.*/
|
||||
/**
|
||||
* @brief Thread waiting for I/O completion.
|
||||
*/
|
||||
Thread *thread;
|
||||
/**
|
||||
* @brief Current slave address without R/W bit.
|
||||
*/
|
||||
i2caddr_t addr;
|
||||
/**
|
||||
* @brief RX DMA mode bit mask.
|
||||
*/
|
||||
uint32_t rxdmamode;
|
||||
/**
|
||||
* @brief TX DMA mode bit mask.
|
||||
*/
|
||||
uint32_t txdmamode;
|
||||
/**
|
||||
* @brief Receive DMA channel.
|
||||
*/
|
||||
const stm32_dma_stream_t *dmarx;
|
||||
/**
|
||||
* @brief Transmit DMA channel.
|
||||
*/
|
||||
const stm32_dma_stream_t *dmatx;
|
||||
/**
|
||||
* @brief Pointer to the I2Cx registers block.
|
||||
*/
|
||||
I2C_TypeDef *i2c;
|
||||
};
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver macros. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Get errors from I2C driver.
|
||||
*
|
||||
* @param[in] i2cp pointer to the @p I2CDriver object
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
#define i2c_lld_get_errors(i2cp) ((i2cp)->errors)
|
||||
|
||||
/*===========================================================================*/
|
||||
/* External declarations. */
|
||||
/*===========================================================================*/
|
||||
|
||||
#if !defined(__DOXYGEN__)
|
||||
#if STM32_I2C_USE_I2C1
|
||||
extern I2CDriver I2CD1;
|
||||
#endif
|
||||
|
||||
#if STM32_I2C_USE_I2C2
|
||||
extern I2CDriver I2CD2;
|
||||
#endif
|
||||
|
||||
#endif /* !defined(__DOXYGEN__) */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
void i2c_lld_init(void);
|
||||
void i2c_lld_start(I2CDriver *i2cp);
|
||||
void i2c_lld_stop(I2CDriver *i2cp);
|
||||
msg_t i2c_lld_master_transmit_timeout(I2CDriver *i2cp, i2caddr_t addr,
|
||||
const uint8_t *txbuf, size_t txbytes,
|
||||
uint8_t *rxbuf, size_t rxbytes,
|
||||
systime_t timeout);
|
||||
msg_t i2c_lld_master_receive_timeout(I2CDriver *i2cp, i2caddr_t addr,
|
||||
uint8_t *rxbuf, size_t rxbytes,
|
||||
systime_t timeout);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* HAL_USE_I2C */
|
||||
|
||||
#endif /* _I2C_LLD_H_ */
|
||||
|
||||
/** @} */
|
||||
+916
@@ -0,0 +1,916 @@
|
||||
/*
|
||||
ChibiOS/RT - Copyright (C) 2006-2013 Giovanni Di Sirio
|
||||
|
||||
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 stm32_otg.h
|
||||
* @brief STM32 OTG registers layout header.
|
||||
*
|
||||
* @addtogroup USB
|
||||
* @{
|
||||
*/
|
||||
|
||||
#ifndef _STM32_OTG_H_
|
||||
#define _STM32_OTG_H_
|
||||
|
||||
/**
|
||||
* @brief Number of the implemented endpoints in OTG_FS.
|
||||
* @details This value does not include the endpoint 0 that is always present.
|
||||
*/
|
||||
#define STM32_OTG1_ENDOPOINTS_NUMBER 3
|
||||
|
||||
/**
|
||||
* @brief Number of the implemented endpoints in OTG_HS.
|
||||
* @details This value does not include the endpoint 0 that is always present.
|
||||
*/
|
||||
#define STM32_OTG2_ENDOPOINTS_NUMBER 5
|
||||
|
||||
/**
|
||||
* @brief OTG_FS FIFO memory size in words.
|
||||
*/
|
||||
#define STM32_OTG1_FIFO_MEM_SIZE 320
|
||||
|
||||
/**
|
||||
* @brief OTG_HS FIFO memory size in words.
|
||||
*/
|
||||
#define STM32_OTG2_FIFO_MEM_SIZE 1024
|
||||
|
||||
/**
|
||||
* @brief Host channel registers group.
|
||||
*/
|
||||
typedef struct {
|
||||
volatile uint32_t HCCHAR; /**< @brief Host channel characteristics
|
||||
register. */
|
||||
volatile uint32_t resvd8;
|
||||
volatile uint32_t HCINT; /**< @brief Host channel interrupt register.*/
|
||||
volatile uint32_t HCINTMSK; /**< @brief Host channel interrupt mask
|
||||
register. */
|
||||
volatile uint32_t HCTSIZ; /**< @brief Host channel transfer size
|
||||
register. */
|
||||
volatile uint32_t resvd14;
|
||||
volatile uint32_t resvd18;
|
||||
volatile uint32_t resvd1c;
|
||||
} stm32_otg_host_chn_t;
|
||||
|
||||
/**
|
||||
* @brief Device input endpoint registers group.
|
||||
*/
|
||||
typedef struct {
|
||||
volatile uint32_t DIEPCTL; /**< @brief Device control IN endpoint
|
||||
control register. */
|
||||
volatile uint32_t resvd4;
|
||||
volatile uint32_t DIEPINT; /**< @brief Device IN endpoint interrupt
|
||||
register. */
|
||||
volatile uint32_t resvdC;
|
||||
volatile uint32_t DIEPTSIZ; /**< @brief Device IN endpoint transfer size
|
||||
register. */
|
||||
volatile uint32_t resvd14;
|
||||
volatile uint32_t DTXFSTS; /**< @brief Device IN endpoint transmit FIFO
|
||||
status register. */
|
||||
volatile uint32_t resvd1C;
|
||||
} stm32_otg_in_ep_t;
|
||||
|
||||
/**
|
||||
* @brief Device output endpoint registers group.
|
||||
*/
|
||||
typedef struct {
|
||||
volatile uint32_t DOEPCTL; /**< @brief Device control OUT endpoint
|
||||
control register. */
|
||||
volatile uint32_t resvd4;
|
||||
volatile uint32_t DOEPINT; /**< @brief Device OUT endpoint interrupt
|
||||
register. */
|
||||
volatile uint32_t resvdC;
|
||||
volatile uint32_t DOEPTSIZ; /**< @brief Device OUT endpoint transfer
|
||||
size register. */
|
||||
volatile uint32_t resvd14;
|
||||
volatile uint32_t resvd18;
|
||||
volatile uint32_t resvd1C;
|
||||
} stm32_otg_out_ep_t;
|
||||
|
||||
/**
|
||||
* @brief USB registers memory map.
|
||||
*/
|
||||
typedef struct {
|
||||
volatile uint32_t GOTGCTL; /**< @brief OTG control and status register.*/
|
||||
volatile uint32_t GOTGINT; /**< @brief OTG interrupt register. */
|
||||
volatile uint32_t GAHBCFG; /**< @brief AHB configuration register. */
|
||||
volatile uint32_t GUSBCFG; /**< @brief USB configuration register. */
|
||||
volatile uint32_t GRSTCTL; /**< @brief Reset register size. */
|
||||
volatile uint32_t GINTSTS; /**< @brief Interrupt register. */
|
||||
volatile uint32_t GINTMSK; /**< @brief Interrupt mask register. */
|
||||
volatile uint32_t GRXSTSR; /**< @brief Receive status debug read
|
||||
register. */
|
||||
volatile uint32_t GRXSTSP; /**< @brief Receive status read/pop
|
||||
register. */
|
||||
volatile uint32_t GRXFSIZ; /**< @brief Receive FIFO size register. */
|
||||
volatile uint32_t DIEPTXF0; /**< @brief Endpoint 0 transmit FIFO size
|
||||
register. */
|
||||
volatile uint32_t HNPTXSTS; /**< @brief Non-periodic transmit FIFO/queue
|
||||
status register. */
|
||||
volatile uint32_t resvd30;
|
||||
volatile uint32_t resvd34;
|
||||
volatile uint32_t GCCFG; /**< @brief General core configuration. */
|
||||
volatile uint32_t CID; /**< @brief Core ID register. */
|
||||
volatile uint32_t resvd58[48];
|
||||
volatile uint32_t HPTXFSIZ; /**< @brief Host periodic transmit FIFO size
|
||||
register. */
|
||||
volatile uint32_t DIEPTXF[15];/**< @brief Device IN endpoint transmit FIFO
|
||||
size registers. */
|
||||
volatile uint32_t resvd140[176];
|
||||
volatile uint32_t HCFG; /**< @brief Host configuration register. */
|
||||
volatile uint32_t HFIR; /**< @brief Host frame interval register. */
|
||||
volatile uint32_t HFNUM; /**< @brief Host frame number/frame time
|
||||
Remaining register. */
|
||||
volatile uint32_t resvd40C;
|
||||
volatile uint32_t HPTXSTS; /**< @brief Host periodic transmit FIFO/queue
|
||||
status register. */
|
||||
volatile uint32_t HAINT; /**< @brief Host all channels interrupt
|
||||
register. */
|
||||
volatile uint32_t HAINTMSK; /**< @brief Host all channels interrupt mask
|
||||
register. */
|
||||
volatile uint32_t resvd41C[9];
|
||||
volatile uint32_t HPRT; /**< @brief Host port control and status
|
||||
register. */
|
||||
volatile uint32_t resvd444[47];
|
||||
stm32_otg_host_chn_t hc[16]; /**< @brief Host channels array. */
|
||||
volatile uint32_t resvd700[64];
|
||||
volatile uint32_t DCFG; /**< @brief Device configuration register. */
|
||||
volatile uint32_t DCTL; /**< @brief Device control register. */
|
||||
volatile uint32_t DSTS; /**< @brief Device status register. */
|
||||
volatile uint32_t resvd80C;
|
||||
volatile uint32_t DIEPMSK; /**< @brief Device IN endpoint common
|
||||
interrupt mask register. */
|
||||
volatile uint32_t DOEPMSK; /**< @brief Device OUT endpoint common
|
||||
interrupt mask register. */
|
||||
volatile uint32_t DAINT; /**< @brief Device all endpoints interrupt
|
||||
register. */
|
||||
volatile uint32_t DAINTMSK; /**< @brief Device all endpoints interrupt
|
||||
mask register. */
|
||||
volatile uint32_t resvd820;
|
||||
volatile uint32_t resvd824;
|
||||
volatile uint32_t DVBUSDIS; /**< @brief Device VBUS discharge time
|
||||
register. */
|
||||
volatile uint32_t DVBUSPULSE; /**< @brief Device VBUS pulsing time
|
||||
register. */
|
||||
volatile uint32_t resvd830;
|
||||
volatile uint32_t DIEPEMPMSK; /**< @brief Device IN endpoint FIFO empty
|
||||
interrupt mask register. */
|
||||
volatile uint32_t resvd838;
|
||||
volatile uint32_t resvd83C;
|
||||
volatile uint32_t resvd840[16];
|
||||
volatile uint32_t resvd880[16];
|
||||
volatile uint32_t resvd8C0[16];
|
||||
stm32_otg_in_ep_t ie[16]; /**< @brief Input endpoints. */
|
||||
stm32_otg_out_ep_t oe[16]; /**< @brief Output endpoints. */
|
||||
volatile uint32_t resvdD00[64];
|
||||
volatile uint32_t PCGCCTL; /**< @brief Power and clock gating control
|
||||
register. */
|
||||
volatile uint32_t resvdE04[127];
|
||||
volatile uint32_t FIFO[16][1024];
|
||||
} stm32_otg_t;
|
||||
|
||||
/**
|
||||
* @name GOTGCTL register bit definitions
|
||||
* @{
|
||||
*/
|
||||
#define GOTGCTL_BSVLD (1U<<19) /**< B-Session Valid. */
|
||||
#define GOTGCTL_ASVLD (1U<<18) /**< A-Session Valid. */
|
||||
#define GOTGCTL_DBCT (1U<<17) /**< Long/Short debounce time. */
|
||||
#define GOTGCTL_CIDSTS (1U<<16) /**< Connector ID status. */
|
||||
#define GOTGCTL_DHNPEN (1U<<11) /**< Device HNP enabled. */
|
||||
#define GOTGCTL_HSHNPEN (1U<<10) /**< Host Set HNP enable. */
|
||||
#define GOTGCTL_HNPRQ (1U<<9) /**< HNP request. */
|
||||
#define GOTGCTL_HNGSCS (1U<<8) /**< Host negotiation success. */
|
||||
#define GOTGCTL_SRQ (1U<<1) /**< Session request. */
|
||||
#define GOTGCTL_SRQSCS (1U<<0) /**< Session request success. */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name GOTGINT register bit definitions
|
||||
* @{
|
||||
*/
|
||||
#define GOTGINT_DBCDNE (1U<<19) /**< Debounce done. */
|
||||
#define GOTGINT_ADTOCHG (1U<<18) /**< A-Device timeout change. */
|
||||
#define GOTGINT_HNGDET (1U<<17) /**< Host negotiation detected. */
|
||||
#define GOTGINT_HNSSCHG (1U<<9) /**< Host negotiation success
|
||||
status change. */
|
||||
#define GOTGINT_SRSSCHG (1U<<8) /**< Session request success
|
||||
status change. */
|
||||
#define GOTGINT_SEDET (1U<<2) /**< Session end detected. */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name GAHBCFG register bit definitions
|
||||
* @{
|
||||
*/
|
||||
#define GAHBCFG_PTXFELVL (1U<<8) /**< Periodic TxFIFO empty
|
||||
level. */
|
||||
#define GAHBCFG_TXFELVL (1U<<7) /**< Non-periodic TxFIFO empty
|
||||
level. */
|
||||
#define GAHBCFG_DMAEN (1U<<5) /**< DMA enable (HS only). */
|
||||
#define GAHBCFG_HBSTLEN_MASK (15U<<1) /**< Burst length/type mask (HS
|
||||
only). */
|
||||
#define GAHBCFG_HBSTLEN(n) ((n)<<1) /**< Burst length/type (HS
|
||||
only). */
|
||||
#define GAHBCFG_GINTMSK (1U<<0) /**< Global interrupt mask. */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name GUSBCFG register bit definitions
|
||||
* @{
|
||||
*/
|
||||
#define GUSBCFG_CTXPKT (1U<<31) /**< Corrupt Tx packet. */
|
||||
#define GUSBCFG_FDMOD (1U<<30) /**< Force Device Mode. */
|
||||
#define GUSBCFG_FHMOD (1U<<29) /**< Force Host Mode. */
|
||||
#define GUSBCFG_TRDT_MASK (15U<<10) /**< USB Turnaround time field
|
||||
mask. */
|
||||
#define GUSBCFG_TRDT(n) ((n)<<10) /**< USB Turnaround time field
|
||||
value. */
|
||||
#define GUSBCFG_HNPCAP (1U<<9) /**< HNP-Capable. */
|
||||
#define GUSBCFG_SRPCAP (1U<<8) /**< SRP-Capable. */
|
||||
#define GUSBCFG_PHYSEL (1U<<6) /**< USB 2.0 High-Speed PHY or
|
||||
USB 1.1 Full-Speed serial
|
||||
transceiver Select. */
|
||||
#define GUSBCFG_TOCAL_MASK (7U<<0) /**< HS/FS timeout calibration
|
||||
field mask. */
|
||||
#define GUSBCFG_TOCAL(n) ((n)<<0) /**< HS/FS timeout calibration
|
||||
field value. */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name GRSTCTL register bit definitions
|
||||
* @{
|
||||
*/
|
||||
#define GRSTCTL_AHBIDL (1U<<31) /**< AHB Master Idle. */
|
||||
#define GRSTCTL_TXFNUM_MASK (31U<<6) /**< TxFIFO number field mask. */
|
||||
#define GRSTCTL_TXFNUM(n) ((n)<<6) /**< TxFIFO number field value. */
|
||||
#define GRSTCTL_TXFFLSH (1U<<5) /**< TxFIFO flush. */
|
||||
#define GRSTCTL_RXFFLSH (1U<<4) /**< RxFIFO flush. */
|
||||
#define GRSTCTL_FCRST (1U<<2) /**< Host frame counter reset. */
|
||||
#define GRSTCTL_HSRST (1U<<1) /**< HClk soft reset. */
|
||||
#define GRSTCTL_CSRST (1U<<0) /**< Core soft reset. */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name GINTSTS register bit definitions
|
||||
* @{
|
||||
*/
|
||||
#define GINTSTS_WKUPINT (1U<<31) /**< Resume/Remote wakeup
|
||||
detected interrupt. */
|
||||
#define GINTSTS_SRQINT (1U<<30) /**< Session request/New session
|
||||
detected interrupt. */
|
||||
#define GINTSTS_DISCINT (1U<<29) /**< Disconnect detected
|
||||
interrupt. */
|
||||
#define GINTSTS_CIDSCHG (1U<<28) /**< Connector ID status change.*/
|
||||
#define GINTSTS_PTXFE (1U<<26) /**< Periodic TxFIFO empty. */
|
||||
#define GINTSTS_HCINT (1U<<25) /**< Host channels interrupt. */
|
||||
#define GINTSTS_HPRTINT (1U<<24) /**< Host port interrupt. */
|
||||
#define GINTSTS_IPXFR (1U<<21) /**< Incomplete periodic
|
||||
transfer. */
|
||||
#define GINTSTS_IISOOXFR (1U<<21) /**< Incomplete isochronous OUT
|
||||
transfer. */
|
||||
#define GINTSTS_IISOIXFR (1U<<20) /**< Incomplete isochronous IN
|
||||
transfer. */
|
||||
#define GINTSTS_OEPINT (1U<<19) /**< OUT endpoints interrupt. */
|
||||
#define GINTSTS_IEPINT (1U<<18) /**< IN endpoints interrupt. */
|
||||
#define GINTSTS_EOPF (1U<<15) /**< End of periodic frame
|
||||
interrupt. */
|
||||
#define GINTSTS_ISOODRP (1U<<14) /**< Isochronous OUT packet
|
||||
dropped interrupt. */
|
||||
#define GINTSTS_ENUMDNE (1U<<13) /**< Enumeration done. */
|
||||
#define GINTSTS_USBRST (1U<<12) /**< USB reset. */
|
||||
#define GINTSTS_USBSUSP (1U<<11) /**< USB suspend. */
|
||||
#define GINTSTS_ESUSP (1U<<10) /**< Early suspend. */
|
||||
#define GINTSTS_GONAKEFF (1U<<7) /**< Global OUT NAK effective. */
|
||||
#define GINTSTS_GINAKEFF (1U<<6) /**< Global IN non-periodic NAK
|
||||
effective. */
|
||||
#define GINTSTS_NPTXFE (1U<<5) /**< Non-periodic TxFIFO empty. */
|
||||
#define GINTSTS_RXFLVL (1U<<4) /**< RxFIFO non-empty. */
|
||||
#define GINTSTS_SOF (1U<<3) /**< Start of frame. */
|
||||
#define GINTSTS_OTGINT (1U<<2) /**< OTG interrupt. */
|
||||
#define GINTSTS_MMIS (1U<<1) /**< Mode Mismatch interrupt. */
|
||||
#define GINTSTS_CMOD (1U<<0) /**< Current mode of operation. */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name GINTMSK register bit definitions
|
||||
* @{
|
||||
*/
|
||||
#define GINTMSK_WKUM (1U<<31) /**< Resume/remote wakeup
|
||||
detected interrupt mask. */
|
||||
#define GINTMSK_SRQM (1U<<30) /**< Session request/New session
|
||||
detected interrupt mask. */
|
||||
#define GINTMSK_DISCM (1U<<29) /**< Disconnect detected
|
||||
interrupt mask. */
|
||||
#define GINTMSK_CIDSCHGM (1U<<28) /**< Connector ID status change
|
||||
mask. */
|
||||
#define GINTMSK_PTXFEM (1U<<26) /**< Periodic TxFIFO empty mask.*/
|
||||
#define GINTMSK_HCM (1U<<25) /**< Host channels interrupt
|
||||
mask. */
|
||||
#define GINTMSK_HPRTM (1U<<24) /**< Host port interrupt mask. */
|
||||
#define GINTMSK_IPXFRM (1U<<21) /**< Incomplete periodic
|
||||
transfer mask. */
|
||||
#define GINTMSK_IISOOXFRM (1U<<21) /**< Incomplete isochronous OUT
|
||||
transfer mask. */
|
||||
#define GINTMSK_IISOIXFRM (1U<<20) /**< Incomplete isochronous IN
|
||||
transfer mask. */
|
||||
#define GINTMSK_OEPM (1U<<19) /**< OUT endpoints interrupt
|
||||
mask. */
|
||||
#define GINTMSK_IEPM (1U<<18) /**< IN endpoints interrupt
|
||||
mask. */
|
||||
#define GINTMSK_EOPFM (1U<<15) /**< End of periodic frame
|
||||
interrupt mask. */
|
||||
#define GINTMSK_ISOODRPM (1U<<14) /**< Isochronous OUT packet
|
||||
dropped interrupt mask. */
|
||||
#define GINTMSK_ENUMDNEM (1U<<13) /**< Enumeration done mask. */
|
||||
#define GINTMSK_USBRSTM (1U<<12) /**< USB reset mask. */
|
||||
#define GINTMSK_USBSUSPM (1U<<11) /**< USB suspend mask. */
|
||||
#define GINTMSK_ESUSPM (1U<<10) /**< Early suspend mask. */
|
||||
#define GINTMSK_GONAKEFFM (1U<<7) /**< Global OUT NAK effective
|
||||
mask. */
|
||||
#define GINTMSK_GINAKEFFM (1U<<6) /**< Global non-periodic IN NAK
|
||||
effective mask. */
|
||||
#define GINTMSK_NPTXFEM (1U<<5) /**< Non-periodic TxFIFO empty
|
||||
mask. */
|
||||
#define GINTMSK_RXFLVLM (1U<<4) /**< Receive FIFO non-empty
|
||||
mask. */
|
||||
#define GINTMSK_SOFM (1U<<3) /**< Start of (micro)frame mask.*/
|
||||
#define GINTMSK_OTGM (1U<<2) /**< OTG interrupt mask. */
|
||||
#define GINTMSK_MMISM (1U<<1) /**< Mode Mismatch interrupt
|
||||
mask. */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name GRXSTSR register bit definitions
|
||||
* @{
|
||||
*/
|
||||
#define GRXSTSR_PKTSTS_MASK (15U<<17) /**< Packet status mask. */
|
||||
#define GRXSTSR_PKTSTS(n) ((n)<<17) /**< Packet status value. */
|
||||
#define GRXSTSR_OUT_GLOBAL_NAK GRXSTSR_PKTSTS(1)
|
||||
#define GRXSTSR_OUT_DATA GRXSTSR_PKTSTS(2)
|
||||
#define GRXSTSR_OUT_COMP GRXSTSR_PKTSTS(3)
|
||||
#define GRXSTSR_SETUP_COMP GRXSTSR_PKTSTS(4)
|
||||
#define GRXSTSR_SETUP_DATA GRXSTSR_PKTSTS(6)
|
||||
#define GRXSTSR_DPID_MASK (3U<<15) /**< Data PID mask. */
|
||||
#define GRXSTSR_DPID(n) ((n)<<15) /**< Data PID value. */
|
||||
#define GRXSTSR_BCNT_MASK (0x7FF<<4) /**< Byte count mask. */
|
||||
#define GRXSTSR_BCNT(n) ((n)<<4) /**< Byte count value. */
|
||||
#define GRXSTSR_CHNUM_MASK (15U<<0) /**< Channel number mask. */
|
||||
#define GRXSTSR_CHNUM(n) ((n)<<0) /**< Channel number value. */
|
||||
#define GRXSTSR_EPNUM_MASK (15U<<0) /**< Endpoint number mask. */
|
||||
#define GRXSTSR_EPNUM(n) ((n)<<0) /**< Endpoint number value. */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name GRXSTSP register bit definitions
|
||||
* @{
|
||||
*/
|
||||
#define GRXSTSP_PKTSTS_MASK (15<<17) /**< Packet status mask. */
|
||||
#define GRXSTSP_PKTSTS(n) ((n)<<17) /**< Packet status value. */
|
||||
#define GRXSTSP_OUT_GLOBAL_NAK GRXSTSP_PKTSTS(1)
|
||||
#define GRXSTSP_OUT_DATA GRXSTSP_PKTSTS(2)
|
||||
#define GRXSTSP_OUT_COMP GRXSTSP_PKTSTS(3)
|
||||
#define GRXSTSP_SETUP_COMP GRXSTSP_PKTSTS(4)
|
||||
#define GRXSTSP_SETUP_DATA GRXSTSP_PKTSTS(6)
|
||||
#define GRXSTSP_DPID_MASK (3U<<15) /**< Data PID mask. */
|
||||
#define GRXSTSP_DPID(n) ((n)<<15) /**< Data PID value. */
|
||||
#define GRXSTSP_BCNT_MASK (0x7FF<<4) /**< Byte count mask. */
|
||||
#define GRXSTSP_BCNT_OFF 4 /**< Byte count offset. */
|
||||
#define GRXSTSP_BCNT(n) ((n)<<4) /**< Byte count value. */
|
||||
#define GRXSTSP_CHNUM_MASK (15U<<0) /**< Channel number mask. */
|
||||
#define GRXSTSP_CHNUM(n) ((n)<<0) /**< Channel number value. */
|
||||
#define GRXSTSP_EPNUM_MASK (15U<<0) /**< Endpoint number mask. */
|
||||
#define GRXSTSP_EPNUM_OFF 0 /**< Endpoint number offset. */
|
||||
#define GRXSTSP_EPNUM(n) ((n)<<0) /**< Endpoint number value. */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name GRXFSIZ register bit definitions
|
||||
* @{
|
||||
*/
|
||||
#define GRXFSIZ_RXFD_MASK (0xFFFF<<0) /**< RxFIFO depth mask. */
|
||||
#define GRXFSIZ_RXFD(n) ((n)<<0) /**< RxFIFO depth value. */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name DIEPTXFx register bit definitions
|
||||
* @{
|
||||
*/
|
||||
#define DIEPTXF_INEPTXFD_MASK (0xFFFFU<<16)/**< IN endpoint TxFIFO depth
|
||||
mask. */
|
||||
#define DIEPTXF_INEPTXFD(n) ((n)<<16) /**< IN endpoint TxFIFO depth
|
||||
value. */
|
||||
#define DIEPTXF_INEPTXSA_MASK (0xFFFF<<0) /**< IN endpoint FIFOx transmit
|
||||
RAM start address mask. */
|
||||
#define DIEPTXF_INEPTXSA(n) ((n)<<0) /**< IN endpoint FIFOx transmit
|
||||
RAM start address value. */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name GCCFG register bit definitions
|
||||
* @{
|
||||
*/
|
||||
#define GCCFG_NOVBUSSENS (1U<<21) /**< VBUS sensing disable. */
|
||||
#define GCCFG_SOFOUTEN (1U<<20) /**< SOF output enable. */
|
||||
#define GCCFG_VBUSBSEN (1U<<19) /**< Enable the VBUS sensing "B"
|
||||
device. */
|
||||
#define GCCFG_VBUSASEN (1U<<18) /**< Enable the VBUS sensing "A"
|
||||
device. */
|
||||
#define GCCFG_PWRDWN (1U<<16) /**< Power down. */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name HPTXFSIZ register bit definitions
|
||||
* @{
|
||||
*/
|
||||
#define HPTXFSIZ_PTXFD_MASK (0xFFFFU<<16)/**< Host periodic TxFIFO
|
||||
depth mask. */
|
||||
#define HPTXFSIZ_PTXFD(n) ((n)<<16) /**< Host periodic TxFIFO
|
||||
depth value. */
|
||||
#define HPTXFSIZ_PTXSA_MASK (0xFFFFU<<0)/**< Host periodic TxFIFO
|
||||
Start address mask. */
|
||||
#define HPTXFSIZ_PTXSA(n) ((n)<<0) /**< Host periodic TxFIFO
|
||||
start address value. */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name HCFG register bit definitions
|
||||
* @{
|
||||
*/
|
||||
#define HCFG_FSLSS (1U<<2) /**< FS- and LS-only support. */
|
||||
#define HCFG_FSLSPCS_MASK (3U<<0) /**< FS/LS PHY clock select
|
||||
mask. */
|
||||
#define HCFG_FSLSPCS_48 (1U<<0) /**< PHY clock is running at
|
||||
48 MHz. */
|
||||
#define HCFG_FSLSPCS_6 (2U<<0) /**< PHY clock is running at
|
||||
6 MHz. */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name HFIR register bit definitions
|
||||
* @{
|
||||
*/
|
||||
#define HFIR_FRIVL_MASK (0xFFFFU<<0)/**< Frame interval mask. */
|
||||
#define HFIR_FRIVL(n) ((n)<<0) /**< Frame interval value. */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name HFNUM register bit definitions
|
||||
* @{
|
||||
*/
|
||||
#define HFNUM_FTREM_MASK (0xFFFFU<<16)/**< Frame time Remaining mask.*/
|
||||
#define HFNUM_FTREM(n) ((n)<<16) /**< Frame time Remaining value.*/
|
||||
#define HFNUM_FRNUM_MASK (0xFFFFU<<0)/**< Frame number mask. */
|
||||
#define HFNUM_FRNUM(n) ((n)<<0) /**< Frame number value. */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name HPTXSTS register bit definitions
|
||||
* @{
|
||||
*/
|
||||
#define HPTXSTS_PTXQTOP_MASK (0xFFU<<24) /**< Top of the periodic
|
||||
transmit request queue
|
||||
mask. */
|
||||
#define HPTXSTS_PTXQTOP(n) ((n)<<24) /**< Top of the periodic
|
||||
transmit request queue
|
||||
value. */
|
||||
#define HPTXSTS_PTXQSAV_MASK (0xFF<<16) /**< Periodic transmit request
|
||||
queue Space Available
|
||||
mask. */
|
||||
#define HPTXSTS_PTXQSAV(n) ((n)<<16) /**< Periodic transmit request
|
||||
queue Space Available
|
||||
value. */
|
||||
#define HPTXSTS_PTXFSAVL_MASK (0xFFFF<<0) /**< Periodic transmit Data
|
||||
FIFO Space Available
|
||||
mask. */
|
||||
#define HPTXSTS_PTXFSAVL(n) ((n)<<0) /**< Periodic transmit Data
|
||||
FIFO Space Available
|
||||
value. */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name HAINT register bit definitions
|
||||
* @{
|
||||
*/
|
||||
#define HAINT_HAINT_MASK (0xFFFFU<<0)/**< Channel interrupts mask. */
|
||||
#define HAINT_HAINT(n) ((n)<<0) /**< Channel interrupts value. */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name HAINTMSK register bit definitions
|
||||
* @{
|
||||
*/
|
||||
#define HAINTMSK_HAINTM_MASK (0xFFFFU<<0)/**< Channel interrupt mask
|
||||
mask. */
|
||||
#define HAINTMSK_HAINTM(n) ((n)<<0) /**< Channel interrupt mask
|
||||
value. */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name HPRT register bit definitions
|
||||
* @{
|
||||
*/
|
||||
#define HPRT_PSPD_MASK (3U<<17) /**< Port speed mask. */
|
||||
#define HPRT_PSPD_FS (1U<<17) /**< Full speed value. */
|
||||
#define HPRT_PSPD_LS (2U<<17) /**< Low speed value. */
|
||||
#define HPRT_PTCTL_MASK (15<<13) /**< Port Test control mask. */
|
||||
#define HPRT_PTCTL(n) ((n)<<13) /**< Port Test control value. */
|
||||
#define HPRT_PPWR (1U<<12) /**< Port power. */
|
||||
#define HPRT_PLSTS_MASK (3U<<11) /**< Port Line status mask. */
|
||||
#define HPRT_PLSTS_DM (1U<<11) /**< Logic level of D-. */
|
||||
#define HPRT_PLSTS_DP (1U<<10) /**< Logic level of D+. */
|
||||
#define HPRT_PRST (1U<<8) /**< Port reset. */
|
||||
#define HPRT_PSUSP (1U<<7) /**< Port suspend. */
|
||||
#define HPRT_PRES (1U<<6) /**< Port Resume. */
|
||||
#define HPRT_POCCHNG (1U<<5) /**< Port overcurrent change. */
|
||||
#define HPRT_POCA (1U<<4) /**< Port overcurrent active. */
|
||||
#define HPRT_PENCHNG (1U<<3) /**< Port enable/disable change.*/
|
||||
#define HPRT_PENA (1U<<2) /**< Port enable. */
|
||||
#define HPRT_PCDET (1U<<1) /**< Port Connect detected. */
|
||||
#define HPRT_PCSTS (1U<<0) /**< Port connect status. */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name HCCHAR register bit definitions
|
||||
* @{
|
||||
*/
|
||||
#define HCCHAR_CHENA (1U<<31) /**< Channel enable. */
|
||||
#define HCCHAR_CHDIS (1U<<30) /**< Channel Disable. */
|
||||
#define HCCHAR_ODDFRM (1U<<29) /**< Odd frame. */
|
||||
#define HCCHAR_DAD_MASK (0x7FU<<22) /**< Device Address mask. */
|
||||
#define HCCHAR_DAD(n) ((n)<<22) /**< Device Address value. */
|
||||
#define HCCHAR_MCNT_MASK (3U<<20) /**< Multicount mask. */
|
||||
#define HCCHAR_MCNT(n) ((n)<<20) /**< Multicount value. */
|
||||
#define HCCHAR_EPTYP_MASK (3U<<18) /**< Endpoint type mask. */
|
||||
#define HCCHAR_EPTYP(n) ((n)<<18) /**< Endpoint type value. */
|
||||
#define HCCHAR_EPTYP_CTL (0U<<18) /**< Control endpoint value. */
|
||||
#define HCCHAR_EPTYP_ISO (1U<<18) /**< Isochronous endpoint value.*/
|
||||
#define HCCHAR_EPTYP_BULK (2U<<18) /**< Bulk endpoint value. */
|
||||
#define HCCHAR_EPTYP_INTR (3U<<18) /**< Interrupt endpoint value. */
|
||||
#define HCCHAR_LSDEV (1U<<17) /**< Low-Speed device. */
|
||||
#define HCCHAR_EPDIR (1U<<15) /**< Endpoint direction. */
|
||||
#define HCCHAR_EPNUM_MASK (15U<<11) /**< Endpoint number mask. */
|
||||
#define HCCHAR_EPNUM(n) ((n)<<11) /**< Endpoint number value. */
|
||||
#define HCCHAR_MPS_MASK (11U<<0) /**< Maximum packet size mask. */
|
||||
#define HCCHAR_MPS(n) (11U<<0) /**< Maximum packet size value. */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name HCINT register bit definitions
|
||||
* @{
|
||||
*/
|
||||
#define HCINT_DTERR (1U<<10) /**< Data toggle error. */
|
||||
#define HCINT_FRMOR (1U<<9) /**< Frame overrun. */
|
||||
#define HCINT_BBERR (1U<<8) /**< Babble error. */
|
||||
#define HCINT_TRERR (1U<<7) /**< Transaction Error. */
|
||||
#define HCINT_ACK (1U<<5) /**< ACK response
|
||||
received/transmitted
|
||||
interrupt. */
|
||||
#define HCINT_NAK (1U<<4) /**< NAK response received
|
||||
interrupt. */
|
||||
#define HCINT_STALL (1U<<3) /**< STALL response received
|
||||
interrupt. */
|
||||
#define HCINT_CHH (1U<<1) /**< Channel halted. */
|
||||
#define HCINT_XFRC (1U<<0) /**< Transfer completed. */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name HCINTMSK register bit definitions
|
||||
* @{
|
||||
*/
|
||||
#define HCINTMSK_DTERRM (1U<<10) /**< Data toggle error mask. */
|
||||
#define HCINTMSK_FRMORM (1U<<9) /**< Frame overrun mask. */
|
||||
#define HCINTMSK_BBERRM (1U<<8) /**< Babble error mask. */
|
||||
#define HCINTMSK_TRERRM (1U<<7) /**< Transaction error mask. */
|
||||
#define HCINTMSK_NYET (1U<<6) /**< NYET response received
|
||||
interrupt mask. */
|
||||
#define HCINTMSK_ACKM (1U<<5) /**< ACK Response
|
||||
received/transmitted
|
||||
interrupt mask. */
|
||||
#define HCINTMSK_NAKM (1U<<4) /**< NAK response received
|
||||
interrupt mask. */
|
||||
#define HCINTMSK_STALLM (1U<<3) /**< STALL response received
|
||||
interrupt mask. */
|
||||
#define HCINTMSK_CHHM (1U<<1) /**< Channel halted mask. */
|
||||
#define HCINTMSK_XFRCM (1U<<0) /**< Transfer completed mask. */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name HCTSIZ register bit definitions
|
||||
* @{
|
||||
*/
|
||||
#define HCTSIZ_DPID_MASK (3U<<29) /**< PID mask. */
|
||||
#define HCTSIZ_DPID_DATA0 (0U<<29) /**< DATA0. */
|
||||
#define HCTSIZ_DPID_DATA2 (1U<<29) /**< DATA2. */
|
||||
#define HCTSIZ_DPID_DATA1 (2U<<29) /**< DATA1. */
|
||||
#define HCTSIZ_DPID_MDATA (3U<<29) /**< MDATA. */
|
||||
#define HCTSIZ_PKTCNT_MASK (0x3FFU<<19)/**< Packet count mask. */
|
||||
#define HCTSIZ_PKTCNT(n) ((n)<<19) /**< Packet count value. */
|
||||
#define HCTSIZ_XFRSIZ_MASK (0x7FFFF<<0)/**< Transfer size mask. */
|
||||
#define HCTSIZ_XFRSIZ(n) ((n)<<0) /**< Transfer size value. */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name DCFG register bit definitions
|
||||
* @{
|
||||
*/
|
||||
#define DCFG_PFIVL_MASK (3U<<11) /**< Periodic frame interval
|
||||
mask. */
|
||||
#define DCFG_PFIVL(n) ((n)<<11) /**< Periodic frame interval
|
||||
value. */
|
||||
#define DCFG_DAD_MASK (0x7FU<<4) /**< Device address mask. */
|
||||
#define DCFG_DAD(n) ((n)<<4) /**< Device address value. */
|
||||
#define DCFG_NZLSOHSK (1U<<2) /**< Non-Zero-Length status
|
||||
OUT handshake. */
|
||||
#define DCFG_DSPD_MASK (3U<<0) /**< Device speed mask. */
|
||||
#define DCFG_DSPD_HS (0U<<0) /**< High speed (USB 2.0). */
|
||||
#define DCFG_DSPD_HS_FS (1U<<0) /**< High speed (USB 2.0) in FS
|
||||
mode. */
|
||||
#define DCFG_DSPD_FS11 (3U<<0) /**< Full speed (USB 1.1
|
||||
transceiver clock is 48
|
||||
MHz). */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name DCTL register bit definitions
|
||||
* @{
|
||||
*/
|
||||
#define DCTL_POPRGDNE (1U<<11) /**< Power-on programming done. */
|
||||
#define DCTL_CGONAK (1U<<10) /**< Clear global OUT NAK. */
|
||||
#define DCTL_SGONAK (1U<<9) /**< Set global OUT NAK. */
|
||||
#define DCTL_CGINAK (1U<<8) /**< Clear global non-periodic
|
||||
IN NAK. */
|
||||
#define DCTL_SGINAK (1U<<7) /**< Set global non-periodic
|
||||
IN NAK. */
|
||||
#define DCTL_TCTL_MASK (7U<<4) /**< Test control mask. */
|
||||
#define DCTL_TCTL(n) ((n)<<4 /**< Test control value. */
|
||||
#define DCTL_GONSTS (1U<<3) /**< Global OUT NAK status. */
|
||||
#define DCTL_GINSTS (1U<<2) /**< Global non-periodic IN
|
||||
NAK status. */
|
||||
#define DCTL_SDIS (1U<<1) /**< Soft disconnect. */
|
||||
#define DCTL_RWUSIG (1U<<0) /**< Remote wakeup signaling. */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name DSTS register bit definitions
|
||||
* @{
|
||||
*/
|
||||
#define DSTS_FNSOF_MASK (0x3FFU<<8) /**< Frame number of the received
|
||||
SOF mask. */
|
||||
#define DSTS_FNSOF(n) ((n)<<8) /**< Frame number of the received
|
||||
SOF value. */
|
||||
#define DSTS_EERR (1U<<3) /**< Erratic error. */
|
||||
#define DSTS_ENUMSPD_MASK (3U<<1) /**< Enumerated speed mask. */
|
||||
#define DSTS_ENUMSPD_FS_48 (3U<<1) /**< Full speed (PHY clock is
|
||||
running at 48 MHz). */
|
||||
#define DSTS_SUSPSTS (1U<<0) /**< Suspend status. */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name DIEPMSK register bit definitions
|
||||
* @{
|
||||
*/
|
||||
#define DIEPMSK_TXFEM (1U<<6) /**< Transmit FIFO empty mask. */
|
||||
#define DIEPMSK_INEPNEM (1U<<6) /**< IN endpoint NAK effective
|
||||
mask. */
|
||||
#define DIEPMSK_ITTXFEMSK (1U<<4) /**< IN token received when
|
||||
TxFIFO empty mask. */
|
||||
#define DIEPMSK_TOCM (1U<<3) /**< Timeout condition mask. */
|
||||
#define DIEPMSK_EPDM (1U<<1) /**< Endpoint disabled
|
||||
interrupt mask. */
|
||||
#define DIEPMSK_XFRCM (1U<<0) /**< Transfer completed
|
||||
interrupt mask. */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name DOEPMSK register bit definitions
|
||||
* @{
|
||||
*/
|
||||
#define DOEPMSK_OTEPDM (1U<<4) /**< OUT token received when
|
||||
endpoint disabled mask. */
|
||||
#define DOEPMSK_STUPM (1U<<3) /**< SETUP phase done mask. */
|
||||
#define DOEPMSK_EPDM (1U<<1) /**< Endpoint disabled
|
||||
interrupt mask. */
|
||||
#define DOEPMSK_XFRCM (1U<<0) /**< Transfer completed
|
||||
interrupt mask. */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name DAINT register bit definitions
|
||||
* @{
|
||||
*/
|
||||
#define DAINT_OEPINT_MASK (0xFFFFU<<16)/**< OUT endpoint interrupt
|
||||
bits mask. */
|
||||
#define DAINT_OEPINT(n) ((n)<<16) /**< OUT endpoint interrupt
|
||||
bits value. */
|
||||
#define DAINT_IEPINT_MASK (0xFFFFU<<0)/**< IN endpoint interrupt
|
||||
bits mask. */
|
||||
#define DAINT_IEPINT(n) ((n)<<0) /**< IN endpoint interrupt
|
||||
bits value. */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name DAINTMSK register bit definitions
|
||||
* @{
|
||||
*/
|
||||
#define DAINTMSK_OEPM_MASK (0xFFFFU<<16)/**< OUT EP interrupt mask
|
||||
bits mask. */
|
||||
#define DAINTMSK_OEPM(n) (1U<<(16+(n)))/**< OUT EP interrupt mask
|
||||
bits value. */
|
||||
#define DAINTMSK_IEPM_MASK (0xFFFFU<<0)/**< IN EP interrupt mask
|
||||
bits mask. */
|
||||
#define DAINTMSK_IEPM(n) (1U<<(n)) /**< IN EP interrupt mask
|
||||
bits value. */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name DVBUSDIS register bit definitions
|
||||
* @{
|
||||
*/
|
||||
#define DVBUSDIS_VBUSDT_MASK (0xFFFFU<<0)/**< Device VBUS discharge
|
||||
time mask. */
|
||||
#define DVBUSDIS_VBUSDT(n) ((n)<<0) /**< Device VBUS discharge
|
||||
time value. */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name DVBUSPULSE register bit definitions
|
||||
* @{
|
||||
*/
|
||||
#define DVBUSPULSE_DVBUSP_MASK (0xFFFU<<0) /**< Device VBUSpulsing time
|
||||
mask. */
|
||||
#define DVBUSPULSE_DVBUSP(n) ((n)<<0) /**< Device VBUS pulsing time
|
||||
value. */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name DIEPEMPMSK register bit definitions
|
||||
* @{
|
||||
*/
|
||||
#define DIEPEMPMSK_INEPTXFEM(n) (1U<<(n)) /**< IN EP Tx FIFO empty
|
||||
interrupt mask bit. */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name DIEPCTL register bit definitions
|
||||
* @{
|
||||
*/
|
||||
#define DIEPCTL_EPENA (1U<<31) /**< Endpoint enable. */
|
||||
#define DIEPCTL_EPDIS (1U<<30) /**< Endpoint disable. */
|
||||
#define DIEPCTL_SD1PID (1U<<29) /**< Set DATA1 PID. */
|
||||
#define DIEPCTL_SODDFRM (1U<<29) /**< Set odd frame. */
|
||||
#define DIEPCTL_SD0PID (1U<<28) /**< Set DATA0 PID. */
|
||||
#define DIEPCTL_SEVNFRM (1U<<28) /**< Set even frame. */
|
||||
#define DIEPCTL_SNAK (1U<<27) /**< Set NAK. */
|
||||
#define DIEPCTL_CNAK (1U<<26) /**< Clear NAK. */
|
||||
#define DIEPCTL_TXFNUM_MASK (15U<<22) /**< TxFIFO number mask. */
|
||||
#define DIEPCTL_TXFNUM(n) ((n)<<22) /**< TxFIFO number value. */
|
||||
#define DIEPCTL_STALL (1U<<21) /**< STALL handshake. */
|
||||
#define DIEPCTL_SNPM (1U<<20) /**< Snoop mode. */
|
||||
#define DIEPCTL_EPTYP_MASK (3<<18) /**< Endpoint type mask. */
|
||||
#define DIEPCTL_EPTYP_CTRL (0U<<18) /**< Control. */
|
||||
#define DIEPCTL_EPTYP_ISO (1U<<18) /**< Isochronous. */
|
||||
#define DIEPCTL_EPTYP_BULK (2U<<18) /**< Bulk. */
|
||||
#define DIEPCTL_EPTYP_INTR (3U<<18) /**< Interrupt. */
|
||||
#define DIEPCTL_NAKSTS (1U<<17) /**< NAK status. */
|
||||
#define DIEPCTL_EONUM (1U<<16) /**< Even/odd frame. */
|
||||
#define DIEPCTL_DPID (1U<<16) /**< Endpoint data PID. */
|
||||
#define DIEPCTL_USBAEP (1U<<15) /**< USB active endpoint. */
|
||||
#define DIEPCTL_MPSIZ_MASK (0x3FFU<<0) /**< Maximum Packet size mask. */
|
||||
#define DIEPCTL_MPSIZ(n) ((n)<<0) /**< Maximum Packet size value. */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name DIEPINT register bit definitions
|
||||
* @{
|
||||
*/
|
||||
#define DIEPINT_TXFE (1U<<7) /**< Transmit FIFO empty. */
|
||||
#define DIEPINT_INEPNE (1U<<6) /**< IN endpoint NAK effective. */
|
||||
#define DIEPINT_ITTXFE (1U<<4) /**< IN Token received when
|
||||
TxFIFO is empty. */
|
||||
#define DIEPINT_TOC (1U<<3) /**< Timeout condition. */
|
||||
#define DIEPINT_EPDISD (1U<<1) /**< Endpoint disabled
|
||||
interrupt. */
|
||||
#define DIEPINT_XFRC (1U<<0) /**< Transfer completed. */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name DIEPTSIZ register bit definitions
|
||||
* @{
|
||||
*/
|
||||
#define DIEPTSIZ_MCNT_MASK (3U<<29) /**< Multi count mask. */
|
||||
#define DIEPTSIZ_MCNT(n) ((n)<<29) /**< Multi count value. */
|
||||
#define DIEPTSIZ_PKTCNT_MASK (0x3FF<<19) /**< Packet count mask. */
|
||||
#define DIEPTSIZ_PKTCNT(n) ((n)<<19) /**< Packet count value. */
|
||||
#define DIEPTSIZ_XFRSIZ_MASK (0x7FFFFU<<0)/**< Transfer size mask. */
|
||||
#define DIEPTSIZ_XFRSIZ(n) ((n)<<0) /**< Transfer size value. */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name DTXFSTS register bit definitions.
|
||||
* @{
|
||||
*/
|
||||
#define DTXFSTS_INEPTFSAV_MASK (0xFFFF<<0) /**< IN endpoint TxFIFO space
|
||||
available. */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name DOEPCTL register bit definitions.
|
||||
* @{
|
||||
*/
|
||||
#define DOEPCTL_EPENA (1U<<31) /**< Endpoint enable. */
|
||||
#define DOEPCTL_EPDIS (1U<<30) /**< Endpoint disable. */
|
||||
#define DOEPCTL_SD1PID (1U<<29) /**< Set DATA1 PID. */
|
||||
#define DOEPCTL_SODDFRM (1U<<29) /**< Set odd frame. */
|
||||
#define DOEPCTL_SD0PID (1U<<28) /**< Set DATA0 PID. */
|
||||
#define DOEPCTL_SEVNFRM (1U<<28) /**< Set even frame. */
|
||||
#define DOEPCTL_SNAK (1U<<27) /**< Set NAK. */
|
||||
#define DOEPCTL_CNAK (1U<<26) /**< Clear NAK. */
|
||||
#define DOEPCTL_STALL (1U<<21) /**< STALL handshake. */
|
||||
#define DOEPCTL_SNPM (1U<<20) /**< Snoop mode. */
|
||||
#define DOEPCTL_EPTYP_MASK (3U<<18) /**< Endpoint type mask. */
|
||||
#define DOEPCTL_EPTYP_CTRL (0U<<18) /**< Control. */
|
||||
#define DOEPCTL_EPTYP_ISO (1U<<18) /**< Isochronous. */
|
||||
#define DOEPCTL_EPTYP_BULK (2U<<18) /**< Bulk. */
|
||||
#define DOEPCTL_EPTYP_INTR (3U<<18) /**< Interrupt. */
|
||||
#define DOEPCTL_NAKSTS (1U<<17) /**< NAK status. */
|
||||
#define DOEPCTL_EONUM (1U<<16) /**< Even/odd frame. */
|
||||
#define DOEPCTL_DPID (1U<<16) /**< Endpoint data PID. */
|
||||
#define DOEPCTL_USBAEP (1U<<15) /**< USB active endpoint. */
|
||||
#define DOEPCTL_MPSIZ_MASK (0x3FFU<<0) /**< Maximum Packet size mask. */
|
||||
#define DOEPCTL_MPSIZ(n) ((n)<<0) /**< Maximum Packet size value. */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name DOEPINT register bit definitions
|
||||
* @{
|
||||
*/
|
||||
#define DOEPINT_B2BSTUP (1U<<6) /**< Back-to-back SETUP packets
|
||||
received. */
|
||||
#define DOEPINT_OTEPDIS (1U<<4) /**< OUT token received when
|
||||
endpoint disabled. */
|
||||
#define DOEPINT_STUP (1U<<3) /**< SETUP phase done. */
|
||||
#define DOEPINT_EPDISD (1U<<1) /**< Endpoint disabled
|
||||
interrupt. */
|
||||
#define DOEPINT_XFRC (1U<<0) /**< Transfer completed
|
||||
interrupt. */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name DOEPTSIZ register bit definitions
|
||||
* @{
|
||||
*/
|
||||
#define DOEPTSIZ_RXDPID_MASK (3U<<29) /**< Received data PID mask. */
|
||||
#define DOEPTSIZ_RXDPID(n) ((n)<<29) /**< Received data PID value. */
|
||||
#define DOEPTSIZ_STUPCNT_MASK (3U<<29) /**< SETUP packet count mask. */
|
||||
#define DOEPTSIZ_STUPCNT(n) ((n)<<29) /**< SETUP packet count value. */
|
||||
#define DOEPTSIZ_PKTCNT_MASK (0x3FFU<<19)/**< Packet count mask. */
|
||||
#define DOEPTSIZ_PKTCNT(n) ((n)<<19) /**< Packet count value. */
|
||||
#define DOEPTSIZ_XFRSIZ_MASK (0x7FFFFU<<0)/**< Transfer size mask. */
|
||||
#define DOEPTSIZ_XFRSIZ(n) ((n)<<0) /**< Transfer size value. */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name PCGCCTL register bit definitions
|
||||
* @{
|
||||
*/
|
||||
#define PCGCCTL_PHYSUSP (1U<<4) /**< PHY Suspended. */
|
||||
#define PCGCCTL_GATEHCLK (1U<<1) /**< Gate HCLK. */
|
||||
#define PCGCCTL_STPPCLK (1U<<0) /**< Stop PCLK. */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @brief OTG_FS registers block memory address.
|
||||
*/
|
||||
#define OTG_FS_ADDR 0x50000000
|
||||
|
||||
/**
|
||||
* @brief OTG_HS registers block memory address.
|
||||
*/
|
||||
#define OTG_HS_ADDR 0x40040000
|
||||
|
||||
/**
|
||||
* @brief Accesses to the OTG_FS registers block.
|
||||
*/
|
||||
#define OTG_FS ((stm32_otg_t *)OTG_FS_ADDR)
|
||||
|
||||
/**
|
||||
* @brief Accesses to the OTG_HS registers block.
|
||||
*/
|
||||
#define OTG_HS ((stm32_otg_t *)OTG_HS_ADDR)
|
||||
|
||||
#endif /* _STM32_OTG_H_ */
|
||||
|
||||
/** @} */
|
||||
+1354
File diff suppressed because it is too large
Load Diff
+552
@@ -0,0 +1,552 @@
|
||||
/*
|
||||
ChibiOS/RT - Copyright (C) 2006-2013 Giovanni Di Sirio
|
||||
|
||||
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 STM32/OTGv1/usb_lld.h
|
||||
* @brief STM32 USB subsystem low level driver header.
|
||||
*
|
||||
* @addtogroup USB
|
||||
* @{
|
||||
*/
|
||||
|
||||
#ifndef _USB_LLD_H_
|
||||
#define _USB_LLD_H_
|
||||
|
||||
#if HAL_USE_USB || defined(__DOXYGEN__)
|
||||
|
||||
#include "stm32_otg.h"
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver constants. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Maximum endpoint address.
|
||||
*/
|
||||
#if !STM32_USB_USE_OTG2 || defined(__DOXYGEN__)
|
||||
#define USB_MAX_ENDPOINTS 3
|
||||
#else
|
||||
#define USB_MAX_ENDPOINTS 5
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Status stage handling method.
|
||||
*/
|
||||
#define USB_EP0_STATUS_STAGE USB_EP0_STATUS_STAGE_SW
|
||||
|
||||
/**
|
||||
* @brief The address can be changed immediately upon packet reception.
|
||||
*/
|
||||
#define USB_SET_ADDRESS_MODE USB_EARLY_SET_ADDRESS
|
||||
|
||||
/**
|
||||
* @brief Method for set address acknowledge.
|
||||
*/
|
||||
#define USB_SET_ADDRESS_ACK_HANDLING USB_SET_ADDRESS_ACK_SW
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver pre-compile time settings. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief OTG1 driver enable switch.
|
||||
* @details If set to @p TRUE the support for OTG_FS is included.
|
||||
* @note The default is @p TRUE.
|
||||
*/
|
||||
#if !defined(STM32_USB_USE_OTG1) || defined(__DOXYGEN__)
|
||||
#define STM32_USB_USE_OTG1 FALSE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief OTG2 driver enable switch.
|
||||
* @details If set to @p TRUE the support for OTG_HS is included.
|
||||
* @note The default is @p TRUE.
|
||||
*/
|
||||
#if !defined(STM32_USB_USE_OTG2) || defined(__DOXYGEN__)
|
||||
#define STM32_USB_USE_OTG2 FALSE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief OTG1 interrupt priority level setting.
|
||||
*/
|
||||
#if !defined(STM32_USB_OTG1_IRQ_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define STM32_USB_OTG1_IRQ_PRIORITY 14
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief OTG2 interrupt priority level setting.
|
||||
*/
|
||||
#if !defined(STM32_USB_OTG2_IRQ_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define STM32_USB_OTG2_IRQ_PRIORITY 14
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief OTG1 RX shared FIFO size.
|
||||
* @note Must be a multiple of 4.
|
||||
*/
|
||||
#if !defined(STM32_USB_OTG1_RX_FIFO_SIZE) || defined(__DOXYGEN__)
|
||||
#define STM32_USB_OTG1_RX_FIFO_SIZE 512
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief OTG2 RX shared FIFO size.
|
||||
* @note Must be a multiple of 4.
|
||||
*/
|
||||
#if !defined(STM32_USB_OTG2_RX_FIFO_SIZE) || defined(__DOXYGEN__)
|
||||
#define STM32_USB_OTG2_RX_FIFO_SIZE 1024
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Dedicated data pump threads priority.
|
||||
*/
|
||||
#if !defined(STM32_USB_OTG_THREAD_PRIO) || defined(__DOXYGEN__)
|
||||
#define STM32_USB_OTG_THREAD_PRIO LOWPRIO
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Dedicated data pump threads stack size.
|
||||
*/
|
||||
#if !defined(STM32_USB_OTG_THREAD_STACK_SIZE) || defined(__DOXYGEN__)
|
||||
#define STM32_USB_OTG_THREAD_STACK_SIZE 128
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Exception priority level during TXFIFOs operations.
|
||||
* @note Because an undocumented silicon behavior the operation of
|
||||
* copying a packet into a TXFIFO must not be interrupted by
|
||||
* any other operation on the OTG peripheral.
|
||||
* This parameter represents the priority mask during copy
|
||||
* operations. The default value only allows to call USB
|
||||
* functions from callbacks invoked from USB ISR handlers.
|
||||
* If you need to invoke USB functions from other handlers
|
||||
* then raise this priority mast to the same level of the
|
||||
* handler you need to use.
|
||||
* @note The value zero means disabled, when disabled calling USB
|
||||
* functions is only safe from thread level or from USB
|
||||
* callbacks.
|
||||
*/
|
||||
#if !defined(STM32_USB_OTGFIFO_FILL_BASEPRI) || defined(__DOXYGEN__)
|
||||
#define STM32_USB_OTGFIFO_FILL_BASEPRI 0
|
||||
#endif
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Derived constants and error checks. */
|
||||
/*===========================================================================*/
|
||||
|
||||
#if STM32_USB_USE_OTG1 && !STM32_HAS_OTG1
|
||||
#error "OTG1 not present in the selected device"
|
||||
#endif
|
||||
|
||||
#if STM32_USB_USE_OTG2 && !STM32_HAS_OTG2
|
||||
#error "OTG2 not present in the selected device"
|
||||
#endif
|
||||
|
||||
#if !STM32_USB_USE_OTG1 && !STM32_USB_USE_OTG2
|
||||
#error "USB driver activated but no USB peripheral assigned"
|
||||
#endif
|
||||
|
||||
#if STM32_USB_USE_OTG1 && \
|
||||
!CORTEX_IS_VALID_KERNEL_PRIORITY(STM32_USB_OTG1_IRQ_PRIORITY)
|
||||
#error "Invalid IRQ priority assigned to OTG1"
|
||||
#endif
|
||||
|
||||
#if STM32_USB_USE_OTG2 && \
|
||||
!CORTEX_IS_VALID_KERNEL_PRIORITY(STM32_USB_OTG2_IRQ_PRIORITY)
|
||||
#error "Invalid IRQ priority assigned to OTG2"
|
||||
#endif
|
||||
|
||||
#if (STM32_USB_OTG1_RX_FIFO_SIZE & 3) != 0
|
||||
#error "OTG1 RX FIFO size must be a multiple of 4"
|
||||
#endif
|
||||
|
||||
#if (STM32_USB_OTG2_RX_FIFO_SIZE & 3) != 0
|
||||
#error "OTG2 RX FIFO size must be a multiple of 4"
|
||||
#endif
|
||||
|
||||
#if defined(STM32F4XX) || defined(STM32F2XX)
|
||||
#define STM32_USBCLK STM32_PLL48CLK
|
||||
#elif defined(STM32F10X_CL)
|
||||
#define STM32_USBCLK STM32_OTGFSCLK
|
||||
#else
|
||||
#error "unsupported STM32 platform for OTG functionality"
|
||||
#endif
|
||||
|
||||
#if STM32_USBCLK != 48000000
|
||||
#error "the USB OTG driver requires a 48MHz clock"
|
||||
#endif
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver data structures and types. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Peripheral-specific parameters block.
|
||||
*/
|
||||
typedef struct {
|
||||
uint32_t rx_fifo_size;
|
||||
uint32_t otg_ram_size;
|
||||
uint32_t num_endpoints;
|
||||
} stm32_otg_params_t;
|
||||
|
||||
/**
|
||||
* @brief Type of an IN endpoint state structure.
|
||||
*/
|
||||
typedef struct {
|
||||
/**
|
||||
* @brief Buffer mode, queue or linear.
|
||||
*/
|
||||
bool_t txqueued;
|
||||
/**
|
||||
* @brief Requested transmit transfer size.
|
||||
*/
|
||||
size_t txsize;
|
||||
/**
|
||||
* @brief Transmitted bytes so far.
|
||||
*/
|
||||
size_t txcnt;
|
||||
union {
|
||||
struct {
|
||||
/**
|
||||
* @brief Pointer to the transmission linear buffer.
|
||||
*/
|
||||
const uint8_t *txbuf;
|
||||
} linear;
|
||||
struct {
|
||||
/**
|
||||
* @brief Pointer to the output queue.
|
||||
*/
|
||||
OutputQueue *txqueue;
|
||||
} queue;
|
||||
} mode;
|
||||
/**
|
||||
* @brief Total transmit transfer size.
|
||||
*/
|
||||
size_t totsize;
|
||||
} USBInEndpointState;
|
||||
|
||||
/**
|
||||
* @brief Type of an OUT endpoint state structure.
|
||||
*/
|
||||
typedef struct {
|
||||
/**
|
||||
* @brief Buffer mode, queue or linear.
|
||||
*/
|
||||
bool_t rxqueued;
|
||||
/**
|
||||
* @brief Requested receive transfer size.
|
||||
*/
|
||||
size_t rxsize;
|
||||
/**
|
||||
* @brief Received bytes so far.
|
||||
*/
|
||||
size_t rxcnt;
|
||||
union {
|
||||
struct {
|
||||
/**
|
||||
* @brief Pointer to the receive linear buffer.
|
||||
*/
|
||||
uint8_t *rxbuf;
|
||||
} linear;
|
||||
struct {
|
||||
/**
|
||||
* @brief Pointer to the input queue.
|
||||
*/
|
||||
InputQueue *rxqueue;
|
||||
} queue;
|
||||
} mode;
|
||||
/**
|
||||
* @brief Total transmit transfer size.
|
||||
*/
|
||||
size_t totsize;
|
||||
} 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 the IN endpoint is not
|
||||
* used.
|
||||
*/
|
||||
usbepcallback_t in_cb;
|
||||
/**
|
||||
* @brief OUT endpoint notification callback.
|
||||
* @details This field must be set to @p NULL if the OUT endpoint is not
|
||||
* used.
|
||||
*/
|
||||
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 structure maintains the state of the IN endpoint.
|
||||
*/
|
||||
USBInEndpointState *in_state;
|
||||
/**
|
||||
* @brief @p USBEndpointState associated to the OUT endpoint.
|
||||
* @details This structure maintains the state of the OUT endpoint.
|
||||
*/
|
||||
USBOutEndpointState *out_state;
|
||||
/* End of the mandatory fields.*/
|
||||
/**
|
||||
* @brief Determines the space allocated for the TXFIFO as multiples of
|
||||
* the packet size (@p in_maxsize). Note that zero is interpreted
|
||||
* as one for simplicity and robustness.
|
||||
*/
|
||||
uint16_t in_multiplier;
|
||||
/**
|
||||
* @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;
|
||||
#if defined(USB_DRIVER_EXT_FIELDS)
|
||||
USB_DRIVER_EXT_FIELDS
|
||||
#endif
|
||||
/* End of the mandatory fields.*/
|
||||
/**
|
||||
* @brief Pointer to the OTG peripheral associated to this driver.
|
||||
*/
|
||||
stm32_otg_t *otg;
|
||||
/**
|
||||
* @brief Peripheral-specific parameters.
|
||||
*/
|
||||
const stm32_otg_params_t *otgparams;
|
||||
/**
|
||||
* @brief Pointer to the next address in the packet memory.
|
||||
*/
|
||||
uint32_t pmnext;
|
||||
/**
|
||||
* @brief Mask of TXFIFOs to be filled by the pump thread.
|
||||
*/
|
||||
uint32_t txpending;
|
||||
/**
|
||||
* @brief Pointer to the thread.
|
||||
*/
|
||||
Thread *thd_ptr;
|
||||
/**
|
||||
* @brief Pointer to the thread when it is sleeping or @p NULL.
|
||||
*/
|
||||
Thread *thd_wait;
|
||||
/**
|
||||
* @brief Working area for the dedicated data pump thread;
|
||||
*/
|
||||
WORKING_AREA(wa_pump, STM32_USB_OTG_THREAD_STACK_SIZE);
|
||||
};
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver macros. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @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
|
||||
*/
|
||||
#define usb_lld_connect_bus(usbp) ((usbp)->otg->GCCFG |= GCCFG_VBUSBSEN)
|
||||
|
||||
/**
|
||||
* @brief Disconnect the USB device.
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
#define usb_lld_disconnect_bus(usbp) ((usbp)->otg->GCCFG &= ~GCCFG_VBUSBSEN)
|
||||
|
||||
/*===========================================================================*/
|
||||
/* External declarations. */
|
||||
/*===========================================================================*/
|
||||
|
||||
#if STM32_USB_USE_OTG1 && !defined(__DOXYGEN__)
|
||||
extern USBDriver USBD1;
|
||||
#endif
|
||||
|
||||
#if STM32_USB_USE_OTG2 && !defined(__DOXYGEN__)
|
||||
extern USBDriver USBD2;
|
||||
#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_prepare_receive(USBDriver *usbp, usbep_t ep);
|
||||
void usb_lld_prepare_transmit(USBDriver *usbp, usbep_t ep);
|
||||
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 /* _USB_LLD_H_ */
|
||||
|
||||
/** @} */
|
||||
+329
@@ -0,0 +1,329 @@
|
||||
/*
|
||||
ChibiOS/RT - Copyright (C) 2006-2013 Giovanni Di Sirio
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
/*
|
||||
Concepts and parts of this file have been contributed by Uladzimir Pylinsky
|
||||
aka barthess.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file STM32/RTCv1/rtc_lld.c
|
||||
* @brief STM32 RTC subsystem low level driver header.
|
||||
*
|
||||
* @addtogroup RTC
|
||||
* @{
|
||||
*/
|
||||
|
||||
#include "ch.h"
|
||||
#include "hal.h"
|
||||
|
||||
#if HAL_USE_RTC || defined(__DOXYGEN__)
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local definitions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver exported variables. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief RTC driver identifier.
|
||||
*/
|
||||
RTCDriver RTCD1;
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local variables and types. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Wait for synchronization of RTC registers with APB1 bus.
|
||||
* @details This function must be invoked before trying to read RTC registers
|
||||
* in the backup domain: DIV, CNT, ALR. CR registers can always
|
||||
* be read.
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
#define rtc_lld_apb1_sync() {while ((RTC->CRL & RTC_CRL_RSF) == 0);}
|
||||
|
||||
/**
|
||||
* @brief Wait for for previous write operation complete.
|
||||
* @details This function must be invoked before writing to any RTC registers
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
#define rtc_lld_wait_write() {while ((RTC->CRL & RTC_CRL_RTOFF) == 0);}
|
||||
|
||||
/**
|
||||
* @brief Acquires write access to RTC registers.
|
||||
* @details Before writing to the backup domain RTC registers the previous
|
||||
* write operation must be completed. Use this function before
|
||||
* writing to PRL, CNT, ALR registers.
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
#define rtc_lld_acquire() {rtc_lld_wait_write(); RTC->CRL |= RTC_CRL_CNF;}
|
||||
|
||||
/**
|
||||
* @brief Releases write access to RTC registers.
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
#define rtc_lld_release() {RTC->CRL &= ~RTC_CRL_CNF;}
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver interrupt handlers. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief RTC interrupt handler.
|
||||
*
|
||||
* @isr
|
||||
*/
|
||||
CH_IRQ_HANDLER(RTC_IRQHandler) {
|
||||
uint16_t flags;
|
||||
|
||||
CH_IRQ_PROLOGUE();
|
||||
|
||||
/* This wait works only when AHB1 bus was previously powered off by any
|
||||
reason (standby, reset, etc). In other cases it does nothing.*/
|
||||
rtc_lld_apb1_sync();
|
||||
|
||||
/* Mask of all enabled and pending sources.*/
|
||||
flags = RTC->CRH & RTC->CRL;
|
||||
RTC->CRL &= ~(RTC_CRL_SECF | RTC_CRL_ALRF | RTC_CRL_OWF);
|
||||
|
||||
if (flags & RTC_CRL_SECF)
|
||||
RTCD1.callback(&RTCD1, RTC_EVENT_SECOND);
|
||||
|
||||
if (flags & RTC_CRL_ALRF)
|
||||
RTCD1.callback(&RTCD1, RTC_EVENT_ALARM);
|
||||
|
||||
if (flags & RTC_CRL_OWF)
|
||||
RTCD1.callback(&RTCD1, RTC_EVENT_OVERFLOW);
|
||||
|
||||
CH_IRQ_EPILOGUE();
|
||||
}
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver exported functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Load value of RTCCLK to prescaler registers.
|
||||
* @note The pre-scaler must not be set on every reset as RTC clock
|
||||
* counts are lost when it is set.
|
||||
* @note This function designed to be called from
|
||||
* hal_lld_backup_domain_init(). Because there is only place
|
||||
* where possible to detect BKP domain reset event reliably.
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void rtc_lld_set_prescaler(void){
|
||||
rtc_lld_acquire();
|
||||
RTC->PRLH = (uint16_t)((STM32_RTCCLK - 1) >> 16) & 0x000F;
|
||||
RTC->PRLL = (uint16_t)(((STM32_RTCCLK - 1)) & 0xFFFF);
|
||||
rtc_lld_release();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initialize RTC.
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void rtc_lld_init(void){
|
||||
|
||||
/* RSF bit must be cleared by software after an APB1 reset or an APB1 clock
|
||||
stop. Otherwise its value will not be actual. */
|
||||
RTC->CRL &= ~RTC_CRL_RSF;
|
||||
|
||||
/* Required because access to PRL.*/
|
||||
rtc_lld_apb1_sync();
|
||||
|
||||
/* All interrupts initially disabled.*/
|
||||
rtc_lld_wait_write();
|
||||
RTC->CRH = 0;
|
||||
|
||||
/* Callback initially disabled.*/
|
||||
RTCD1.callback = NULL;
|
||||
|
||||
/* IRQ vector permanently assigned to this driver.*/
|
||||
nvicEnableVector(RTC_IRQn, CORTEX_PRIORITY_MASK(STM32_RTC_IRQ_PRIORITY));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set current time.
|
||||
* @note Fractional part will be silently ignored. There is no possibility
|
||||
* to change it on STM32F1xx platform.
|
||||
*
|
||||
* @param[in] rtcp pointer to RTC driver structure
|
||||
* @param[in] timespec pointer to a @p RTCTime structure
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void rtc_lld_set_time(RTCDriver *rtcp, const RTCTime *timespec) {
|
||||
|
||||
(void)rtcp;
|
||||
|
||||
rtc_lld_acquire();
|
||||
RTC->CNTH = (uint16_t)(timespec->tv_sec >> 16);
|
||||
RTC->CNTL = (uint16_t)(timespec->tv_sec & 0xFFFF);
|
||||
rtc_lld_release();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get current time.
|
||||
*
|
||||
* @param[in] rtcp pointer to RTC driver structure
|
||||
* @param[in] timespec pointer to a @p RTCTime structure
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void rtc_lld_get_time(RTCDriver *rtcp, RTCTime *timespec) {
|
||||
(void)rtcp;
|
||||
|
||||
uint32_t time_frac;
|
||||
|
||||
/* Required because access to CNT and DIV.*/
|
||||
rtc_lld_apb1_sync();
|
||||
|
||||
/* Loops until two consecutive read returning the same value.*/
|
||||
do {
|
||||
timespec->tv_sec = ((uint32_t)(RTC->CNTH) << 16) + RTC->CNTL;
|
||||
time_frac = (((uint32_t)RTC->DIVH) << 16) + (uint32_t)RTC->DIVL;
|
||||
} while ((timespec->tv_sec) != (((uint32_t)(RTC->CNTH) << 16) + RTC->CNTL));
|
||||
|
||||
timespec->tv_msec = (uint16_t)(((STM32_RTCCLK - 1 - time_frac) * 1000) /
|
||||
STM32_RTCCLK);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set alarm time.
|
||||
*
|
||||
* @note Default value after BKP domain reset is 0xFFFFFFFF
|
||||
*
|
||||
* @param[in] rtcp pointer to RTC driver structure
|
||||
* @param[in] alarm alarm identifier
|
||||
* @param[in] alarmspec pointer to a @p RTCAlarm structure
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void rtc_lld_set_alarm(RTCDriver *rtcp,
|
||||
rtcalarm_t alarm,
|
||||
const RTCAlarm *alarmspec) {
|
||||
|
||||
(void)rtcp;
|
||||
(void)alarm;
|
||||
|
||||
rtc_lld_acquire();
|
||||
if (alarmspec != NULL) {
|
||||
RTC->ALRH = (uint16_t)(alarmspec->tv_sec >> 16);
|
||||
RTC->ALRL = (uint16_t)(alarmspec->tv_sec & 0xFFFF);
|
||||
}
|
||||
else {
|
||||
RTC->ALRH = 0;
|
||||
RTC->ALRL = 0;
|
||||
}
|
||||
rtc_lld_release();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get current alarm.
|
||||
* @note If an alarm has not been set then the returned alarm specification
|
||||
* is not meaningful.
|
||||
*
|
||||
* @note Default value after BKP domain reset is 0xFFFFFFFF.
|
||||
*
|
||||
* @param[in] rtcp pointer to RTC driver structure
|
||||
* @param[in] alarm alarm identifier
|
||||
* @param[out] alarmspec pointer to a @p RTCAlarm structure
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void rtc_lld_get_alarm(RTCDriver *rtcp,
|
||||
rtcalarm_t alarm,
|
||||
RTCAlarm *alarmspec) {
|
||||
|
||||
(void)rtcp;
|
||||
(void)alarm;
|
||||
|
||||
/* Required because access to ALR.*/
|
||||
rtc_lld_apb1_sync();
|
||||
|
||||
alarmspec->tv_sec = ((RTC->ALRH << 16) + RTC->ALRL);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enables or disables RTC callbacks.
|
||||
* @details This function enables or disables callbacks, use a @p NULL pointer
|
||||
* in order to disable a callback.
|
||||
*
|
||||
* @param[in] rtcp pointer to RTC driver structure
|
||||
* @param[in] callback callback function pointer or @p NULL
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void rtc_lld_set_callback(RTCDriver *rtcp, rtccb_t callback) {
|
||||
|
||||
if (callback != NULL) {
|
||||
|
||||
/* IRQ sources enabled only after setting up the callback.*/
|
||||
rtcp->callback = callback;
|
||||
|
||||
rtc_lld_wait_write();
|
||||
RTC->CRL &= ~(RTC_CRL_OWF | RTC_CRL_ALRF | RTC_CRL_SECF);
|
||||
RTC->CRH = RTC_CRH_OWIE | RTC_CRH_ALRIE | RTC_CRH_SECIE;
|
||||
}
|
||||
else {
|
||||
rtc_lld_wait_write();
|
||||
RTC->CRH = 0;
|
||||
|
||||
/* Callback set to NULL only after disabling the IRQ sources.*/
|
||||
rtcp->callback = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
#include "chrtclib.h"
|
||||
|
||||
/**
|
||||
* @brief Get current time in format suitable for usage in FatFS.
|
||||
*
|
||||
* @param[in] rtcp pointer to RTC driver structure
|
||||
* @return FAT time value.
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
uint32_t rtc_lld_get_time_fat(RTCDriver *rtcp) {
|
||||
uint32_t fattime;
|
||||
struct tm timp;
|
||||
|
||||
rtcGetTimeTm(rtcp, &timp);
|
||||
|
||||
fattime = (timp.tm_sec) >> 1;
|
||||
fattime |= (timp.tm_min) << 5;
|
||||
fattime |= (timp.tm_hour) << 11;
|
||||
fattime |= (timp.tm_mday) << 16;
|
||||
fattime |= (timp.tm_mon + 1) << 21;
|
||||
fattime |= (timp.tm_year - 80) << 25;
|
||||
|
||||
return fattime;
|
||||
}
|
||||
#endif /* HAL_USE_RTC */
|
||||
|
||||
/** @} */
|
||||
+187
@@ -0,0 +1,187 @@
|
||||
/*
|
||||
ChibiOS/RT - Copyright (C) 2006-2013 Giovanni Di Sirio
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
/*
|
||||
Concepts and parts of this file have been contributed by Uladzimir Pylinsky
|
||||
aka barthess.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file STM32/RTCv1/rtc_lld.h
|
||||
* @brief STM32F1xx RTC subsystem low level driver header.
|
||||
*
|
||||
* @addtogroup RTC
|
||||
* @{
|
||||
*/
|
||||
|
||||
#ifndef _RTC_LLD_H_
|
||||
#define _RTC_LLD_H_
|
||||
|
||||
#if HAL_USE_RTC || defined(__DOXYGEN__)
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver constants. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief This RTC implementation supports callbacks.
|
||||
*/
|
||||
#define RTC_SUPPORTS_CALLBACKS TRUE
|
||||
|
||||
/**
|
||||
* @brief One alarm comparator available.
|
||||
*/
|
||||
#define RTC_ALARMS 1
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver pre-compile time settings. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @name Configuration options
|
||||
* @{
|
||||
*/
|
||||
/*
|
||||
* RTC driver system settings.
|
||||
*/
|
||||
#define STM32_RTC_IRQ_PRIORITY 15
|
||||
/** @} */
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Derived constants and error checks. */
|
||||
/*===========================================================================*/
|
||||
|
||||
#if HAL_USE_RTC && !STM32_HAS_RTC
|
||||
#error "RTC not present in the selected device"
|
||||
#endif
|
||||
|
||||
#if STM32_RTCCLK == 0
|
||||
#error "RTC clock not enabled"
|
||||
#endif
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver data structures and types. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Type of a structure representing an RTC alarm time stamp.
|
||||
*/
|
||||
typedef struct RTCAlarm RTCAlarm;
|
||||
|
||||
/**
|
||||
* @brief Type of a structure representing an RTC callbacks config.
|
||||
*/
|
||||
typedef struct RTCCallbackConfig RTCCallbackConfig;
|
||||
|
||||
/**
|
||||
* @brief Type of an RTC alarm.
|
||||
* @details Meaningful on platforms with more than 1 alarm comparator.
|
||||
*/
|
||||
typedef uint32_t rtcalarm_t;
|
||||
|
||||
/**
|
||||
* @brief Type of an RTC event.
|
||||
*/
|
||||
typedef enum {
|
||||
RTC_EVENT_SECOND = 0, /** Triggered every second. */
|
||||
RTC_EVENT_ALARM = 1, /** Triggered on alarm. */
|
||||
RTC_EVENT_OVERFLOW = 2 /** Triggered on counter overflow. */
|
||||
} rtcevent_t;
|
||||
|
||||
/**
|
||||
* @brief Type of a generic RTC callback.
|
||||
*/
|
||||
typedef void (*rtccb_t)(RTCDriver *rtcp, rtcevent_t event);
|
||||
|
||||
/**
|
||||
* @brief Structure representing an RTC callbacks config.
|
||||
*/
|
||||
struct RTCCallbackConfig{
|
||||
/**
|
||||
* @brief Generic RTC callback pointer.
|
||||
*/
|
||||
rtccb_t callback;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Structure representing an RTC time stamp.
|
||||
*/
|
||||
struct RTCTime {
|
||||
/**
|
||||
* @brief Seconds since UNIX epoch.
|
||||
*/
|
||||
uint32_t tv_sec;
|
||||
/**
|
||||
* @brief Fractional part.
|
||||
*/
|
||||
uint32_t tv_msec;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Structure representing an RTC alarm time stamp.
|
||||
*/
|
||||
struct RTCAlarm {
|
||||
/**
|
||||
* @brief Seconds since UNIX epoch.
|
||||
*/
|
||||
uint32_t tv_sec;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Structure representing an RTC driver.
|
||||
*/
|
||||
struct RTCDriver{
|
||||
/**
|
||||
* @brief Callback pointer.
|
||||
*/
|
||||
rtccb_t callback;
|
||||
};
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver macros. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* External declarations. */
|
||||
/*===========================================================================*/
|
||||
|
||||
#if !defined(__DOXYGEN__)
|
||||
extern RTCDriver RTCD1;
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
void rtc_lld_set_prescaler(void);
|
||||
void rtc_lld_init(void);
|
||||
void rtc_lld_set_time(RTCDriver *rtcp, const RTCTime *timespec);
|
||||
void rtc_lld_get_time(RTCDriver *rtcp, RTCTime *timespec);
|
||||
void rtc_lld_set_alarm(RTCDriver *rtcp,
|
||||
rtcalarm_t alarm,
|
||||
const RTCAlarm *alarmspec);
|
||||
void rtc_lld_get_alarm(RTCDriver *rtcp,
|
||||
rtcalarm_t alarm,
|
||||
RTCAlarm *alarmspec);
|
||||
void rtc_lld_set_callback(RTCDriver *rtcp, rtccb_t callback);
|
||||
uint32_t rtc_lld_get_time_fat(RTCDriver *rtcp);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* HAL_USE_RTC */
|
||||
|
||||
#endif /* _RTC_LLD_H_ */
|
||||
|
||||
/** @} */
|
||||
+337
@@ -0,0 +1,337 @@
|
||||
/*
|
||||
ChibiOS/RT - Copyright (C) 2006-2013 Giovanni Di Sirio
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
/*
|
||||
Concepts and parts of this file have been contributed by Uladzimir Pylinsky
|
||||
aka barthess.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file STM32/RTCv2/rtc_lld.c
|
||||
* @brief RTC low level driver.
|
||||
*
|
||||
* @addtogroup RTC
|
||||
* @{
|
||||
*/
|
||||
|
||||
#include "ch.h"
|
||||
#include "hal.h"
|
||||
|
||||
#if HAL_USE_RTC || defined(__DOXYGEN__)
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local definitions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver exported variables. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief RTC driver identifier.
|
||||
*/
|
||||
RTCDriver RTCD1;
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local variables and types. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Wait for synchronization of RTC registers with APB1 bus.
|
||||
* @details This function must be invoked before trying to read RTC registers.
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
#define rtc_lld_apb1_sync() {while ((RTCD1.id_rtc->ISR & RTC_ISR_RSF) == 0);}
|
||||
|
||||
/**
|
||||
* @brief Beginning of configuration procedure.
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
#define rtc_lld_enter_init() { \
|
||||
RTCD1.id_rtc->ISR |= RTC_ISR_INIT; \
|
||||
while ((RTCD1.id_rtc->ISR & RTC_ISR_INITF) == 0) \
|
||||
; \
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Finalizing of configuration procedure.
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
#define rtc_lld_exit_init() {RTCD1.id_rtc->ISR &= ~RTC_ISR_INIT;}
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver interrupt handlers. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver exported functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Enable access to registers.
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
void rtc_lld_init(void){
|
||||
RTCD1.id_rtc = RTC;
|
||||
|
||||
/* Asynchronous part of preloader. Set it to maximum value. */
|
||||
uint32_t prediv_a = 0x7F;
|
||||
|
||||
/* Disable write protection. */
|
||||
RTCD1.id_rtc->WPR = 0xCA;
|
||||
RTCD1.id_rtc->WPR = 0x53;
|
||||
|
||||
/* If calendar not init yet. */
|
||||
if (!(RTC->ISR & RTC_ISR_INITS)){
|
||||
rtc_lld_enter_init();
|
||||
|
||||
/* Prescaler register must be written in two SEPARATE writes. */
|
||||
prediv_a = (prediv_a << 16) |
|
||||
(((STM32_RTCCLK / (prediv_a + 1)) - 1) & 0x7FFF);
|
||||
RTCD1.id_rtc->PRER = prediv_a;
|
||||
RTCD1.id_rtc->PRER = prediv_a;
|
||||
rtc_lld_exit_init();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set current time.
|
||||
* @note Fractional part will be silently ignored. There is no possibility
|
||||
* to set it on STM32 platform.
|
||||
*
|
||||
* @param[in] rtcp pointer to RTC driver structure
|
||||
* @param[in] timespec pointer to a @p RTCTime structure
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
void rtc_lld_set_time(RTCDriver *rtcp, const RTCTime *timespec) {
|
||||
(void)rtcp;
|
||||
|
||||
rtc_lld_enter_init();
|
||||
if (timespec->h12)
|
||||
RTCD1.id_rtc->CR |= RTC_CR_FMT;
|
||||
else
|
||||
RTCD1.id_rtc->CR &= ~RTC_CR_FMT;
|
||||
RTCD1.id_rtc->TR = timespec->tv_time;
|
||||
RTCD1.id_rtc->DR = timespec->tv_date;
|
||||
rtc_lld_exit_init();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get current time.
|
||||
*
|
||||
* @param[in] rtcp pointer to RTC driver structure
|
||||
* @param[out] timespec pointer to a @p RTCTime structure
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
void rtc_lld_get_time(RTCDriver *rtcp, RTCTime *timespec) {
|
||||
(void)rtcp;
|
||||
|
||||
rtc_lld_apb1_sync();
|
||||
|
||||
#if STM32_RTC_HAS_SUBSECONDS
|
||||
{
|
||||
uint32_t prer = RTCD1.id_rtc->PRER & 0x7FFF;
|
||||
uint32_t ssr = RTCD1.id_rtc->SSR;
|
||||
timespec->tv_msec = (1000 * (prer - ssr)) / (prer + 1);
|
||||
}
|
||||
#endif /* STM32_RTC_HAS_SUBSECONDS */
|
||||
timespec->tv_time = RTCD1.id_rtc->TR;
|
||||
timespec->tv_date = RTCD1.id_rtc->DR;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set alarm time.
|
||||
*
|
||||
* @note Default value after BKP domain reset for both comparators is 0.
|
||||
* @note Function does not performs any checks of alarm time validity.
|
||||
*
|
||||
* @param[in] rtcp pointer to RTC driver structure
|
||||
* @param[in] alarm alarm identifier starting from zero
|
||||
* @param[in] alarmspec pointer to a @p RTCAlarm structure
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
void rtc_lld_set_alarm(RTCDriver *rtcp,
|
||||
rtcalarm_t alarm,
|
||||
const RTCAlarm *alarmspec) {
|
||||
|
||||
if (alarm == 0) {
|
||||
if (alarmspec != NULL){
|
||||
rtcp->id_rtc->CR &= ~RTC_CR_ALRAE;
|
||||
while(!(rtcp->id_rtc->ISR & RTC_ISR_ALRAWF))
|
||||
;
|
||||
rtcp->id_rtc->ALRMAR = alarmspec->tv_datetime;
|
||||
rtcp->id_rtc->CR |= RTC_CR_ALRAE;
|
||||
rtcp->id_rtc->CR |= RTC_CR_ALRAIE;
|
||||
}
|
||||
else {
|
||||
rtcp->id_rtc->CR &= ~RTC_CR_ALRAIE;
|
||||
rtcp->id_rtc->CR &= ~RTC_CR_ALRAE;
|
||||
}
|
||||
}
|
||||
#if RTC_ALARMS == 2
|
||||
else{
|
||||
if (alarmspec != NULL){
|
||||
rtcp->id_rtc->CR &= ~RTC_CR_ALRBE;
|
||||
while(!(rtcp->id_rtc->ISR & RTC_ISR_ALRBWF))
|
||||
;
|
||||
rtcp->id_rtc->ALRMBR = alarmspec->tv_datetime;
|
||||
rtcp->id_rtc->CR |= RTC_CR_ALRBE;
|
||||
rtcp->id_rtc->CR |= RTC_CR_ALRBIE;
|
||||
}
|
||||
else {
|
||||
rtcp->id_rtc->CR &= ~RTC_CR_ALRBIE;
|
||||
rtcp->id_rtc->CR &= ~RTC_CR_ALRBE;
|
||||
}
|
||||
}
|
||||
#endif /* RTC_ALARMS == 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get alarm time.
|
||||
*
|
||||
* @param[in] rtcp pointer to RTC driver structure
|
||||
* @param[in] alarm alarm identifier starting from zero
|
||||
* @param[out] alarmspec pointer to a @p RTCAlarm structure
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
void rtc_lld_get_alarm(RTCDriver *rtcp,
|
||||
rtcalarm_t alarm,
|
||||
RTCAlarm *alarmspec) {
|
||||
|
||||
if (alarm == 0)
|
||||
alarmspec->tv_datetime = rtcp->id_rtc->ALRMAR;
|
||||
#if RTC_ALARMS == 2
|
||||
else
|
||||
alarmspec->tv_datetime = rtcp->id_rtc->ALRMBR;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets time of periodic wakeup.
|
||||
*
|
||||
* @note Default value after BKP domain reset is 0x0000FFFF
|
||||
*
|
||||
* @param[in] rtcp pointer to RTC driver structure
|
||||
* @param[in] wakeupspec pointer to a @p RTCWakeup structure
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
#if RTC_HAS_PERIODIC_WAKEUPS
|
||||
void rtcSetPeriodicWakeup_v2(RTCDriver *rtcp, const RTCWakeup *wakeupspec) {
|
||||
|
||||
if (wakeupspec != NULL){
|
||||
chDbgCheck((wakeupspec->wakeup != 0x30000),
|
||||
"rtc_lld_set_periodic_wakeup, forbidden combination");
|
||||
|
||||
rtcp->id_rtc->CR &= ~RTC_CR_WUTE;
|
||||
while(!(rtcp->id_rtc->ISR & RTC_ISR_WUTWF))
|
||||
;
|
||||
rtcp->id_rtc->WUTR = wakeupspec->wakeup & 0xFFFF;
|
||||
rtcp->id_rtc->CR = (wakeupspec->wakeup >> 16) & 0x7;
|
||||
rtcp->id_rtc->CR |= RTC_CR_WUTIE;
|
||||
rtcp->id_rtc->CR |= RTC_CR_WUTE;
|
||||
}
|
||||
else {
|
||||
rtcp->id_rtc->CR &= ~RTC_CR_WUTIE;
|
||||
rtcp->id_rtc->CR &= ~RTC_CR_WUTE;
|
||||
}
|
||||
}
|
||||
#endif /* RTC_HAS_PERIODIC_WAKEUPS */
|
||||
|
||||
/**
|
||||
* @brief Gets time of periodic wakeup.
|
||||
*
|
||||
* @note Default value after BKP domain reset is 0x0000FFFF
|
||||
*
|
||||
* @param[in] rtcp pointer to RTC driver structure
|
||||
* @param[out] wakeupspec pointer to a @p RTCWakeup structure
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
#if RTC_HAS_PERIODIC_WAKEUPS
|
||||
void rtcGetPeriodicWakeup_v2(RTCDriver *rtcp, RTCWakeup *wakeupspec) {
|
||||
|
||||
wakeupspec->wakeup = 0;
|
||||
wakeupspec->wakeup |= rtcp->id_rtc->WUTR;
|
||||
wakeupspec->wakeup |= (((uint32_t)rtcp->id_rtc->CR) & 0x7) << 16;
|
||||
}
|
||||
#endif /* RTC_HAS_PERIODIC_WAKEUPS */
|
||||
|
||||
/**
|
||||
* @brief Get current time in format suitable for usage in FatFS.
|
||||
*
|
||||
* @param[in] rtcp pointer to RTC driver structure
|
||||
* @return FAT time value.
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
uint32_t rtc_lld_get_time_fat(RTCDriver *rtcp) {
|
||||
uint32_t fattime;
|
||||
RTCTime timespec;
|
||||
uint32_t tv_time;
|
||||
uint32_t tv_date;
|
||||
uint32_t v;
|
||||
|
||||
chSysLock();
|
||||
rtcGetTimeI(rtcp, ×pec);
|
||||
chSysUnlock();
|
||||
|
||||
tv_time = timespec.tv_time;
|
||||
tv_date = timespec.tv_date;
|
||||
|
||||
v = (tv_time & RTC_TR_SU) >> RTC_TR_SU_OFFSET;
|
||||
v += ((tv_time & RTC_TR_ST) >> RTC_TR_ST_OFFSET) * 10;
|
||||
fattime = v >> 1;
|
||||
|
||||
v = (tv_time & RTC_TR_MNU) >> RTC_TR_MNU_OFFSET;
|
||||
v += ((tv_time & RTC_TR_MNT) >> RTC_TR_MNT_OFFSET) * 10;
|
||||
fattime |= v << 5;
|
||||
|
||||
v = (tv_time & RTC_TR_HU) >> RTC_TR_HU_OFFSET;
|
||||
v += ((tv_time & RTC_TR_HT) >> RTC_TR_HT_OFFSET) * 10;
|
||||
v += 12 * ((tv_time & RTC_TR_PM) >> RTC_TR_PM_OFFSET);
|
||||
fattime |= v << 11;
|
||||
|
||||
v = (tv_date & RTC_DR_DU) >> RTC_DR_DU_OFFSET;
|
||||
v += ((tv_date & RTC_DR_DT) >> RTC_DR_DT_OFFSET) * 10;
|
||||
fattime |= v << 16;
|
||||
|
||||
v = (tv_date & RTC_DR_MU) >> RTC_DR_MU_OFFSET;
|
||||
v += ((tv_date & RTC_DR_MT) >> RTC_DR_MT_OFFSET) * 10;
|
||||
fattime |= v << 21;
|
||||
|
||||
v = (tv_date & RTC_DR_YU) >> RTC_DR_YU_OFFSET;
|
||||
v += ((tv_date & RTC_DR_YT) >> RTC_DR_YT_OFFSET) * 10;
|
||||
v += 2000 - 1900 - 80;
|
||||
fattime |= v << 25;
|
||||
|
||||
return fattime;
|
||||
}
|
||||
|
||||
#endif /* HAL_USE_RTC */
|
||||
|
||||
/** @} */
|
||||
+225
@@ -0,0 +1,225 @@
|
||||
/*
|
||||
ChibiOS/RT - Copyright (C) 2006-2013 Giovanni Di Sirio
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
/*
|
||||
Concepts and parts of this file have been contributed by Uladzimir Pylinsky
|
||||
aka barthess.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file STM32/RTCv2/rtc_lld.h
|
||||
* @brief RTC low level driver header.
|
||||
*
|
||||
* @addtogroup RTC
|
||||
* @{
|
||||
*/
|
||||
|
||||
#ifndef _RTC_LLD_H_
|
||||
#define _RTC_LLD_H_
|
||||
|
||||
#if HAL_USE_RTC || defined(__DOXYGEN__)
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver constants. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Two alarm comparators available on STM32F4x and STM32F2x.
|
||||
*/
|
||||
#if !defined(STM32F0XX)
|
||||
#define RTC_ALARMS 2
|
||||
#else
|
||||
#define RTC_ALARMS 1
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief STM32F0x has no periodic wakeups.
|
||||
*/
|
||||
#if !defined(STM32F0XX)
|
||||
#define RTC_HAS_PERIODIC_WAKEUPS TRUE
|
||||
#else
|
||||
#define RTC_HAS_PERIODIC_WAKEUPS FALSE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Data offsets in RTC date and time registers.
|
||||
*/
|
||||
#define RTC_TR_PM_OFFSET 22
|
||||
#define RTC_TR_HT_OFFSET 20
|
||||
#define RTC_TR_HU_OFFSET 16
|
||||
#define RTC_TR_MNT_OFFSET 12
|
||||
#define RTC_TR_MNU_OFFSET 8
|
||||
#define RTC_TR_ST_OFFSET 4
|
||||
#define RTC_TR_SU_OFFSET 0
|
||||
|
||||
#define RTC_DR_YT_OFFSET 20
|
||||
#define RTC_DR_YU_OFFSET 16
|
||||
#define RTC_DR_WDU_OFFSET 13
|
||||
#define RTC_DR_MT_OFFSET 12
|
||||
#define RTC_DR_MU_OFFSET 8
|
||||
#define RTC_DR_DT_OFFSET 4
|
||||
#define RTC_DR_DU_OFFSET 0
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver pre-compile time settings. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Derived constants and error checks. */
|
||||
/*===========================================================================*/
|
||||
|
||||
#if HAL_USE_RTC && !STM32_HAS_RTC
|
||||
#error "RTC not present in the selected device"
|
||||
#endif
|
||||
|
||||
#if !(STM32_RTCSEL == STM32_RTCSEL_LSE) && \
|
||||
!(STM32_RTCSEL == STM32_RTCSEL_LSI) && \
|
||||
!(STM32_RTCSEL == STM32_RTCSEL_HSEDIV)
|
||||
#error "invalid source selected for RTC clock"
|
||||
#endif
|
||||
|
||||
#if !defined(RTC_USE_INTERRUPTS) || defined(__DOXYGEN__)
|
||||
#define RTC_USE_INTERRUPTS FALSE
|
||||
#endif
|
||||
|
||||
#if defined(STM32_PCLK1) /* For devices without STM32_PCLK1 (STM32F0xx) */
|
||||
#if STM32_PCLK1 < (STM32_RTCCLK * 7)
|
||||
#error "STM32_PCLK1 frequency is too low to handle RTC without ugly workaround"
|
||||
#endif
|
||||
#endif /* defined(STM32_PCLK1) */
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver data structures and types. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Type of a structure representing an RTC alarm time stamp.
|
||||
*/
|
||||
typedef struct RTCAlarm RTCAlarm;
|
||||
|
||||
/**
|
||||
* @brief Type of a structure representing an RTC wakeup period.
|
||||
*/
|
||||
typedef struct RTCWakeup RTCWakeup;
|
||||
|
||||
/**
|
||||
* @brief Type of a structure representing an RTC callbacks config.
|
||||
*/
|
||||
typedef struct RTCCallbackConfig RTCCallbackConfig;
|
||||
|
||||
/**
|
||||
* @brief Type of an RTC alarm.
|
||||
* @details Meaningful on platforms with more than 1 alarm comparator.
|
||||
*/
|
||||
typedef uint32_t rtcalarm_t;
|
||||
|
||||
/**
|
||||
* @brief Structure representing an RTC time stamp.
|
||||
*/
|
||||
struct RTCTime {
|
||||
/**
|
||||
* @brief RTC date register in STM32 BCD format.
|
||||
*/
|
||||
uint32_t tv_date;
|
||||
/**
|
||||
* @brief RTC time register in STM32 BCD format.
|
||||
*/
|
||||
uint32_t tv_time;
|
||||
/**
|
||||
* @brief Set this to TRUE to use 12 hour notation.
|
||||
*/
|
||||
bool_t h12;
|
||||
/**
|
||||
* @brief Fractional part of time.
|
||||
*/
|
||||
#if STM32_RTC_HAS_SUBSECONDS
|
||||
uint32_t tv_msec;
|
||||
#endif
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Structure representing an RTC alarm time stamp.
|
||||
*/
|
||||
struct RTCAlarm {
|
||||
/**
|
||||
* @brief Date and time of alarm in STM32 BCD.
|
||||
*/
|
||||
uint32_t tv_datetime;
|
||||
};
|
||||
|
||||
#if RTC_HAS_PERIODIC_WAKEUPS
|
||||
/**
|
||||
* @brief Structure representing an RTC periodic wakeup period.
|
||||
*/
|
||||
struct RTCWakeup {
|
||||
/**
|
||||
* @brief RTC WUTR register.
|
||||
* @details Bits [15:0] contain value of WUTR register
|
||||
* Bits [18:16] contain value of WUCKSEL bits in CR register
|
||||
*
|
||||
* @note ((WUTR == 0) || (WUCKSEL == 3)) is forbidden combination.
|
||||
*/
|
||||
uint32_t wakeup;
|
||||
};
|
||||
#endif /* RTC_HAS_PERIODIC_WAKEUPS */
|
||||
|
||||
/**
|
||||
* @brief Structure representing an RTC driver.
|
||||
*/
|
||||
struct RTCDriver{
|
||||
/**
|
||||
* @brief Pointer to the RTC registers block.
|
||||
*/
|
||||
RTC_TypeDef *id_rtc;
|
||||
};
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver macros. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* External declarations. */
|
||||
/*===========================================================================*/
|
||||
|
||||
#if !defined(__DOXYGEN__)
|
||||
extern RTCDriver RTCD1;
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
void rtc_lld_init(void);
|
||||
void rtc_lld_set_time(RTCDriver *rtcp, const RTCTime *timespec);
|
||||
void rtc_lld_get_time(RTCDriver *rtcp, RTCTime *timespec);
|
||||
void rtc_lld_set_alarm(RTCDriver *rtcp,
|
||||
rtcalarm_t alarm,
|
||||
const RTCAlarm *alarmspec);
|
||||
void rtc_lld_get_alarm(RTCDriver *rtcp,
|
||||
rtcalarm_t alarm,
|
||||
RTCAlarm *alarmspec);
|
||||
#if RTC_HAS_PERIODIC_WAKEUPS
|
||||
void rtcSetPeriodicWakeup_v2(RTCDriver *rtcp, const RTCWakeup *wakeupspec);
|
||||
void rtcGetPeriodicWakeup_v2(RTCDriver *rtcp, RTCWakeup *wakeupspec);
|
||||
#endif /* RTC_HAS_PERIODIC_WAKEUPS */
|
||||
uint32_t rtc_lld_get_time_fat(RTCDriver *rtcp);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* HAL_USE_RTC */
|
||||
|
||||
#endif /* _RTC_LLD_H_ */
|
||||
|
||||
/** @} */
|
||||
+636
@@ -0,0 +1,636 @@
|
||||
/*
|
||||
ChibiOS/RT - Copyright (C) 2006-2013 Giovanni Di Sirio
|
||||
|
||||
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 STM32/SPIv1/spi_lld.c
|
||||
* @brief STM32 SPI subsystem low level driver source.
|
||||
*
|
||||
* @addtogroup SPI
|
||||
* @{
|
||||
*/
|
||||
|
||||
#include "ch.h"
|
||||
#include "hal.h"
|
||||
|
||||
#if HAL_USE_SPI || defined(__DOXYGEN__)
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local definitions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
#define SPI1_RX_DMA_CHANNEL \
|
||||
STM32_DMA_GETCHANNEL(STM32_SPI_SPI1_RX_DMA_STREAM, \
|
||||
STM32_SPI1_RX_DMA_CHN)
|
||||
|
||||
#define SPI1_TX_DMA_CHANNEL \
|
||||
STM32_DMA_GETCHANNEL(STM32_SPI_SPI1_TX_DMA_STREAM, \
|
||||
STM32_SPI1_TX_DMA_CHN)
|
||||
|
||||
#define SPI2_RX_DMA_CHANNEL \
|
||||
STM32_DMA_GETCHANNEL(STM32_SPI_SPI2_RX_DMA_STREAM, \
|
||||
STM32_SPI2_RX_DMA_CHN)
|
||||
|
||||
#define SPI2_TX_DMA_CHANNEL \
|
||||
STM32_DMA_GETCHANNEL(STM32_SPI_SPI2_TX_DMA_STREAM, \
|
||||
STM32_SPI2_TX_DMA_CHN)
|
||||
|
||||
#define SPI3_RX_DMA_CHANNEL \
|
||||
STM32_DMA_GETCHANNEL(STM32_SPI_SPI3_RX_DMA_STREAM, \
|
||||
STM32_SPI3_RX_DMA_CHN)
|
||||
|
||||
#define SPI3_TX_DMA_CHANNEL \
|
||||
STM32_DMA_GETCHANNEL(STM32_SPI_SPI3_TX_DMA_STREAM, \
|
||||
STM32_SPI3_TX_DMA_CHN)
|
||||
|
||||
#define SPI4_RX_DMA_CHANNEL \
|
||||
STM32_DMA_GETCHANNEL(STM32_SPI_SPI4_RX_DMA_STREAM, \
|
||||
STM32_SPI4_RX_DMA_CHN)
|
||||
|
||||
#define SPI4_TX_DMA_CHANNEL \
|
||||
STM32_DMA_GETCHANNEL(STM32_SPI_SPI4_TX_DMA_STREAM, \
|
||||
STM32_SPI4_TX_DMA_CHN)
|
||||
|
||||
#define SPI5_RX_DMA_CHANNEL \
|
||||
STM32_DMA_GETCHANNEL(STM32_SPI_SPI5_RX_DMA_STREAM, \
|
||||
STM32_SPI5_RX_DMA_CHN)
|
||||
|
||||
#define SPI5_TX_DMA_CHANNEL \
|
||||
STM32_DMA_GETCHANNEL(STM32_SPI_SPI5_TX_DMA_STREAM, \
|
||||
STM32_SPI5_TX_DMA_CHN)
|
||||
|
||||
#define SPI6_RX_DMA_CHANNEL \
|
||||
STM32_DMA_GETCHANNEL(STM32_SPI_SPI6_RX_DMA_STREAM, \
|
||||
STM32_SPI6_RX_DMA_CHN)
|
||||
|
||||
#define SPI6_TX_DMA_CHANNEL \
|
||||
STM32_DMA_GETCHANNEL(STM32_SPI_SPI6_TX_DMA_STREAM, \
|
||||
STM32_SPI6_TX_DMA_CHN)
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver exported variables. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/** @brief SPI1 driver identifier.*/
|
||||
#if STM32_SPI_USE_SPI1 || defined(__DOXYGEN__)
|
||||
SPIDriver SPID1;
|
||||
#endif
|
||||
|
||||
/** @brief SPI2 driver identifier.*/
|
||||
#if STM32_SPI_USE_SPI2 || defined(__DOXYGEN__)
|
||||
SPIDriver SPID2;
|
||||
#endif
|
||||
|
||||
/** @brief SPI3 driver identifier.*/
|
||||
#if STM32_SPI_USE_SPI3 || defined(__DOXYGEN__)
|
||||
SPIDriver SPID3;
|
||||
#endif
|
||||
|
||||
/** @brief SPI4 driver identifier.*/
|
||||
#if STM32_SPI_USE_SPI4 || defined(__DOXYGEN__)
|
||||
SPIDriver SPID4;
|
||||
#endif
|
||||
|
||||
/** @brief SPI5 driver identifier.*/
|
||||
#if STM32_SPI_USE_SPI5 || defined(__DOXYGEN__)
|
||||
SPIDriver SPID5;
|
||||
#endif
|
||||
|
||||
/** @brief SPI6 driver identifier.*/
|
||||
#if STM32_SPI_USE_SPI6 || defined(__DOXYGEN__)
|
||||
SPIDriver SPID6;
|
||||
#endif
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local variables and types. */
|
||||
/*===========================================================================*/
|
||||
|
||||
static uint16_t dummytx;
|
||||
static uint16_t dummyrx;
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Shared end-of-rx service routine.
|
||||
*
|
||||
* @param[in] spip pointer to the @p SPIDriver object
|
||||
* @param[in] flags pre-shifted content of the ISR register
|
||||
*/
|
||||
static void spi_lld_serve_rx_interrupt(SPIDriver *spip, uint32_t flags) {
|
||||
|
||||
/* DMA errors handling.*/
|
||||
#if defined(STM32_SPI_DMA_ERROR_HOOK)
|
||||
if ((flags & (STM32_DMA_ISR_TEIF | STM32_DMA_ISR_DMEIF)) != 0) {
|
||||
STM32_SPI_DMA_ERROR_HOOK(spip);
|
||||
}
|
||||
#else
|
||||
(void)flags;
|
||||
#endif
|
||||
|
||||
/* Stop everything.*/
|
||||
dmaStreamDisable(spip->dmatx);
|
||||
dmaStreamDisable(spip->dmarx);
|
||||
|
||||
/* Portable SPI ISR code defined in the high level driver, note, it is
|
||||
a macro.*/
|
||||
_spi_isr_code(spip);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Shared end-of-tx service routine.
|
||||
*
|
||||
* @param[in] spip pointer to the @p SPIDriver object
|
||||
* @param[in] flags pre-shifted content of the ISR register
|
||||
*/
|
||||
static void spi_lld_serve_tx_interrupt(SPIDriver *spip, uint32_t flags) {
|
||||
|
||||
/* DMA errors handling.*/
|
||||
#if defined(STM32_SPI_DMA_ERROR_HOOK)
|
||||
(void)spip;
|
||||
if ((flags & (STM32_DMA_ISR_TEIF | STM32_DMA_ISR_DMEIF)) != 0) {
|
||||
STM32_SPI_DMA_ERROR_HOOK(spip);
|
||||
}
|
||||
#else
|
||||
(void)spip;
|
||||
(void)flags;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver interrupt handlers. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver exported functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Low level SPI driver initialization.
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void spi_lld_init(void) {
|
||||
|
||||
dummytx = 0xFFFF;
|
||||
|
||||
#if STM32_SPI_USE_SPI1
|
||||
spiObjectInit(&SPID1);
|
||||
SPID1.spi = SPI1;
|
||||
SPID1.dmarx = STM32_DMA_STREAM(STM32_SPI_SPI1_RX_DMA_STREAM);
|
||||
SPID1.dmatx = STM32_DMA_STREAM(STM32_SPI_SPI1_TX_DMA_STREAM);
|
||||
SPID1.rxdmamode = STM32_DMA_CR_CHSEL(SPI1_RX_DMA_CHANNEL) |
|
||||
STM32_DMA_CR_PL(STM32_SPI_SPI1_DMA_PRIORITY) |
|
||||
STM32_DMA_CR_DIR_P2M |
|
||||
STM32_DMA_CR_TCIE |
|
||||
STM32_DMA_CR_DMEIE |
|
||||
STM32_DMA_CR_TEIE;
|
||||
SPID1.txdmamode = STM32_DMA_CR_CHSEL(SPI1_TX_DMA_CHANNEL) |
|
||||
STM32_DMA_CR_PL(STM32_SPI_SPI1_DMA_PRIORITY) |
|
||||
STM32_DMA_CR_DIR_M2P |
|
||||
STM32_DMA_CR_DMEIE |
|
||||
STM32_DMA_CR_TEIE;
|
||||
#endif
|
||||
|
||||
#if STM32_SPI_USE_SPI2
|
||||
spiObjectInit(&SPID2);
|
||||
SPID2.spi = SPI2;
|
||||
SPID2.dmarx = STM32_DMA_STREAM(STM32_SPI_SPI2_RX_DMA_STREAM);
|
||||
SPID2.dmatx = STM32_DMA_STREAM(STM32_SPI_SPI2_TX_DMA_STREAM);
|
||||
SPID2.rxdmamode = STM32_DMA_CR_CHSEL(SPI2_RX_DMA_CHANNEL) |
|
||||
STM32_DMA_CR_PL(STM32_SPI_SPI2_DMA_PRIORITY) |
|
||||
STM32_DMA_CR_DIR_P2M |
|
||||
STM32_DMA_CR_TCIE |
|
||||
STM32_DMA_CR_DMEIE |
|
||||
STM32_DMA_CR_TEIE;
|
||||
SPID2.txdmamode = STM32_DMA_CR_CHSEL(SPI2_TX_DMA_CHANNEL) |
|
||||
STM32_DMA_CR_PL(STM32_SPI_SPI2_DMA_PRIORITY) |
|
||||
STM32_DMA_CR_DIR_M2P |
|
||||
STM32_DMA_CR_DMEIE |
|
||||
STM32_DMA_CR_TEIE;
|
||||
#endif
|
||||
|
||||
#if STM32_SPI_USE_SPI3
|
||||
spiObjectInit(&SPID3);
|
||||
SPID3.spi = SPI3;
|
||||
SPID3.dmarx = STM32_DMA_STREAM(STM32_SPI_SPI3_RX_DMA_STREAM);
|
||||
SPID3.dmatx = STM32_DMA_STREAM(STM32_SPI_SPI3_TX_DMA_STREAM);
|
||||
SPID3.rxdmamode = STM32_DMA_CR_CHSEL(SPI3_RX_DMA_CHANNEL) |
|
||||
STM32_DMA_CR_PL(STM32_SPI_SPI3_DMA_PRIORITY) |
|
||||
STM32_DMA_CR_DIR_P2M |
|
||||
STM32_DMA_CR_TCIE |
|
||||
STM32_DMA_CR_DMEIE |
|
||||
STM32_DMA_CR_TEIE;
|
||||
SPID3.txdmamode = STM32_DMA_CR_CHSEL(SPI3_TX_DMA_CHANNEL) |
|
||||
STM32_DMA_CR_PL(STM32_SPI_SPI3_DMA_PRIORITY) |
|
||||
STM32_DMA_CR_DIR_M2P |
|
||||
STM32_DMA_CR_DMEIE |
|
||||
STM32_DMA_CR_TEIE;
|
||||
#endif
|
||||
|
||||
#if STM32_SPI_USE_SPI4
|
||||
spiObjectInit(&SPID4);
|
||||
SPID4.spi = SPI4;
|
||||
SPID4.dmarx = STM32_DMA_STREAM(STM32_SPI_SPI4_RX_DMA_STREAM);
|
||||
SPID4.dmatx = STM32_DMA_STREAM(STM32_SPI_SPI4_TX_DMA_STREAM);
|
||||
SPID4.rxdmamode = STM32_DMA_CR_CHSEL(SPI4_RX_DMA_CHANNEL) |
|
||||
STM32_DMA_CR_PL(STM32_SPI_SPI4_DMA_PRIORITY) |
|
||||
STM32_DMA_CR_DIR_P2M |
|
||||
STM32_DMA_CR_TCIE |
|
||||
STM32_DMA_CR_DMEIE |
|
||||
STM32_DMA_CR_TEIE;
|
||||
SPID4.txdmamode = STM32_DMA_CR_CHSEL(SPI4_TX_DMA_CHANNEL) |
|
||||
STM32_DMA_CR_PL(STM32_SPI_SPI4_DMA_PRIORITY) |
|
||||
STM32_DMA_CR_DIR_M2P |
|
||||
STM32_DMA_CR_DMEIE |
|
||||
STM32_DMA_CR_TEIE;
|
||||
#endif
|
||||
|
||||
#if STM32_SPI_USE_SPI5
|
||||
spiObjectInit(&SPID5);
|
||||
SPID5.spi = SPI5;
|
||||
SPID5.dmarx = STM32_DMA_STREAM(STM32_SPI_SPI5_RX_DMA_STREAM);
|
||||
SPID5.dmatx = STM32_DMA_STREAM(STM32_SPI_SPI5_TX_DMA_STREAM);
|
||||
SPID5.rxdmamode = STM32_DMA_CR_CHSEL(SPI5_RX_DMA_CHANNEL) |
|
||||
STM32_DMA_CR_PL(STM32_SPI_SPI5_DMA_PRIORITY) |
|
||||
STM32_DMA_CR_DIR_P2M |
|
||||
STM32_DMA_CR_TCIE |
|
||||
STM32_DMA_CR_DMEIE |
|
||||
STM32_DMA_CR_TEIE;
|
||||
SPID5.txdmamode = STM32_DMA_CR_CHSEL(SPI5_TX_DMA_CHANNEL) |
|
||||
STM32_DMA_CR_PL(STM32_SPI_SPI5_DMA_PRIORITY) |
|
||||
STM32_DMA_CR_DIR_M2P |
|
||||
STM32_DMA_CR_DMEIE |
|
||||
STM32_DMA_CR_TEIE;
|
||||
#endif
|
||||
|
||||
#if STM32_SPI_USE_SPI6
|
||||
spiObjectInit(&SPID6);
|
||||
SPID6.spi = SPI6;
|
||||
SPID6.dmarx = STM32_DMA_STREAM(STM32_SPI_SPI6_RX_DMA_STREAM);
|
||||
SPID6.dmatx = STM32_DMA_STREAM(STM32_SPI_SPI6_TX_DMA_STREAM);
|
||||
SPID6.rxdmamode = STM32_DMA_CR_CHSEL(SPI6_RX_DMA_CHANNEL) |
|
||||
STM32_DMA_CR_PL(STM32_SPI_SPI6_DMA_PRIORITY) |
|
||||
STM32_DMA_CR_DIR_P2M |
|
||||
STM32_DMA_CR_TCIE |
|
||||
STM32_DMA_CR_DMEIE |
|
||||
STM32_DMA_CR_TEIE;
|
||||
SPID6.txdmamode = STM32_DMA_CR_CHSEL(SPI6_TX_DMA_CHANNEL) |
|
||||
STM32_DMA_CR_PL(STM32_SPI_SPI6_DMA_PRIORITY) |
|
||||
STM32_DMA_CR_DIR_M2P |
|
||||
STM32_DMA_CR_DMEIE |
|
||||
STM32_DMA_CR_TEIE;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Configures and activates the SPI peripheral.
|
||||
*
|
||||
* @param[in] spip pointer to the @p SPIDriver object
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void spi_lld_start(SPIDriver *spip) {
|
||||
|
||||
/* If in stopped state then enables the SPI and DMA clocks.*/
|
||||
if (spip->state == SPI_STOP) {
|
||||
#if STM32_SPI_USE_SPI1
|
||||
if (&SPID1 == spip) {
|
||||
bool_t b;
|
||||
b = dmaStreamAllocate(spip->dmarx,
|
||||
STM32_SPI_SPI1_IRQ_PRIORITY,
|
||||
(stm32_dmaisr_t)spi_lld_serve_rx_interrupt,
|
||||
(void *)spip);
|
||||
chDbgAssert(!b, "spi_lld_start(), #1", "stream already allocated");
|
||||
b = dmaStreamAllocate(spip->dmatx,
|
||||
STM32_SPI_SPI1_IRQ_PRIORITY,
|
||||
(stm32_dmaisr_t)spi_lld_serve_tx_interrupt,
|
||||
(void *)spip);
|
||||
chDbgAssert(!b, "spi_lld_start(), #2", "stream already allocated");
|
||||
rccEnableSPI1(FALSE);
|
||||
}
|
||||
#endif
|
||||
#if STM32_SPI_USE_SPI2
|
||||
if (&SPID2 == spip) {
|
||||
bool_t b;
|
||||
b = dmaStreamAllocate(spip->dmarx,
|
||||
STM32_SPI_SPI2_IRQ_PRIORITY,
|
||||
(stm32_dmaisr_t)spi_lld_serve_rx_interrupt,
|
||||
(void *)spip);
|
||||
chDbgAssert(!b, "spi_lld_start(), #3", "stream already allocated");
|
||||
b = dmaStreamAllocate(spip->dmatx,
|
||||
STM32_SPI_SPI2_IRQ_PRIORITY,
|
||||
(stm32_dmaisr_t)spi_lld_serve_tx_interrupt,
|
||||
(void *)spip);
|
||||
chDbgAssert(!b, "spi_lld_start(), #4", "stream already allocated");
|
||||
rccEnableSPI2(FALSE);
|
||||
}
|
||||
#endif
|
||||
#if STM32_SPI_USE_SPI3
|
||||
if (&SPID3 == spip) {
|
||||
bool_t b;
|
||||
b = dmaStreamAllocate(spip->dmarx,
|
||||
STM32_SPI_SPI3_IRQ_PRIORITY,
|
||||
(stm32_dmaisr_t)spi_lld_serve_rx_interrupt,
|
||||
(void *)spip);
|
||||
chDbgAssert(!b, "spi_lld_start(), #5", "stream already allocated");
|
||||
b = dmaStreamAllocate(spip->dmatx,
|
||||
STM32_SPI_SPI3_IRQ_PRIORITY,
|
||||
(stm32_dmaisr_t)spi_lld_serve_tx_interrupt,
|
||||
(void *)spip);
|
||||
chDbgAssert(!b, "spi_lld_start(), #6", "stream already allocated");
|
||||
rccEnableSPI3(FALSE);
|
||||
}
|
||||
#endif
|
||||
#if STM32_SPI_USE_SPI4
|
||||
if (&SPID4 == spip) {
|
||||
bool_t b;
|
||||
b = dmaStreamAllocate(spip->dmarx,
|
||||
STM32_SPI_SPI4_IRQ_PRIORITY,
|
||||
(stm32_dmaisr_t)spi_lld_serve_rx_interrupt,
|
||||
(void *)spip);
|
||||
chDbgAssert(!b, "spi_lld_start(), #7", "stream already allocated");
|
||||
b = dmaStreamAllocate(spip->dmatx,
|
||||
STM32_SPI_SPI4_IRQ_PRIORITY,
|
||||
(stm32_dmaisr_t)spi_lld_serve_tx_interrupt,
|
||||
(void *)spip);
|
||||
chDbgAssert(!b, "spi_lld_start(), #8", "stream already allocated");
|
||||
rccEnableSPI4(FALSE);
|
||||
}
|
||||
#endif
|
||||
#if STM32_SPI_USE_SPI5
|
||||
if (&SPID5 == spip) {
|
||||
bool_t b;
|
||||
b = dmaStreamAllocate(spip->dmarx,
|
||||
STM32_SPI_SPI5_IRQ_PRIORITY,
|
||||
(stm32_dmaisr_t)spi_lld_serve_rx_interrupt,
|
||||
(void *)spip);
|
||||
chDbgAssert(!b, "spi_lld_start(), #9", "stream already allocated");
|
||||
b = dmaStreamAllocate(spip->dmatx,
|
||||
STM32_SPI_SPI5_IRQ_PRIORITY,
|
||||
(stm32_dmaisr_t)spi_lld_serve_tx_interrupt,
|
||||
(void *)spip);
|
||||
chDbgAssert(!b, "spi_lld_start(), #10", "stream already allocated");
|
||||
rccEnableSPI5(FALSE);
|
||||
}
|
||||
#endif
|
||||
#if STM32_SPI_USE_SPI6
|
||||
if (&SPID6 == spip) {
|
||||
bool_t b;
|
||||
b = dmaStreamAllocate(spip->dmarx,
|
||||
STM32_SPI_SPI6_IRQ_PRIORITY,
|
||||
(stm32_dmaisr_t)spi_lld_serve_rx_interrupt,
|
||||
(void *)spip);
|
||||
chDbgAssert(!b, "spi_lld_start(), #11", "stream already allocated");
|
||||
b = dmaStreamAllocate(spip->dmatx,
|
||||
STM32_SPI_SPI6_IRQ_PRIORITY,
|
||||
(stm32_dmaisr_t)spi_lld_serve_tx_interrupt,
|
||||
(void *)spip);
|
||||
chDbgAssert(!b, "spi_lld_start(), #12", "stream already allocated");
|
||||
rccEnableSPI6(FALSE);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* DMA setup.*/
|
||||
dmaStreamSetPeripheral(spip->dmarx, &spip->spi->DR);
|
||||
dmaStreamSetPeripheral(spip->dmatx, &spip->spi->DR);
|
||||
}
|
||||
|
||||
/* Configuration-specific DMA setup.*/
|
||||
if ((spip->config->cr1 & SPI_CR1_DFF) == 0) {
|
||||
/* Frame width is 8 bits or smaller.*/
|
||||
spip->rxdmamode = (spip->rxdmamode & ~STM32_DMA_CR_SIZE_MASK) |
|
||||
STM32_DMA_CR_PSIZE_BYTE | STM32_DMA_CR_MSIZE_BYTE;
|
||||
spip->txdmamode = (spip->txdmamode & ~STM32_DMA_CR_SIZE_MASK) |
|
||||
STM32_DMA_CR_PSIZE_BYTE | STM32_DMA_CR_MSIZE_BYTE;
|
||||
}
|
||||
else {
|
||||
/* Frame width is larger than 8 bits.*/
|
||||
spip->rxdmamode = (spip->rxdmamode & ~STM32_DMA_CR_SIZE_MASK) |
|
||||
STM32_DMA_CR_PSIZE_HWORD | STM32_DMA_CR_MSIZE_HWORD;
|
||||
spip->txdmamode = (spip->txdmamode & ~STM32_DMA_CR_SIZE_MASK) |
|
||||
STM32_DMA_CR_PSIZE_HWORD | STM32_DMA_CR_MSIZE_HWORD;
|
||||
}
|
||||
/* SPI setup and enable.*/
|
||||
spip->spi->CR1 = 0;
|
||||
spip->spi->CR1 = spip->config->cr1 | SPI_CR1_MSTR | SPI_CR1_SSM |
|
||||
SPI_CR1_SSI;
|
||||
spip->spi->CR2 = SPI_CR2_SSOE | SPI_CR2_RXDMAEN | SPI_CR2_TXDMAEN;
|
||||
spip->spi->CR1 |= SPI_CR1_SPE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Deactivates the SPI peripheral.
|
||||
*
|
||||
* @param[in] spip pointer to the @p SPIDriver object
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void spi_lld_stop(SPIDriver *spip) {
|
||||
|
||||
/* If in ready state then disables the SPI clock.*/
|
||||
if (spip->state == SPI_READY) {
|
||||
|
||||
/* SPI disable.*/
|
||||
spip->spi->CR1 = 0;
|
||||
spip->spi->CR2 = 0;
|
||||
dmaStreamRelease(spip->dmarx);
|
||||
dmaStreamRelease(spip->dmatx);
|
||||
|
||||
#if STM32_SPI_USE_SPI1
|
||||
if (&SPID1 == spip)
|
||||
rccDisableSPI1(FALSE);
|
||||
#endif
|
||||
#if STM32_SPI_USE_SPI2
|
||||
if (&SPID2 == spip)
|
||||
rccDisableSPI2(FALSE);
|
||||
#endif
|
||||
#if STM32_SPI_USE_SPI3
|
||||
if (&SPID3 == spip)
|
||||
rccDisableSPI3(FALSE);
|
||||
#endif
|
||||
#if STM32_SPI_USE_SPI4
|
||||
if (&SPID4 == spip)
|
||||
rccDisableSPI4(FALSE);
|
||||
#endif
|
||||
#if STM32_SPI_USE_SPI5
|
||||
if (&SPID5 == spip)
|
||||
rccDisableSPI5(FALSE);
|
||||
#endif
|
||||
#if STM32_SPI_USE_SPI6
|
||||
if (&SPID6 == spip)
|
||||
rccDisableSPI6(FALSE);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Asserts the slave select signal and prepares for transfers.
|
||||
*
|
||||
* @param[in] spip pointer to the @p SPIDriver object
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void spi_lld_select(SPIDriver *spip) {
|
||||
|
||||
palClearPad(spip->config->ssport, spip->config->sspad);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Deasserts the slave select signal.
|
||||
* @details The previously selected peripheral is unselected.
|
||||
*
|
||||
* @param[in] spip pointer to the @p SPIDriver object
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void spi_lld_unselect(SPIDriver *spip) {
|
||||
|
||||
palSetPad(spip->config->ssport, spip->config->sspad);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Ignores data on the SPI bus.
|
||||
* @details This asynchronous function starts the transmission of a series of
|
||||
* idle words on the SPI bus and ignores the received data.
|
||||
* @post At the end of the operation the configured callback is invoked.
|
||||
*
|
||||
* @param[in] spip pointer to the @p SPIDriver object
|
||||
* @param[in] n number of words to be ignored
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void spi_lld_ignore(SPIDriver *spip, size_t n) {
|
||||
|
||||
dmaStreamSetMemory0(spip->dmarx, &dummyrx);
|
||||
dmaStreamSetTransactionSize(spip->dmarx, n);
|
||||
dmaStreamSetMode(spip->dmarx, spip->rxdmamode);
|
||||
|
||||
dmaStreamSetMemory0(spip->dmatx, &dummytx);
|
||||
dmaStreamSetTransactionSize(spip->dmatx, n);
|
||||
dmaStreamSetMode(spip->dmatx, spip->txdmamode);
|
||||
|
||||
dmaStreamEnable(spip->dmarx);
|
||||
dmaStreamEnable(spip->dmatx);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Exchanges data on the SPI bus.
|
||||
* @details This asynchronous function starts a simultaneous transmit/receive
|
||||
* operation.
|
||||
* @post At the end of the operation the configured callback is invoked.
|
||||
* @note The buffers are organized as uint8_t arrays for data sizes below or
|
||||
* equal to 8 bits else it is organized as uint16_t arrays.
|
||||
*
|
||||
* @param[in] spip pointer to the @p SPIDriver object
|
||||
* @param[in] n number of words to be exchanged
|
||||
* @param[in] txbuf the pointer to the transmit buffer
|
||||
* @param[out] rxbuf the pointer to the receive buffer
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void spi_lld_exchange(SPIDriver *spip, size_t n,
|
||||
const void *txbuf, void *rxbuf) {
|
||||
|
||||
dmaStreamSetMemory0(spip->dmarx, rxbuf);
|
||||
dmaStreamSetTransactionSize(spip->dmarx, n);
|
||||
dmaStreamSetMode(spip->dmarx, spip->rxdmamode| STM32_DMA_CR_MINC);
|
||||
|
||||
dmaStreamSetMemory0(spip->dmatx, txbuf);
|
||||
dmaStreamSetTransactionSize(spip->dmatx, n);
|
||||
dmaStreamSetMode(spip->dmatx, spip->txdmamode | STM32_DMA_CR_MINC);
|
||||
|
||||
dmaStreamEnable(spip->dmarx);
|
||||
dmaStreamEnable(spip->dmatx);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sends data over the SPI bus.
|
||||
* @details This asynchronous function starts a transmit operation.
|
||||
* @post At the end of the operation the configured callback is invoked.
|
||||
* @note The buffers are organized as uint8_t arrays for data sizes below or
|
||||
* equal to 8 bits else it is organized as uint16_t arrays.
|
||||
*
|
||||
* @param[in] spip pointer to the @p SPIDriver object
|
||||
* @param[in] n number of words to send
|
||||
* @param[in] txbuf the pointer to the transmit buffer
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void spi_lld_send(SPIDriver *spip, size_t n, const void *txbuf) {
|
||||
|
||||
dmaStreamSetMemory0(spip->dmarx, &dummyrx);
|
||||
dmaStreamSetTransactionSize(spip->dmarx, n);
|
||||
dmaStreamSetMode(spip->dmarx, spip->rxdmamode);
|
||||
|
||||
dmaStreamSetMemory0(spip->dmatx, txbuf);
|
||||
dmaStreamSetTransactionSize(spip->dmatx, n);
|
||||
dmaStreamSetMode(spip->dmatx, spip->txdmamode | STM32_DMA_CR_MINC);
|
||||
|
||||
dmaStreamEnable(spip->dmarx);
|
||||
dmaStreamEnable(spip->dmatx);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Receives data from the SPI bus.
|
||||
* @details This asynchronous function starts a receive operation.
|
||||
* @post At the end of the operation the configured callback is invoked.
|
||||
* @note The buffers are organized as uint8_t arrays for data sizes below or
|
||||
* equal to 8 bits else it is organized as uint16_t arrays.
|
||||
*
|
||||
* @param[in] spip pointer to the @p SPIDriver object
|
||||
* @param[in] n number of words to receive
|
||||
* @param[out] rxbuf the pointer to the receive buffer
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void spi_lld_receive(SPIDriver *spip, size_t n, void *rxbuf) {
|
||||
|
||||
dmaStreamSetMemory0(spip->dmarx, rxbuf);
|
||||
dmaStreamSetTransactionSize(spip->dmarx, n);
|
||||
dmaStreamSetMode(spip->dmarx, spip->rxdmamode | STM32_DMA_CR_MINC);
|
||||
|
||||
dmaStreamSetMemory0(spip->dmatx, &dummytx);
|
||||
dmaStreamSetTransactionSize(spip->dmatx, n);
|
||||
dmaStreamSetMode(spip->dmatx, spip->txdmamode);
|
||||
|
||||
dmaStreamEnable(spip->dmarx);
|
||||
dmaStreamEnable(spip->dmatx);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Exchanges one frame using a polled wait.
|
||||
* @details This synchronous function exchanges one frame using a polled
|
||||
* synchronization method. This function is useful when exchanging
|
||||
* small amount of data on high speed channels, usually in this
|
||||
* situation is much more efficient just wait for completion using
|
||||
* polling than suspending the thread waiting for an interrupt.
|
||||
*
|
||||
* @param[in] spip pointer to the @p SPIDriver object
|
||||
* @param[in] frame the data frame to send over the SPI bus
|
||||
* @return The received data frame from the SPI bus.
|
||||
*/
|
||||
uint16_t spi_lld_polled_exchange(SPIDriver *spip, uint16_t frame) {
|
||||
|
||||
spip->spi->DR = frame;
|
||||
while ((spip->spi->SR & SPI_SR_RXNE) == 0)
|
||||
;
|
||||
return spip->spi->DR;
|
||||
}
|
||||
|
||||
#endif /* HAL_USE_SPI */
|
||||
|
||||
/** @} */
|
||||
+628
@@ -0,0 +1,628 @@
|
||||
/*
|
||||
ChibiOS/RT - Copyright (C) 2006-2013 Giovanni Di Sirio
|
||||
|
||||
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 STM32/SPIv1/spi_lld.h
|
||||
* @brief STM32 SPI subsystem low level driver header.
|
||||
*
|
||||
* @addtogroup SPI
|
||||
* @{
|
||||
*/
|
||||
|
||||
#ifndef _SPI_LLD_H_
|
||||
#define _SPI_LLD_H_
|
||||
|
||||
#if HAL_USE_SPI || defined(__DOXYGEN__)
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver constants. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver pre-compile time settings. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @name Configuration options
|
||||
* @{
|
||||
*/
|
||||
/**
|
||||
* @brief SPI1 driver enable switch.
|
||||
* @details If set to @p TRUE the support for SPI1 is included.
|
||||
* @note The default is @p FALSE.
|
||||
*/
|
||||
#if !defined(STM32_SPI_USE_SPI1) || defined(__DOXYGEN__)
|
||||
#define STM32_SPI_USE_SPI1 FALSE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief SPI2 driver enable switch.
|
||||
* @details If set to @p TRUE the support for SPI2 is included.
|
||||
* @note The default is @p FALSE.
|
||||
*/
|
||||
#if !defined(STM32_SPI_USE_SPI2) || defined(__DOXYGEN__)
|
||||
#define STM32_SPI_USE_SPI2 FALSE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief SPI3 driver enable switch.
|
||||
* @details If set to @p TRUE the support for SPI3 is included.
|
||||
* @note The default is @p FALSE.
|
||||
*/
|
||||
#if !defined(STM32_SPI_USE_SPI3) || defined(__DOXYGEN__)
|
||||
#define STM32_SPI_USE_SPI3 FALSE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief SPI4 driver enable switch.
|
||||
* @details If set to @p TRUE the support for SPI4 is included.
|
||||
* @note The default is @p FALSE.
|
||||
*/
|
||||
#if !defined(STM32_SPI_USE_SPI4) || defined(__DOXYGEN__)
|
||||
#define STM32_SPI_USE_SPI4 FALSE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief SPI5 driver enable switch.
|
||||
* @details If set to @p TRUE the support for SPI5 is included.
|
||||
* @note The default is @p FALSE.
|
||||
*/
|
||||
#if !defined(STM32_SPI_USE_SPI5) || defined(__DOXYGEN__)
|
||||
#define STM32_SPI_USE_SPI5 FALSE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief SPI6 driver enable switch.
|
||||
* @details If set to @p TRUE the support for SPI6 is included.
|
||||
* @note The default is @p FALSE.
|
||||
*/
|
||||
#if !defined(STM32_SPI_USE_SPI6) || defined(__DOXYGEN__)
|
||||
#define STM32_SPI_USE_SPI6 FALSE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief SPI1 interrupt priority level setting.
|
||||
*/
|
||||
#if !defined(STM32_SPI_SPI1_IRQ_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define STM32_SPI_SPI1_IRQ_PRIORITY 10
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief SPI2 interrupt priority level setting.
|
||||
*/
|
||||
#if !defined(STM32_SPI_SPI2_IRQ_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define STM32_SPI_SPI2_IRQ_PRIORITY 10
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief SPI3 interrupt priority level setting.
|
||||
*/
|
||||
#if !defined(STM32_SPI_SPI3_IRQ_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define STM32_SPI_SPI3_IRQ_PRIORITY 10
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief SPI4 interrupt priority level setting.
|
||||
*/
|
||||
#if !defined(STM32_SPI_SPI4_IRQ_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define STM32_SPI_SPI4_IRQ_PRIORITY 10
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief SPI5 interrupt priority level setting.
|
||||
*/
|
||||
#if !defined(STM32_SPI_SPI5_IRQ_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define STM32_SPI_SPI5_IRQ_PRIORITY 10
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief SPI6 interrupt priority level setting.
|
||||
*/
|
||||
#if !defined(STM32_SPI_SPI6_IRQ_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define STM32_SPI_SPI6_IRQ_PRIORITY 10
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief SPI1 DMA priority (0..3|lowest..highest).
|
||||
* @note The priority level is used for both the TX and RX DMA streams but
|
||||
* because of the streams ordering the RX stream has always priority
|
||||
* over the TX stream.
|
||||
*/
|
||||
#if !defined(STM32_SPI_SPI1_DMA_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define STM32_SPI_SPI1_DMA_PRIORITY 1
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief SPI2 DMA priority (0..3|lowest..highest).
|
||||
* @note The priority level is used for both the TX and RX DMA streams but
|
||||
* because of the streams ordering the RX stream has always priority
|
||||
* over the TX stream.
|
||||
*/
|
||||
#if !defined(STM32_SPI_SPI2_DMA_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define STM32_SPI_SPI2_DMA_PRIORITY 1
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief SPI3 DMA priority (0..3|lowest..highest).
|
||||
* @note The priority level is used for both the TX and RX DMA streams but
|
||||
* because of the streams ordering the RX stream has always priority
|
||||
* over the TX stream.
|
||||
*/
|
||||
#if !defined(STM32_SPI_SPI3_DMA_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define STM32_SPI_SPI3_DMA_PRIORITY 1
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief SPI4 DMA priority (0..3|lowest..highest).
|
||||
* @note The priority level is used for both the TX and RX DMA streams but
|
||||
* because of the streams ordering the RX stream has always priority
|
||||
* over the TX stream.
|
||||
*/
|
||||
#if !defined(STM32_SPI_SPI4_DMA_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define STM32_SPI_SPI4_DMA_PRIORITY 1
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief SPI5 DMA priority (0..3|lowest..highest).
|
||||
* @note The priority level is used for both the TX and RX DMA streams but
|
||||
* because of the streams ordering the RX stream has always priority
|
||||
* over the TX stream.
|
||||
*/
|
||||
#if !defined(STM32_SPI_SPI5_DMA_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define STM32_SPI_SPI5_DMA_PRIORITY 1
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief SPI6 DMA priority (0..3|lowest..highest).
|
||||
* @note The priority level is used for both the TX and RX DMA streams but
|
||||
* because of the streams ordering the RX stream has always priority
|
||||
* over the TX stream.
|
||||
*/
|
||||
#if !defined(STM32_SPI_SPI6_DMA_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define STM32_SPI_SPI6_DMA_PRIORITY 1
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief SPI DMA error hook.
|
||||
*/
|
||||
#if !defined(STM32_SPI_DMA_ERROR_HOOK) || defined(__DOXYGEN__)
|
||||
#define STM32_SPI_DMA_ERROR_HOOK(spip) chSysHalt()
|
||||
#endif
|
||||
|
||||
#if STM32_ADVANCED_DMA || defined(__DOXYGEN__)
|
||||
|
||||
/**
|
||||
* @brief DMA stream used for SPI1 RX operations.
|
||||
* @note This option is only available on platforms with enhanced DMA.
|
||||
*/
|
||||
#if !defined(STM32_SPI_SPI1_RX_DMA_STREAM) || defined(__DOXYGEN__)
|
||||
#define STM32_SPI_SPI1_RX_DMA_STREAM STM32_DMA_STREAM_ID(2, 0)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief DMA stream used for SPI1 TX operations.
|
||||
* @note This option is only available on platforms with enhanced DMA.
|
||||
*/
|
||||
#if !defined(STM32_SPI_SPI1_TX_DMA_STREAM) || defined(__DOXYGEN__)
|
||||
#define STM32_SPI_SPI1_TX_DMA_STREAM STM32_DMA_STREAM_ID(2, 3)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief DMA stream used for SPI2 RX operations.
|
||||
* @note This option is only available on platforms with enhanced DMA.
|
||||
*/
|
||||
#if !defined(STM32_SPI_SPI2_RX_DMA_STREAM) || defined(__DOXYGEN__)
|
||||
#define STM32_SPI_SPI2_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 3)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief DMA stream used for SPI2 TX operations.
|
||||
* @note This option is only available on platforms with enhanced DMA.
|
||||
*/
|
||||
#if !defined(STM32_SPI_SPI2_TX_DMA_STREAM) || defined(__DOXYGEN__)
|
||||
#define STM32_SPI_SPI2_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 4)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief DMA stream used for SPI3 RX operations.
|
||||
* @note This option is only available on platforms with enhanced DMA.
|
||||
*/
|
||||
#if !defined(STM32_SPI_SPI3_RX_DMA_STREAM) || defined(__DOXYGEN__)
|
||||
#define STM32_SPI_SPI3_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 0)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief DMA stream used for SPI3 TX operations.
|
||||
* @note This option is only available on platforms with enhanced DMA.
|
||||
*/
|
||||
#if !defined(STM32_SPI_SPI3_TX_DMA_STREAM) || defined(__DOXYGEN__)
|
||||
#define STM32_SPI_SPI3_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 7)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief DMA stream used for SPI4 RX operations.
|
||||
* @note This option is only available on platforms with enhanced DMA.
|
||||
*/
|
||||
#if !defined(STM32_SPI_SPI4_RX_DMA_STREAM) || defined(__DOXYGEN__)
|
||||
#define STM32_SPI_SPI4_RX_DMA_STREAM STM32_DMA_STREAM_ID(2, 0)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief DMA stream used for SPI4 TX operations.
|
||||
* @note This option is only available on platforms with enhanced DMA.
|
||||
*/
|
||||
#if !defined(STM32_SPI_SPI4_TX_DMA_STREAM) || defined(__DOXYGEN__)
|
||||
#define STM32_SPI_SPI4_TX_DMA_STREAM STM32_DMA_STREAM_ID(2, 1)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief DMA stream used for SPI5 RX operations.
|
||||
* @note This option is only available on platforms with enhanced DMA.
|
||||
*/
|
||||
#if !defined(STM32_SPI_SPI5_RX_DMA_STREAM) || defined(__DOXYGEN__)
|
||||
#define STM32_SPI_SPI5_RX_DMA_STREAM STM32_DMA_STREAM_ID(2, 3)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief DMA stream used for SPI5 TX operations.
|
||||
* @note This option is only available on platforms with enhanced DMA.
|
||||
*/
|
||||
#if !defined(STM32_SPI_SPI5_TX_DMA_STREAM) || defined(__DOXYGEN__)
|
||||
#define STM32_SPI_SPI5_TX_DMA_STREAM STM32_DMA_STREAM_ID(2, 4)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief DMA stream used for SPI6 RX operations.
|
||||
* @note This option is only available on platforms with enhanced DMA.
|
||||
*/
|
||||
#if !defined(STM32_SPI_SPI6_RX_DMA_STREAM) || defined(__DOXYGEN__)
|
||||
#define STM32_SPI_SPI6_RX_DMA_STREAM STM32_DMA_STREAM_ID(2, 6)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief DMA stream used for SPI6 TX operations.
|
||||
* @note This option is only available on platforms with enhanced DMA.
|
||||
*/
|
||||
#if !defined(STM32_SPI_SPI6_TX_DMA_STREAM) || defined(__DOXYGEN__)
|
||||
#define STM32_SPI_SPI6_TX_DMA_STREAM STM32_DMA_STREAM_ID(2, 5)
|
||||
#endif
|
||||
|
||||
#else /* !STM32_ADVANCED_DMA */
|
||||
|
||||
/* Fixed streams for platforms using the old DMA peripheral, the values are
|
||||
valid for both STM32F1xx and STM32L1xx.*/
|
||||
#define STM32_SPI_SPI1_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 2)
|
||||
#define STM32_SPI_SPI1_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 3)
|
||||
#define STM32_SPI_SPI2_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 4)
|
||||
#define STM32_SPI_SPI2_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 5)
|
||||
#define STM32_SPI_SPI3_RX_DMA_STREAM STM32_DMA_STREAM_ID(2, 1)
|
||||
#define STM32_SPI_SPI3_TX_DMA_STREAM STM32_DMA_STREAM_ID(2, 2)
|
||||
#define STM32_SPI_SPI4_RX_DMA_STREAM STM32_DMA_STREAM_ID(2, 0)
|
||||
#define STM32_SPI_SPI4_TX_DMA_STREAM STM32_DMA_STREAM_ID(2, 1)
|
||||
#define STM32_SPI_SPI5_RX_DMA_STREAM STM32_DMA_STREAM_ID(2, 3)
|
||||
#define STM32_SPI_SPI5_TX_DMA_STREAM STM32_DMA_STREAM_ID(2, 4)
|
||||
#define STM32_SPI_SPI6_RX_DMA_STREAM STM32_DMA_STREAM_ID(2, 5)
|
||||
#define STM32_SPI_SPI6_TX_DMA_STREAM STM32_DMA_STREAM_ID(2, 6)
|
||||
|
||||
#endif /* !STM32_ADVANCED_DMA*/
|
||||
/** @} */
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Derived constants and error checks. */
|
||||
/*===========================================================================*/
|
||||
|
||||
#if STM32_SPI_USE_SPI1 && !STM32_HAS_SPI1
|
||||
#error "SPI1 not present in the selected device"
|
||||
#endif
|
||||
|
||||
#if STM32_SPI_USE_SPI2 && !STM32_HAS_SPI2
|
||||
#error "SPI2 not present in the selected device"
|
||||
#endif
|
||||
|
||||
#if STM32_SPI_USE_SPI3 && !STM32_HAS_SPI3
|
||||
#error "SPI3 not present in the selected device"
|
||||
#endif
|
||||
|
||||
#if STM32_SPI_USE_SPI4 && !STM32_HAS_SPI4
|
||||
#error "SPI4 not present in the selected device"
|
||||
#endif
|
||||
|
||||
#if STM32_SPI_USE_SPI5 && !STM32_HAS_SPI5
|
||||
#error "SPI5 not present in the selected device"
|
||||
#endif
|
||||
|
||||
#if STM32_SPI_USE_SPI6 && !STM32_HAS_SPI6
|
||||
#error "SPI6 not present in the selected device"
|
||||
#endif
|
||||
|
||||
#if !STM32_SPI_USE_SPI1 && !STM32_SPI_USE_SPI2 && !STM32_SPI_USE_SPI3 && \
|
||||
!STM32_SPI_USE_SPI4 && !STM32_SPI_USE_SPI5 && !STM32_SPI_USE_SPI6
|
||||
#error "SPI driver activated but no SPI peripheral assigned"
|
||||
#endif
|
||||
|
||||
#if STM32_SPI_USE_SPI1 && \
|
||||
!CORTEX_IS_VALID_KERNEL_PRIORITY(STM32_SPI_SPI1_IRQ_PRIORITY)
|
||||
#error "Invalid IRQ priority assigned to SPI1"
|
||||
#endif
|
||||
|
||||
#if STM32_SPI_USE_SPI2 && \
|
||||
!CORTEX_IS_VALID_KERNEL_PRIORITY(STM32_SPI_SPI2_IRQ_PRIORITY)
|
||||
#error "Invalid IRQ priority assigned to SPI2"
|
||||
#endif
|
||||
|
||||
#if STM32_SPI_USE_SPI3 && \
|
||||
!CORTEX_IS_VALID_KERNEL_PRIORITY(STM32_SPI_SPI3_IRQ_PRIORITY)
|
||||
#error "Invalid IRQ priority assigned to SPI3"
|
||||
#endif
|
||||
|
||||
#if STM32_SPI_USE_SPI4 && \
|
||||
!CORTEX_IS_VALID_KERNEL_PRIORITY(STM32_SPI_SPI4_IRQ_PRIORITY)
|
||||
#error "Invalid IRQ priority assigned to SPI4"
|
||||
#endif
|
||||
|
||||
#if STM32_SPI_USE_SPI5 && \
|
||||
!CORTEX_IS_VALID_KERNEL_PRIORITY(STM32_SPI_SPI5_IRQ_PRIORITY)
|
||||
#error "Invalid IRQ priority assigned to SPI5"
|
||||
#endif
|
||||
|
||||
#if STM32_SPI_USE_SPI6 && \
|
||||
!CORTEX_IS_VALID_KERNEL_PRIORITY(STM32_SPI_SPI6_IRQ_PRIORITY)
|
||||
#error "Invalid IRQ priority assigned to SPI6"
|
||||
#endif
|
||||
|
||||
#if STM32_SPI_USE_SPI1 && \
|
||||
!STM32_DMA_IS_VALID_PRIORITY(STM32_SPI_SPI1_DMA_PRIORITY)
|
||||
#error "Invalid DMA priority assigned to SPI1"
|
||||
#endif
|
||||
|
||||
#if STM32_SPI_USE_SPI2 && \
|
||||
!STM32_DMA_IS_VALID_PRIORITY(STM32_SPI_SPI2_DMA_PRIORITY)
|
||||
#error "Invalid DMA priority assigned to SPI2"
|
||||
#endif
|
||||
|
||||
#if STM32_SPI_USE_SPI3 && \
|
||||
!STM32_DMA_IS_VALID_PRIORITY(STM32_SPI_SPI3_DMA_PRIORITY)
|
||||
#error "Invalid DMA priority assigned to SPI3"
|
||||
#endif
|
||||
|
||||
#if STM32_SPI_USE_SPI4 && \
|
||||
!STM32_DMA_IS_VALID_PRIORITY(STM32_SPI_SPI4_DMA_PRIORITY)
|
||||
#error "Invalid DMA priority assigned to SPI4"
|
||||
#endif
|
||||
|
||||
#if STM32_SPI_USE_SPI5 && \
|
||||
!STM32_DMA_IS_VALID_PRIORITY(STM32_SPI_SPI5_DMA_PRIORITY)
|
||||
#error "Invalid DMA priority assigned to SPI5"
|
||||
#endif
|
||||
|
||||
#if STM32_SPI_USE_SPI6 && \
|
||||
!STM32_DMA_IS_VALID_PRIORITY(STM32_SPI_SPI6_DMA_PRIORITY)
|
||||
#error "Invalid DMA priority assigned to SPI6"
|
||||
#endif
|
||||
|
||||
#if STM32_SPI_USE_SPI1 && \
|
||||
!STM32_DMA_IS_VALID_ID(STM32_SPI_SPI1_RX_DMA_STREAM, STM32_SPI1_RX_DMA_MSK)
|
||||
#error "invalid DMA stream associated to SPI1 RX"
|
||||
#endif
|
||||
|
||||
#if STM32_SPI_USE_SPI1 && \
|
||||
!STM32_DMA_IS_VALID_ID(STM32_SPI_SPI1_TX_DMA_STREAM, STM32_SPI1_TX_DMA_MSK)
|
||||
#error "invalid DMA stream associated to SPI1 TX"
|
||||
#endif
|
||||
|
||||
#if STM32_SPI_USE_SPI2 && \
|
||||
!STM32_DMA_IS_VALID_ID(STM32_SPI_SPI2_RX_DMA_STREAM, STM32_SPI2_RX_DMA_MSK)
|
||||
#error "invalid DMA stream associated to SPI2 RX"
|
||||
#endif
|
||||
|
||||
#if STM32_SPI_USE_SPI2 && \
|
||||
!STM32_DMA_IS_VALID_ID(STM32_SPI_SPI2_TX_DMA_STREAM, STM32_SPI2_TX_DMA_MSK)
|
||||
#error "invalid DMA stream associated to SPI2 TX"
|
||||
#endif
|
||||
|
||||
#if STM32_SPI_USE_SPI3 && \
|
||||
!STM32_DMA_IS_VALID_ID(STM32_SPI_SPI3_RX_DMA_STREAM, STM32_SPI3_RX_DMA_MSK)
|
||||
#error "invalid DMA stream associated to SPI3 RX"
|
||||
#endif
|
||||
|
||||
#if STM32_SPI_USE_SPI3 && \
|
||||
!STM32_DMA_IS_VALID_ID(STM32_SPI_SPI3_TX_DMA_STREAM, STM32_SPI3_TX_DMA_MSK)
|
||||
#error "invalid DMA stream associated to SPI3 TX"
|
||||
#endif
|
||||
|
||||
#if STM32_SPI_USE_SPI4 && \
|
||||
!STM32_DMA_IS_VALID_ID(STM32_SPI_SPI4_RX_DMA_STREAM, STM32_SPI4_RX_DMA_MSK)
|
||||
#error "invalid DMA stream associated to SPI4 RX"
|
||||
#endif
|
||||
|
||||
#if STM32_SPI_USE_SPI4 && \
|
||||
!STM32_DMA_IS_VALID_ID(STM32_SPI_SPI4_TX_DMA_STREAM, STM32_SPI4_TX_DMA_MSK)
|
||||
#error "invalid DMA stream associated to SPI4 TX"
|
||||
#endif
|
||||
|
||||
#if STM32_SPI_USE_SPI5 && \
|
||||
!STM32_DMA_IS_VALID_ID(STM32_SPI_SPI5_RX_DMA_STREAM, STM32_SPI5_RX_DMA_MSK)
|
||||
#error "invalid DMA stream associated to SPI5 RX"
|
||||
#endif
|
||||
|
||||
#if STM32_SPI_USE_SPI5 && \
|
||||
!STM32_DMA_IS_VALID_ID(STM32_SPI_SPI5_TX_DMA_STREAM, STM32_SPI5_TX_DMA_MSK)
|
||||
#error "invalid DMA stream associated to SPI5 TX"
|
||||
#endif
|
||||
|
||||
#if STM32_SPI_USE_SPI6 && \
|
||||
!STM32_DMA_IS_VALID_ID(STM32_SPI_SPI6_RX_DMA_STREAM, STM32_SPI6_RX_DMA_MSK)
|
||||
#error "invalid DMA stream associated to SPI6 RX"
|
||||
#endif
|
||||
|
||||
#if STM32_SPI_USE_SPI6 && \
|
||||
!STM32_DMA_IS_VALID_ID(STM32_SPI_SPI6_TX_DMA_STREAM, STM32_SPI6_TX_DMA_MSK)
|
||||
#error "invalid DMA stream associated to SPI6 TX"
|
||||
#endif
|
||||
|
||||
#if !defined(STM32_DMA_REQUIRED)
|
||||
#define STM32_DMA_REQUIRED
|
||||
#endif
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver data structures and types. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Type of a structure representing an SPI driver.
|
||||
*/
|
||||
typedef struct SPIDriver SPIDriver;
|
||||
|
||||
/**
|
||||
* @brief SPI notification callback type.
|
||||
*
|
||||
* @param[in] spip pointer to the @p SPIDriver object triggering the
|
||||
* callback
|
||||
*/
|
||||
typedef void (*spicallback_t)(SPIDriver *spip);
|
||||
|
||||
/**
|
||||
* @brief Driver configuration structure.
|
||||
*/
|
||||
typedef struct {
|
||||
/**
|
||||
* @brief Operation complete callback or @p NULL.
|
||||
*/
|
||||
spicallback_t end_cb;
|
||||
/* End of the mandatory fields.*/
|
||||
/**
|
||||
* @brief The chip select line port.
|
||||
*/
|
||||
ioportid_t ssport;
|
||||
/**
|
||||
* @brief The chip select line pad number.
|
||||
*/
|
||||
uint16_t sspad;
|
||||
/**
|
||||
* @brief SPI initialization data.
|
||||
*/
|
||||
uint16_t cr1;
|
||||
} SPIConfig;
|
||||
|
||||
/**
|
||||
* @brief Structure representing a SPI driver.
|
||||
*/
|
||||
struct SPIDriver {
|
||||
/**
|
||||
* @brief Driver state.
|
||||
*/
|
||||
spistate_t state;
|
||||
/**
|
||||
* @brief Current configuration data.
|
||||
*/
|
||||
const SPIConfig *config;
|
||||
#if SPI_USE_WAIT || defined(__DOXYGEN__)
|
||||
/**
|
||||
* @brief Waiting thread.
|
||||
*/
|
||||
Thread *thread;
|
||||
#endif /* SPI_USE_WAIT */
|
||||
#if SPI_USE_MUTUAL_EXCLUSION || defined(__DOXYGEN__)
|
||||
#if CH_USE_MUTEXES || defined(__DOXYGEN__)
|
||||
/**
|
||||
* @brief Mutex protecting the bus.
|
||||
*/
|
||||
Mutex mutex;
|
||||
#elif CH_USE_SEMAPHORES
|
||||
Semaphore semaphore;
|
||||
#endif
|
||||
#endif /* SPI_USE_MUTUAL_EXCLUSION */
|
||||
#if defined(SPI_DRIVER_EXT_FIELDS)
|
||||
SPI_DRIVER_EXT_FIELDS
|
||||
#endif
|
||||
/* End of the mandatory fields.*/
|
||||
/**
|
||||
* @brief Pointer to the SPIx registers block.
|
||||
*/
|
||||
SPI_TypeDef *spi;
|
||||
/**
|
||||
* @brief Receive DMA stream.
|
||||
*/
|
||||
const stm32_dma_stream_t *dmarx;
|
||||
/**
|
||||
* @brief Transmit DMA stream.
|
||||
*/
|
||||
const stm32_dma_stream_t *dmatx;
|
||||
/**
|
||||
* @brief RX DMA mode bit mask.
|
||||
*/
|
||||
uint32_t rxdmamode;
|
||||
/**
|
||||
* @brief TX DMA mode bit mask.
|
||||
*/
|
||||
uint32_t txdmamode;
|
||||
};
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver macros. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* External declarations. */
|
||||
/*===========================================================================*/
|
||||
|
||||
#if STM32_SPI_USE_SPI1 && !defined(__DOXYGEN__)
|
||||
extern SPIDriver SPID1;
|
||||
#endif
|
||||
|
||||
#if STM32_SPI_USE_SPI2 && !defined(__DOXYGEN__)
|
||||
extern SPIDriver SPID2;
|
||||
#endif
|
||||
|
||||
#if STM32_SPI_USE_SPI3 && !defined(__DOXYGEN__)
|
||||
extern SPIDriver SPID3;
|
||||
#endif
|
||||
|
||||
#if STM32_SPI_USE_SPI4 && !defined(__DOXYGEN__)
|
||||
extern SPIDriver SPID4;
|
||||
#endif
|
||||
|
||||
#if STM32_SPI_USE_SPI5 && !defined(__DOXYGEN__)
|
||||
extern SPIDriver SPID5;
|
||||
#endif
|
||||
|
||||
#if STM32_SPI_USE_SPI6 && !defined(__DOXYGEN__)
|
||||
extern SPIDriver SPID6;
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
void spi_lld_init(void);
|
||||
void spi_lld_start(SPIDriver *spip);
|
||||
void spi_lld_stop(SPIDriver *spip);
|
||||
void spi_lld_select(SPIDriver *spip);
|
||||
void spi_lld_unselect(SPIDriver *spip);
|
||||
void spi_lld_ignore(SPIDriver *spip, size_t n);
|
||||
void spi_lld_exchange(SPIDriver *spip, size_t n,
|
||||
const void *txbuf, void *rxbuf);
|
||||
void spi_lld_send(SPIDriver *spip, size_t n, const void *txbuf);
|
||||
void spi_lld_receive(SPIDriver *spip, size_t n, void *rxbuf);
|
||||
uint16_t spi_lld_polled_exchange(SPIDriver *spip, uint16_t frame);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* HAL_USE_SPI */
|
||||
|
||||
#endif /* _SPI_LLD_H_ */
|
||||
|
||||
/** @} */
|
||||
+502
@@ -0,0 +1,502 @@
|
||||
/*
|
||||
ChibiOS/RT - Copyright (C) 2006-2013 Giovanni Di Sirio
|
||||
|
||||
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 STM32/SPIv2/spi_lld.c
|
||||
* @brief STM32 SPI subsystem low level driver source.
|
||||
*
|
||||
* @addtogroup SPI
|
||||
* @{
|
||||
*/
|
||||
|
||||
#include "ch.h"
|
||||
#include "hal.h"
|
||||
|
||||
#if HAL_USE_SPI || defined(__DOXYGEN__)
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local definitions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
#define SPI1_RX_DMA_CHANNEL \
|
||||
STM32_DMA_GETCHANNEL(STM32_SPI_SPI1_RX_DMA_STREAM, \
|
||||
STM32_SPI1_RX_DMA_CHN)
|
||||
|
||||
#define SPI1_TX_DMA_CHANNEL \
|
||||
STM32_DMA_GETCHANNEL(STM32_SPI_SPI1_TX_DMA_STREAM, \
|
||||
STM32_SPI1_TX_DMA_CHN)
|
||||
|
||||
#define SPI2_RX_DMA_CHANNEL \
|
||||
STM32_DMA_GETCHANNEL(STM32_SPI_SPI2_RX_DMA_STREAM, \
|
||||
STM32_SPI2_RX_DMA_CHN)
|
||||
|
||||
#define SPI2_TX_DMA_CHANNEL \
|
||||
STM32_DMA_GETCHANNEL(STM32_SPI_SPI2_TX_DMA_STREAM, \
|
||||
STM32_SPI2_TX_DMA_CHN)
|
||||
|
||||
#define SPI3_RX_DMA_CHANNEL \
|
||||
STM32_DMA_GETCHANNEL(STM32_SPI_SPI3_RX_DMA_STREAM, \
|
||||
STM32_SPI3_RX_DMA_CHN)
|
||||
|
||||
#define SPI3_TX_DMA_CHANNEL \
|
||||
STM32_DMA_GETCHANNEL(STM32_SPI_SPI3_TX_DMA_STREAM, \
|
||||
STM32_SPI3_TX_DMA_CHN)
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver exported variables. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/** @brief SPI1 driver identifier.*/
|
||||
#if STM32_SPI_USE_SPI1 || defined(__DOXYGEN__)
|
||||
SPIDriver SPID1;
|
||||
#endif
|
||||
|
||||
/** @brief SPI2 driver identifier.*/
|
||||
#if STM32_SPI_USE_SPI2 || defined(__DOXYGEN__)
|
||||
SPIDriver SPID2;
|
||||
#endif
|
||||
|
||||
/** @brief SPI3 driver identifier.*/
|
||||
#if STM32_SPI_USE_SPI3 || defined(__DOXYGEN__)
|
||||
SPIDriver SPID3;
|
||||
#endif
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local variables and types. */
|
||||
/*===========================================================================*/
|
||||
|
||||
static uint16_t dummytx;
|
||||
static uint16_t dummyrx;
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Shared end-of-rx service routine.
|
||||
*
|
||||
* @param[in] spip pointer to the @p SPIDriver object
|
||||
* @param[in] flags pre-shifted content of the ISR register
|
||||
*/
|
||||
static void spi_lld_serve_rx_interrupt(SPIDriver *spip, uint32_t flags) {
|
||||
|
||||
/* DMA errors handling.*/
|
||||
#if defined(STM32_SPI_DMA_ERROR_HOOK)
|
||||
if ((flags & (STM32_DMA_ISR_TEIF | STM32_DMA_ISR_DMEIF)) != 0) {
|
||||
STM32_SPI_DMA_ERROR_HOOK(spip);
|
||||
}
|
||||
#else
|
||||
(void)flags;
|
||||
#endif
|
||||
|
||||
/* Stop everything.*/
|
||||
dmaStreamDisable(spip->dmatx);
|
||||
dmaStreamDisable(spip->dmarx);
|
||||
|
||||
/* Portable SPI ISR code defined in the high level driver, note, it is
|
||||
a macro.*/
|
||||
_spi_isr_code(spip);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Shared end-of-tx service routine.
|
||||
*
|
||||
* @param[in] spip pointer to the @p SPIDriver object
|
||||
* @param[in] flags pre-shifted content of the ISR register
|
||||
*/
|
||||
static void spi_lld_serve_tx_interrupt(SPIDriver *spip, uint32_t flags) {
|
||||
|
||||
/* DMA errors handling.*/
|
||||
#if defined(STM32_SPI_DMA_ERROR_HOOK)
|
||||
(void)spip;
|
||||
if ((flags & (STM32_DMA_ISR_TEIF | STM32_DMA_ISR_DMEIF)) != 0) {
|
||||
STM32_SPI_DMA_ERROR_HOOK(spip);
|
||||
}
|
||||
#else
|
||||
(void)spip;
|
||||
(void)flags;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver interrupt handlers. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver exported functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Low level SPI driver initialization.
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void spi_lld_init(void) {
|
||||
|
||||
dummytx = 0xFFFF;
|
||||
|
||||
#if STM32_SPI_USE_SPI1
|
||||
spiObjectInit(&SPID1);
|
||||
SPID1.spi = SPI1;
|
||||
SPID1.dmarx = STM32_DMA_STREAM(STM32_SPI_SPI1_RX_DMA_STREAM);
|
||||
SPID1.dmatx = STM32_DMA_STREAM(STM32_SPI_SPI1_TX_DMA_STREAM);
|
||||
SPID1.rxdmamode = STM32_DMA_CR_CHSEL(SPI1_RX_DMA_CHANNEL) |
|
||||
STM32_DMA_CR_PL(STM32_SPI_SPI1_DMA_PRIORITY) |
|
||||
STM32_DMA_CR_DIR_P2M |
|
||||
STM32_DMA_CR_TCIE |
|
||||
STM32_DMA_CR_DMEIE |
|
||||
STM32_DMA_CR_TEIE;
|
||||
SPID1.txdmamode = STM32_DMA_CR_CHSEL(SPI1_TX_DMA_CHANNEL) |
|
||||
STM32_DMA_CR_PL(STM32_SPI_SPI1_DMA_PRIORITY) |
|
||||
STM32_DMA_CR_DIR_M2P |
|
||||
STM32_DMA_CR_DMEIE |
|
||||
STM32_DMA_CR_TEIE;
|
||||
#endif
|
||||
|
||||
#if STM32_SPI_USE_SPI2
|
||||
spiObjectInit(&SPID2);
|
||||
SPID2.spi = SPI2;
|
||||
SPID2.dmarx = STM32_DMA_STREAM(STM32_SPI_SPI2_RX_DMA_STREAM);
|
||||
SPID2.dmatx = STM32_DMA_STREAM(STM32_SPI_SPI2_TX_DMA_STREAM);
|
||||
SPID2.rxdmamode = STM32_DMA_CR_CHSEL(SPI2_RX_DMA_CHANNEL) |
|
||||
STM32_DMA_CR_PL(STM32_SPI_SPI2_DMA_PRIORITY) |
|
||||
STM32_DMA_CR_DIR_P2M |
|
||||
STM32_DMA_CR_TCIE |
|
||||
STM32_DMA_CR_DMEIE |
|
||||
STM32_DMA_CR_TEIE;
|
||||
SPID2.txdmamode = STM32_DMA_CR_CHSEL(SPI2_TX_DMA_CHANNEL) |
|
||||
STM32_DMA_CR_PL(STM32_SPI_SPI2_DMA_PRIORITY) |
|
||||
STM32_DMA_CR_DIR_M2P |
|
||||
STM32_DMA_CR_DMEIE |
|
||||
STM32_DMA_CR_TEIE;
|
||||
#endif
|
||||
|
||||
#if STM32_SPI_USE_SPI3
|
||||
spiObjectInit(&SPID3);
|
||||
SPID3.spi = SPI3;
|
||||
SPID3.dmarx = STM32_DMA_STREAM(STM32_SPI_SPI3_RX_DMA_STREAM);
|
||||
SPID3.dmatx = STM32_DMA_STREAM(STM32_SPI_SPI3_TX_DMA_STREAM);
|
||||
SPID3.rxdmamode = STM32_DMA_CR_CHSEL(SPI3_RX_DMA_CHANNEL) |
|
||||
STM32_DMA_CR_PL(STM32_SPI_SPI3_DMA_PRIORITY) |
|
||||
STM32_DMA_CR_DIR_P2M |
|
||||
STM32_DMA_CR_TCIE |
|
||||
STM32_DMA_CR_DMEIE |
|
||||
STM32_DMA_CR_TEIE;
|
||||
SPID3.txdmamode = STM32_DMA_CR_CHSEL(SPI3_TX_DMA_CHANNEL) |
|
||||
STM32_DMA_CR_PL(STM32_SPI_SPI3_DMA_PRIORITY) |
|
||||
STM32_DMA_CR_DIR_M2P |
|
||||
STM32_DMA_CR_DMEIE |
|
||||
STM32_DMA_CR_TEIE;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Configures and activates the SPI peripheral.
|
||||
*
|
||||
* @param[in] spip pointer to the @p SPIDriver object
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void spi_lld_start(SPIDriver *spip) {
|
||||
uint32_t ds;
|
||||
|
||||
/* If in stopped state then enables the SPI and DMA clocks.*/
|
||||
if (spip->state == SPI_STOP) {
|
||||
#if STM32_SPI_USE_SPI1
|
||||
if (&SPID1 == spip) {
|
||||
bool_t b;
|
||||
b = dmaStreamAllocate(spip->dmarx,
|
||||
STM32_SPI_SPI1_IRQ_PRIORITY,
|
||||
(stm32_dmaisr_t)spi_lld_serve_rx_interrupt,
|
||||
(void *)spip);
|
||||
chDbgAssert(!b, "spi_lld_start(), #1", "stream already allocated");
|
||||
b = dmaStreamAllocate(spip->dmatx,
|
||||
STM32_SPI_SPI1_IRQ_PRIORITY,
|
||||
(stm32_dmaisr_t)spi_lld_serve_tx_interrupt,
|
||||
(void *)spip);
|
||||
chDbgAssert(!b, "spi_lld_start(), #2", "stream already allocated");
|
||||
rccEnableSPI1(FALSE);
|
||||
}
|
||||
#endif
|
||||
#if STM32_SPI_USE_SPI2
|
||||
if (&SPID2 == spip) {
|
||||
bool_t b;
|
||||
b = dmaStreamAllocate(spip->dmarx,
|
||||
STM32_SPI_SPI2_IRQ_PRIORITY,
|
||||
(stm32_dmaisr_t)spi_lld_serve_rx_interrupt,
|
||||
(void *)spip);
|
||||
chDbgAssert(!b, "spi_lld_start(), #3", "stream already allocated");
|
||||
b = dmaStreamAllocate(spip->dmatx,
|
||||
STM32_SPI_SPI2_IRQ_PRIORITY,
|
||||
(stm32_dmaisr_t)spi_lld_serve_tx_interrupt,
|
||||
(void *)spip);
|
||||
chDbgAssert(!b, "spi_lld_start(), #4", "stream already allocated");
|
||||
rccEnableSPI2(FALSE);
|
||||
}
|
||||
#endif
|
||||
#if STM32_SPI_USE_SPI3
|
||||
if (&SPID3 == spip) {
|
||||
bool_t b;
|
||||
b = dmaStreamAllocate(spip->dmarx,
|
||||
STM32_SPI_SPI3_IRQ_PRIORITY,
|
||||
(stm32_dmaisr_t)spi_lld_serve_rx_interrupt,
|
||||
(void *)spip);
|
||||
chDbgAssert(!b, "spi_lld_start(), #5", "stream already allocated");
|
||||
b = dmaStreamAllocate(spip->dmatx,
|
||||
STM32_SPI_SPI3_IRQ_PRIORITY,
|
||||
(stm32_dmaisr_t)spi_lld_serve_tx_interrupt,
|
||||
(void *)spip);
|
||||
chDbgAssert(!b, "spi_lld_start(), #6", "stream already allocated");
|
||||
rccEnableSPI3(FALSE);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* DMA setup.*/
|
||||
dmaStreamSetPeripheral(spip->dmarx, &spip->spi->DR);
|
||||
dmaStreamSetPeripheral(spip->dmatx, &spip->spi->DR);
|
||||
}
|
||||
|
||||
/* Configuration-specific DMA setup.*/
|
||||
ds = spip->config->cr2 & SPI_CR2_DS;
|
||||
if (!ds || (ds <= (SPI_CR2_DS_2 | SPI_CR2_DS_1 | SPI_CR2_DS_0))) {
|
||||
/* Frame width is 8 bits or smaller.*/
|
||||
spip->rxdmamode = (spip->rxdmamode & ~STM32_DMA_CR_SIZE_MASK) |
|
||||
STM32_DMA_CR_PSIZE_BYTE | STM32_DMA_CR_MSIZE_BYTE;
|
||||
spip->txdmamode = (spip->txdmamode & ~STM32_DMA_CR_SIZE_MASK) |
|
||||
STM32_DMA_CR_PSIZE_BYTE | STM32_DMA_CR_MSIZE_BYTE;
|
||||
}
|
||||
else {
|
||||
/* Frame width is larger than 8 bits.*/
|
||||
spip->rxdmamode = (spip->rxdmamode & ~STM32_DMA_CR_SIZE_MASK) |
|
||||
STM32_DMA_CR_PSIZE_HWORD | STM32_DMA_CR_MSIZE_HWORD;
|
||||
spip->txdmamode = (spip->txdmamode & ~STM32_DMA_CR_SIZE_MASK) |
|
||||
STM32_DMA_CR_PSIZE_HWORD | STM32_DMA_CR_MSIZE_HWORD;
|
||||
}
|
||||
/* SPI setup and enable.*/
|
||||
spip->spi->CR1 = 0;
|
||||
spip->spi->CR1 = spip->config->cr1 | SPI_CR1_MSTR | SPI_CR1_SSM |
|
||||
SPI_CR1_SSI;
|
||||
spip->spi->CR2 = spip->config->cr2 | SPI_CR2_FRXTH | SPI_CR2_SSOE |
|
||||
SPI_CR2_RXDMAEN | SPI_CR2_TXDMAEN;
|
||||
spip->spi->CR1 |= SPI_CR1_SPE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Deactivates the SPI peripheral.
|
||||
*
|
||||
* @param[in] spip pointer to the @p SPIDriver object
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void spi_lld_stop(SPIDriver *spip) {
|
||||
|
||||
/* If in ready state then disables the SPI clock.*/
|
||||
if (spip->state == SPI_READY) {
|
||||
|
||||
/* SPI disable.*/
|
||||
spip->spi->CR1 = 0;
|
||||
spip->spi->CR2 = 0;
|
||||
dmaStreamRelease(spip->dmarx);
|
||||
dmaStreamRelease(spip->dmatx);
|
||||
|
||||
#if STM32_SPI_USE_SPI1
|
||||
if (&SPID1 == spip)
|
||||
rccDisableSPI1(FALSE);
|
||||
#endif
|
||||
#if STM32_SPI_USE_SPI2
|
||||
if (&SPID2 == spip)
|
||||
rccDisableSPI2(FALSE);
|
||||
#endif
|
||||
#if STM32_SPI_USE_SPI3
|
||||
if (&SPID3 == spip)
|
||||
rccDisableSPI3(FALSE);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Asserts the slave select signal and prepares for transfers.
|
||||
*
|
||||
* @param[in] spip pointer to the @p SPIDriver object
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void spi_lld_select(SPIDriver *spip) {
|
||||
|
||||
palClearPad(spip->config->ssport, spip->config->sspad);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Deasserts the slave select signal.
|
||||
* @details The previously selected peripheral is unselected.
|
||||
*
|
||||
* @param[in] spip pointer to the @p SPIDriver object
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void spi_lld_unselect(SPIDriver *spip) {
|
||||
|
||||
palSetPad(spip->config->ssport, spip->config->sspad);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Ignores data on the SPI bus.
|
||||
* @details This asynchronous function starts the transmission of a series of
|
||||
* idle words on the SPI bus and ignores the received data.
|
||||
* @post At the end of the operation the configured callback is invoked.
|
||||
*
|
||||
* @param[in] spip pointer to the @p SPIDriver object
|
||||
* @param[in] n number of words to be ignored
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void spi_lld_ignore(SPIDriver *spip, size_t n) {
|
||||
|
||||
dmaStreamSetMemory0(spip->dmarx, &dummyrx);
|
||||
dmaStreamSetTransactionSize(spip->dmarx, n);
|
||||
dmaStreamSetMode(spip->dmarx, spip->rxdmamode);
|
||||
|
||||
dmaStreamSetMemory0(spip->dmatx, &dummytx);
|
||||
dmaStreamSetTransactionSize(spip->dmatx, n);
|
||||
dmaStreamSetMode(spip->dmatx, spip->txdmamode);
|
||||
|
||||
dmaStreamEnable(spip->dmarx);
|
||||
dmaStreamEnable(spip->dmatx);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Exchanges data on the SPI bus.
|
||||
* @details This asynchronous function starts a simultaneous transmit/receive
|
||||
* operation.
|
||||
* @post At the end of the operation the configured callback is invoked.
|
||||
* @note The buffers are organized as uint8_t arrays for data sizes below or
|
||||
* equal to 8 bits else it is organized as uint16_t arrays.
|
||||
*
|
||||
* @param[in] spip pointer to the @p SPIDriver object
|
||||
* @param[in] n number of words to be exchanged
|
||||
* @param[in] txbuf the pointer to the transmit buffer
|
||||
* @param[out] rxbuf the pointer to the receive buffer
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void spi_lld_exchange(SPIDriver *spip, size_t n,
|
||||
const void *txbuf, void *rxbuf) {
|
||||
|
||||
dmaStreamSetMemory0(spip->dmarx, rxbuf);
|
||||
dmaStreamSetTransactionSize(spip->dmarx, n);
|
||||
dmaStreamSetMode(spip->dmarx, spip->rxdmamode| STM32_DMA_CR_MINC);
|
||||
|
||||
dmaStreamSetMemory0(spip->dmatx, txbuf);
|
||||
dmaStreamSetTransactionSize(spip->dmatx, n);
|
||||
dmaStreamSetMode(spip->dmatx, spip->txdmamode | STM32_DMA_CR_MINC);
|
||||
|
||||
dmaStreamEnable(spip->dmarx);
|
||||
dmaStreamEnable(spip->dmatx);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sends data over the SPI bus.
|
||||
* @details This asynchronous function starts a transmit operation.
|
||||
* @post At the end of the operation the configured callback is invoked.
|
||||
* @note The buffers are organized as uint8_t arrays for data sizes below or
|
||||
* equal to 8 bits else it is organized as uint16_t arrays.
|
||||
*
|
||||
* @param[in] spip pointer to the @p SPIDriver object
|
||||
* @param[in] n number of words to send
|
||||
* @param[in] txbuf the pointer to the transmit buffer
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void spi_lld_send(SPIDriver *spip, size_t n, const void *txbuf) {
|
||||
|
||||
dmaStreamSetMemory0(spip->dmarx, &dummyrx);
|
||||
dmaStreamSetTransactionSize(spip->dmarx, n);
|
||||
dmaStreamSetMode(spip->dmarx, spip->rxdmamode);
|
||||
|
||||
dmaStreamSetMemory0(spip->dmatx, txbuf);
|
||||
dmaStreamSetTransactionSize(spip->dmatx, n);
|
||||
dmaStreamSetMode(spip->dmatx, spip->txdmamode | STM32_DMA_CR_MINC);
|
||||
|
||||
dmaStreamEnable(spip->dmarx);
|
||||
dmaStreamEnable(spip->dmatx);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Receives data from the SPI bus.
|
||||
* @details This asynchronous function starts a receive operation.
|
||||
* @post At the end of the operation the configured callback is invoked.
|
||||
* @note The buffers are organized as uint8_t arrays for data sizes below or
|
||||
* equal to 8 bits else it is organized as uint16_t arrays.
|
||||
*
|
||||
* @param[in] spip pointer to the @p SPIDriver object
|
||||
* @param[in] n number of words to receive
|
||||
* @param[out] rxbuf the pointer to the receive buffer
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void spi_lld_receive(SPIDriver *spip, size_t n, void *rxbuf) {
|
||||
|
||||
dmaStreamSetMemory0(spip->dmarx, rxbuf);
|
||||
dmaStreamSetTransactionSize(spip->dmarx, n);
|
||||
dmaStreamSetMode(spip->dmarx, spip->rxdmamode | STM32_DMA_CR_MINC);
|
||||
|
||||
dmaStreamSetMemory0(spip->dmatx, &dummytx);
|
||||
dmaStreamSetTransactionSize(spip->dmatx, n);
|
||||
dmaStreamSetMode(spip->dmatx, spip->txdmamode);
|
||||
|
||||
dmaStreamEnable(spip->dmarx);
|
||||
dmaStreamEnable(spip->dmatx);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Exchanges one frame using a polled wait.
|
||||
* @details This synchronous function exchanges one frame using a polled
|
||||
* synchronization method. This function is useful when exchanging
|
||||
* small amount of data on high speed channels, usually in this
|
||||
* situation is much more efficient just wait for completion using
|
||||
* polling than suspending the thread waiting for an interrupt.
|
||||
*
|
||||
* @param[in] spip pointer to the @p SPIDriver object
|
||||
* @param[in] frame the data frame to send over the SPI bus
|
||||
* @return The received data frame from the SPI bus.
|
||||
*/
|
||||
uint16_t spi_lld_polled_exchange(SPIDriver *spip, uint16_t frame) {
|
||||
|
||||
/*
|
||||
* Data register must be accessed with the appropriate data size.
|
||||
* Byte size access (uint8_t *) for transactions that are <= 8-bit.
|
||||
* Halfword size access (uint16_t) for transactions that are <= 8-bit.
|
||||
*/
|
||||
if ((spip->config->cr2 & SPI_CR2_DS) <= (SPI_CR2_DS_2 |
|
||||
SPI_CR2_DS_1 |
|
||||
SPI_CR2_DS_0)) {
|
||||
volatile uint8_t *spidr = (volatile uint8_t *)&spip->spi->DR;
|
||||
*spidr = (uint8_t)frame;
|
||||
while ((spip->spi->SR & SPI_SR_RXNE) == 0)
|
||||
;
|
||||
return (uint16_t)*spidr;
|
||||
}
|
||||
else {
|
||||
spip->spi->DR = frame;
|
||||
while ((spip->spi->SR & SPI_SR_RXNE) == 0)
|
||||
;
|
||||
return spip->spi->DR;
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* HAL_USE_SPI */
|
||||
|
||||
/** @} */
|
||||
+424
@@ -0,0 +1,424 @@
|
||||
/*
|
||||
ChibiOS/RT - Copyright (C) 2006-2013 Giovanni Di Sirio
|
||||
|
||||
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 STM32/SPIv2/spi_lld.h
|
||||
* @brief STM32 SPI subsystem low level driver header.
|
||||
*
|
||||
* @addtogroup SPI
|
||||
* @{
|
||||
*/
|
||||
|
||||
#ifndef _SPI_LLD_H_
|
||||
#define _SPI_LLD_H_
|
||||
|
||||
#if HAL_USE_SPI || defined(__DOXYGEN__)
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver constants. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver pre-compile time settings. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @name Configuration options
|
||||
* @{
|
||||
*/
|
||||
/**
|
||||
* @brief SPI1 driver enable switch.
|
||||
* @details If set to @p TRUE the support for SPI1 is included.
|
||||
* @note The default is @p FALSE.
|
||||
*/
|
||||
#if !defined(STM32_SPI_USE_SPI1) || defined(__DOXYGEN__)
|
||||
#define STM32_SPI_USE_SPI1 FALSE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief SPI2 driver enable switch.
|
||||
* @details If set to @p TRUE the support for SPI2 is included.
|
||||
* @note The default is @p FALSE.
|
||||
*/
|
||||
#if !defined(STM32_SPI_USE_SPI2) || defined(__DOXYGEN__)
|
||||
#define STM32_SPI_USE_SPI2 FALSE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief SPI3 driver enable switch.
|
||||
* @details If set to @p TRUE the support for SPI3 is included.
|
||||
* @note The default is @p FALSE.
|
||||
*/
|
||||
#if !defined(STM32_SPI_USE_SPI3) || defined(__DOXYGEN__)
|
||||
#define STM32_SPI_USE_SPI3 FALSE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief SPI1 interrupt priority level setting.
|
||||
*/
|
||||
#if !defined(STM32_SPI_SPI1_IRQ_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define STM32_SPI_SPI1_IRQ_PRIORITY 10
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief SPI2 interrupt priority level setting.
|
||||
*/
|
||||
#if !defined(STM32_SPI_SPI2_IRQ_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define STM32_SPI_SPI2_IRQ_PRIORITY 10
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief SPI3 interrupt priority level setting.
|
||||
*/
|
||||
#if !defined(STM32_SPI_SPI3_IRQ_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define STM32_SPI_SPI3_IRQ_PRIORITY 10
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief SPI1 DMA priority (0..3|lowest..highest).
|
||||
* @note The priority level is used for both the TX and RX DMA streams but
|
||||
* because of the streams ordering the RX stream has always priority
|
||||
* over the TX stream.
|
||||
*/
|
||||
#if !defined(STM32_SPI_SPI1_DMA_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define STM32_SPI_SPI1_DMA_PRIORITY 1
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief SPI2 DMA priority (0..3|lowest..highest).
|
||||
* @note The priority level is used for both the TX and RX DMA streams but
|
||||
* because of the streams ordering the RX stream has always priority
|
||||
* over the TX stream.
|
||||
*/
|
||||
#if !defined(STM32_SPI_SPI2_DMA_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define STM32_SPI_SPI2_DMA_PRIORITY 1
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief SPI3 DMA priority (0..3|lowest..highest).
|
||||
* @note The priority level is used for both the TX and RX DMA streams but
|
||||
* because of the streams ordering the RX stream has always priority
|
||||
* over the TX stream.
|
||||
*/
|
||||
#if !defined(STM32_SPI_SPI3_DMA_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define STM32_SPI_SPI3_DMA_PRIORITY 1
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief SPI DMA error hook.
|
||||
*/
|
||||
#if !defined(STM32_SPI_DMA_ERROR_HOOK) || defined(__DOXYGEN__)
|
||||
#define STM32_SPI_DMA_ERROR_HOOK(spip) chSysHalt()
|
||||
#endif
|
||||
|
||||
#if STM32_ADVANCED_DMA || defined(__DOXYGEN__)
|
||||
|
||||
/**
|
||||
* @brief DMA stream used for SPI1 RX operations.
|
||||
* @note This option is only available on platforms with enhanced DMA.
|
||||
*/
|
||||
#if !defined(STM32_SPI_SPI1_RX_DMA_STREAM) || defined(__DOXYGEN__)
|
||||
#define STM32_SPI_SPI1_RX_DMA_STREAM STM32_DMA_STREAM_ID(2, 0)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief DMA stream used for SPI1 TX operations.
|
||||
* @note This option is only available on platforms with enhanced DMA.
|
||||
*/
|
||||
#if !defined(STM32_SPI_SPI1_TX_DMA_STREAM) || defined(__DOXYGEN__)
|
||||
#define STM32_SPI_SPI1_TX_DMA_STREAM STM32_DMA_STREAM_ID(2, 3)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief DMA stream used for SPI2 RX operations.
|
||||
* @note This option is only available on platforms with enhanced DMA.
|
||||
*/
|
||||
#if !defined(STM32_SPI_SPI2_RX_DMA_STREAM) || defined(__DOXYGEN__)
|
||||
#define STM32_SPI_SPI2_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 3)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief DMA stream used for SPI2 TX operations.
|
||||
* @note This option is only available on platforms with enhanced DMA.
|
||||
*/
|
||||
#if !defined(STM32_SPI_SPI2_TX_DMA_STREAM) || defined(__DOXYGEN__)
|
||||
#define STM32_SPI_SPI2_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 4)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief DMA stream used for SPI3 RX operations.
|
||||
* @note This option is only available on platforms with enhanced DMA.
|
||||
*/
|
||||
#if !defined(STM32_SPI_SPI3_RX_DMA_STREAM) || defined(__DOXYGEN__)
|
||||
#define STM32_SPI_SPI3_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 0)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief DMA stream used for SPI3 TX operations.
|
||||
* @note This option is only available on platforms with enhanced DMA.
|
||||
*/
|
||||
#if !defined(STM32_SPI_SPI3_TX_DMA_STREAM) || defined(__DOXYGEN__)
|
||||
#define STM32_SPI_SPI3_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 7)
|
||||
#endif
|
||||
|
||||
#else /* !STM32_ADVANCED_DMA */
|
||||
|
||||
#if defined(STM32F0XX)
|
||||
/* Fixed values for STM32F0xx devices.*/
|
||||
#define STM32_SPI_SPI1_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 2)
|
||||
#define STM32_SPI_SPI1_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 3)
|
||||
#define STM32_SPI_SPI2_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 4)
|
||||
#define STM32_SPI_SPI2_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 5)
|
||||
#endif /* defined(STM32F0XX) */
|
||||
|
||||
#if defined(STM32F30X) || defined(STM32F37X)
|
||||
/* Fixed values for STM32F3xx devices.*/
|
||||
#define STM32_SPI_SPI1_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 2)
|
||||
#define STM32_SPI_SPI1_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 3)
|
||||
#define STM32_SPI_SPI2_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 4)
|
||||
#define STM32_SPI_SPI2_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 5)
|
||||
#define STM32_SPI_SPI3_RX_DMA_STREAM STM32_DMA_STREAM_ID(2, 1)
|
||||
#define STM32_SPI_SPI3_TX_DMA_STREAM STM32_DMA_STREAM_ID(2, 2)
|
||||
#endif /* defined(STM32F30X) */
|
||||
|
||||
#endif /* !STM32_ADVANCED_DMA*/
|
||||
/** @} */
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Derived constants and error checks. */
|
||||
/*===========================================================================*/
|
||||
|
||||
#if STM32_SPI_USE_SPI1 && !STM32_HAS_SPI1
|
||||
#error "SPI1 not present in the selected device"
|
||||
#endif
|
||||
|
||||
#if STM32_SPI_USE_SPI2 && !STM32_HAS_SPI2
|
||||
#error "SPI2 not present in the selected device"
|
||||
#endif
|
||||
|
||||
#if STM32_SPI_USE_SPI3 && !STM32_HAS_SPI3
|
||||
#error "SPI3 not present in the selected device"
|
||||
#endif
|
||||
|
||||
#if !STM32_SPI_USE_SPI1 && !STM32_SPI_USE_SPI2 && !STM32_SPI_USE_SPI3
|
||||
#error "SPI driver activated but no SPI peripheral assigned"
|
||||
#endif
|
||||
|
||||
#if STM32_SPI_USE_SPI1 && \
|
||||
!CORTEX_IS_VALID_KERNEL_PRIORITY(STM32_SPI_SPI1_IRQ_PRIORITY)
|
||||
#error "Invalid IRQ priority assigned to SPI1"
|
||||
#endif
|
||||
|
||||
#if STM32_SPI_USE_SPI2 && \
|
||||
!CORTEX_IS_VALID_KERNEL_PRIORITY(STM32_SPI_SPI2_IRQ_PRIORITY)
|
||||
#error "Invalid IRQ priority assigned to SPI2"
|
||||
#endif
|
||||
|
||||
#if STM32_SPI_USE_SPI3 && \
|
||||
!CORTEX_IS_VALID_KERNEL_PRIORITY(STM32_SPI_SPI3_IRQ_PRIORITY)
|
||||
#error "Invalid IRQ priority assigned to SPI3"
|
||||
#endif
|
||||
|
||||
#if STM32_SPI_USE_SPI1 && \
|
||||
!STM32_DMA_IS_VALID_PRIORITY(STM32_SPI_SPI1_DMA_PRIORITY)
|
||||
#error "Invalid DMA priority assigned to SPI1"
|
||||
#endif
|
||||
|
||||
#if STM32_SPI_USE_SPI2 && \
|
||||
!STM32_DMA_IS_VALID_PRIORITY(STM32_SPI_SPI2_DMA_PRIORITY)
|
||||
#error "Invalid DMA priority assigned to SPI2"
|
||||
#endif
|
||||
|
||||
#if STM32_SPI_USE_SPI3 && \
|
||||
!STM32_DMA_IS_VALID_PRIORITY(STM32_SPI_SPI3_DMA_PRIORITY)
|
||||
#error "Invalid DMA priority assigned to SPI3"
|
||||
#endif
|
||||
|
||||
#if STM32_SPI_USE_SPI1 && \
|
||||
!STM32_DMA_IS_VALID_ID(STM32_SPI_SPI1_RX_DMA_STREAM, STM32_SPI1_RX_DMA_MSK)
|
||||
#error "invalid DMA stream associated to SPI1 RX"
|
||||
#endif
|
||||
|
||||
#if STM32_SPI_USE_SPI1 && \
|
||||
!STM32_DMA_IS_VALID_ID(STM32_SPI_SPI1_TX_DMA_STREAM, STM32_SPI1_TX_DMA_MSK)
|
||||
#error "invalid DMA stream associated to SPI1 TX"
|
||||
#endif
|
||||
|
||||
#if STM32_SPI_USE_SPI2 && \
|
||||
!STM32_DMA_IS_VALID_ID(STM32_SPI_SPI2_RX_DMA_STREAM, STM32_SPI2_RX_DMA_MSK)
|
||||
#error "invalid DMA stream associated to SPI2 RX"
|
||||
#endif
|
||||
|
||||
#if STM32_SPI_USE_SPI2 && \
|
||||
!STM32_DMA_IS_VALID_ID(STM32_SPI_SPI2_TX_DMA_STREAM, STM32_SPI2_TX_DMA_MSK)
|
||||
#error "invalid DMA stream associated to SPI2 TX"
|
||||
#endif
|
||||
|
||||
#if STM32_SPI_USE_SPI3 && \
|
||||
!STM32_DMA_IS_VALID_ID(STM32_SPI_SPI3_RX_DMA_STREAM, STM32_SPI3_RX_DMA_MSK)
|
||||
#error "invalid DMA stream associated to SPI3 RX"
|
||||
#endif
|
||||
|
||||
#if STM32_SPI_USE_SPI3 && \
|
||||
!STM32_DMA_IS_VALID_ID(STM32_SPI_SPI3_TX_DMA_STREAM, STM32_SPI3_TX_DMA_MSK)
|
||||
#error "invalid DMA stream associated to SPI3 TX"
|
||||
#endif
|
||||
|
||||
#if !defined(STM32_DMA_REQUIRED)
|
||||
#define STM32_DMA_REQUIRED
|
||||
#endif
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver data structures and types. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Type of a structure representing an SPI driver.
|
||||
*/
|
||||
typedef struct SPIDriver SPIDriver;
|
||||
|
||||
/**
|
||||
* @brief SPI notification callback type.
|
||||
*
|
||||
* @param[in] spip pointer to the @p SPIDriver object triggering the
|
||||
* callback
|
||||
*/
|
||||
typedef void (*spicallback_t)(SPIDriver *spip);
|
||||
|
||||
/**
|
||||
* @brief Driver configuration structure.
|
||||
*/
|
||||
typedef struct {
|
||||
/**
|
||||
* @brief Operation complete callback or @p NULL.
|
||||
*/
|
||||
spicallback_t end_cb;
|
||||
/* End of the mandatory fields.*/
|
||||
/**
|
||||
* @brief The chip select line port.
|
||||
*/
|
||||
ioportid_t ssport;
|
||||
/**
|
||||
* @brief The chip select line pad number.
|
||||
*/
|
||||
uint16_t sspad;
|
||||
/**
|
||||
* @brief SPI CR1 register initialization data.
|
||||
*/
|
||||
uint16_t cr1;
|
||||
/**
|
||||
* @brief SPI CR2 register initialization data.
|
||||
*/
|
||||
uint16_t cr2;
|
||||
} SPIConfig;
|
||||
|
||||
/**
|
||||
* @brief Structure representing a SPI driver.
|
||||
*/
|
||||
struct SPIDriver {
|
||||
/**
|
||||
* @brief Driver state.
|
||||
*/
|
||||
spistate_t state;
|
||||
/**
|
||||
* @brief Current configuration data.
|
||||
*/
|
||||
const SPIConfig *config;
|
||||
#if SPI_USE_WAIT || defined(__DOXYGEN__)
|
||||
/**
|
||||
* @brief Waiting thread.
|
||||
*/
|
||||
Thread *thread;
|
||||
#endif /* SPI_USE_WAIT */
|
||||
#if SPI_USE_MUTUAL_EXCLUSION || defined(__DOXYGEN__)
|
||||
#if CH_USE_MUTEXES || defined(__DOXYGEN__)
|
||||
/**
|
||||
* @brief Mutex protecting the bus.
|
||||
*/
|
||||
Mutex mutex;
|
||||
#elif CH_USE_SEMAPHORES
|
||||
Semaphore semaphore;
|
||||
#endif
|
||||
#endif /* SPI_USE_MUTUAL_EXCLUSION */
|
||||
#if defined(SPI_DRIVER_EXT_FIELDS)
|
||||
SPI_DRIVER_EXT_FIELDS
|
||||
#endif
|
||||
/* End of the mandatory fields.*/
|
||||
/**
|
||||
* @brief Pointer to the SPIx registers block.
|
||||
*/
|
||||
SPI_TypeDef *spi;
|
||||
/**
|
||||
* @brief Receive DMA stream.
|
||||
*/
|
||||
const stm32_dma_stream_t *dmarx;
|
||||
/**
|
||||
* @brief Transmit DMA stream.
|
||||
*/
|
||||
const stm32_dma_stream_t *dmatx;
|
||||
/**
|
||||
* @brief RX DMA mode bit mask.
|
||||
*/
|
||||
uint32_t rxdmamode;
|
||||
/**
|
||||
* @brief TX DMA mode bit mask.
|
||||
*/
|
||||
uint32_t txdmamode;
|
||||
};
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver macros. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* External declarations. */
|
||||
/*===========================================================================*/
|
||||
|
||||
#if STM32_SPI_USE_SPI1 && !defined(__DOXYGEN__)
|
||||
extern SPIDriver SPID1;
|
||||
#endif
|
||||
|
||||
#if STM32_SPI_USE_SPI2 && !defined(__DOXYGEN__)
|
||||
extern SPIDriver SPID2;
|
||||
#endif
|
||||
|
||||
#if STM32_SPI_USE_SPI3 && !defined(__DOXYGEN__)
|
||||
extern SPIDriver SPID3;
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
void spi_lld_init(void);
|
||||
void spi_lld_start(SPIDriver *spip);
|
||||
void spi_lld_stop(SPIDriver *spip);
|
||||
void spi_lld_select(SPIDriver *spip);
|
||||
void spi_lld_unselect(SPIDriver *spip);
|
||||
void spi_lld_ignore(SPIDriver *spip, size_t n);
|
||||
void spi_lld_exchange(SPIDriver *spip, size_t n,
|
||||
const void *txbuf, void *rxbuf);
|
||||
void spi_lld_send(SPIDriver *spip, size_t n, const void *txbuf);
|
||||
void spi_lld_receive(SPIDriver *spip, size_t n, void *rxbuf);
|
||||
uint16_t spi_lld_polled_exchange(SPIDriver *spip, uint16_t frame);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* HAL_USE_SPI */
|
||||
|
||||
#endif /* _SPI_LLD_H_ */
|
||||
|
||||
/** @} */
|
||||
+775
@@ -0,0 +1,775 @@
|
||||
/*
|
||||
ChibiOS/RT - Copyright (C) 2006-2013 Giovanni Di Sirio
|
||||
|
||||
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 STM32/gpt_lld.c
|
||||
* @brief STM32 GPT subsystem low level driver source.
|
||||
*
|
||||
* @addtogroup GPT
|
||||
* @{
|
||||
*/
|
||||
|
||||
#include "ch.h"
|
||||
#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 TIM1 when enabled.
|
||||
*/
|
||||
#if STM32_GPT_USE_TIM1 || defined(__DOXYGEN__)
|
||||
GPTDriver GPTD1;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief GPTD2 driver identifier.
|
||||
* @note The driver GPTD2 allocates the timer TIM2 when enabled.
|
||||
*/
|
||||
#if STM32_GPT_USE_TIM2 || defined(__DOXYGEN__)
|
||||
GPTDriver GPTD2;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief GPTD3 driver identifier.
|
||||
* @note The driver GPTD3 allocates the timer TIM3 when enabled.
|
||||
*/
|
||||
#if STM32_GPT_USE_TIM3 || defined(__DOXYGEN__)
|
||||
GPTDriver GPTD3;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief GPTD4 driver identifier.
|
||||
* @note The driver GPTD4 allocates the timer TIM4 when enabled.
|
||||
*/
|
||||
#if STM32_GPT_USE_TIM4 || defined(__DOXYGEN__)
|
||||
GPTDriver GPTD4;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief GPTD5 driver identifier.
|
||||
* @note The driver GPTD5 allocates the timer TIM5 when enabled.
|
||||
*/
|
||||
#if STM32_GPT_USE_TIM5 || defined(__DOXYGEN__)
|
||||
GPTDriver GPTD5;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief GPTD6 driver identifier.
|
||||
* @note The driver GPTD6 allocates the timer TIM6 when enabled.
|
||||
*/
|
||||
#if STM32_GPT_USE_TIM6 || defined(__DOXYGEN__)
|
||||
GPTDriver GPTD6;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief GPTD7 driver identifier.
|
||||
* @note The driver GPTD7 allocates the timer TIM7 when enabled.
|
||||
*/
|
||||
#if STM32_GPT_USE_TIM7 || defined(__DOXYGEN__)
|
||||
GPTDriver GPTD7;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief GPTD8 driver identifier.
|
||||
* @note The driver GPTD8 allocates the timer TIM8 when enabled.
|
||||
*/
|
||||
#if STM32_GPT_USE_TIM8 || defined(__DOXYGEN__)
|
||||
GPTDriver GPTD8;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief GPTD9 driver identifier.
|
||||
* @note The driver GPTD9 allocates the timer TIM9 when enabled.
|
||||
*/
|
||||
#if STM32_GPT_USE_TIM9 || defined(__DOXYGEN__)
|
||||
GPTDriver GPTD9;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief GPTD11 driver identifier.
|
||||
* @note The driver GPTD11 allocates the timer TIM11 when enabled.
|
||||
*/
|
||||
#if STM32_GPT_USE_TIM11 || defined(__DOXYGEN__)
|
||||
GPTDriver GPTD11;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief GPTD12 driver identifier.
|
||||
* @note The driver GPTD12 allocates the timer TIM12 when enabled.
|
||||
*/
|
||||
#if STM32_GPT_USE_TIM12 || defined(__DOXYGEN__)
|
||||
GPTDriver GPTD12;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief GPTD14 driver identifier.
|
||||
* @note The driver GPTD14 allocates the timer TIM14 when enabled.
|
||||
*/
|
||||
#if STM32_GPT_USE_TIM14 || defined(__DOXYGEN__)
|
||||
GPTDriver GPTD14;
|
||||
#endif
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local variables and types. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Shared IRQ handler.
|
||||
*
|
||||
* @param[in] gptp pointer to a @p GPTDriver object
|
||||
*/
|
||||
static void gpt_lld_serve_interrupt(GPTDriver *gptp) {
|
||||
|
||||
gptp->tim->SR = 0;
|
||||
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 STM32_GPT_USE_TIM1
|
||||
#if !defined(STM32_TIM1_UP_HANDLER)
|
||||
#error "STM32_TIM1_UP_HANDLER not defined"
|
||||
#endif
|
||||
/**
|
||||
* @brief TIM2 interrupt handler.
|
||||
*
|
||||
* @isr
|
||||
*/
|
||||
CH_IRQ_HANDLER(STM32_TIM1_UP_HANDLER) {
|
||||
|
||||
CH_IRQ_PROLOGUE();
|
||||
|
||||
gpt_lld_serve_interrupt(&GPTD1);
|
||||
|
||||
CH_IRQ_EPILOGUE();
|
||||
}
|
||||
#endif /* STM32_GPT_USE_TIM1 */
|
||||
|
||||
#if STM32_GPT_USE_TIM2
|
||||
#if !defined(STM32_TIM2_HANDLER)
|
||||
#error "STM32_TIM2_HANDLER not defined"
|
||||
#endif
|
||||
/**
|
||||
* @brief TIM2 interrupt handler.
|
||||
*
|
||||
* @isr
|
||||
*/
|
||||
CH_IRQ_HANDLER(STM32_TIM2_HANDLER) {
|
||||
|
||||
CH_IRQ_PROLOGUE();
|
||||
|
||||
gpt_lld_serve_interrupt(&GPTD2);
|
||||
|
||||
CH_IRQ_EPILOGUE();
|
||||
}
|
||||
#endif /* STM32_GPT_USE_TIM2 */
|
||||
|
||||
#if STM32_GPT_USE_TIM3
|
||||
#if !defined(STM32_TIM3_HANDLER)
|
||||
#error "STM32_TIM3_HANDLER not defined"
|
||||
#endif
|
||||
/**
|
||||
* @brief TIM3 interrupt handler.
|
||||
*
|
||||
* @isr
|
||||
*/
|
||||
CH_IRQ_HANDLER(STM32_TIM3_HANDLER) {
|
||||
|
||||
CH_IRQ_PROLOGUE();
|
||||
|
||||
gpt_lld_serve_interrupt(&GPTD3);
|
||||
|
||||
CH_IRQ_EPILOGUE();
|
||||
}
|
||||
#endif /* STM32_GPT_USE_TIM3 */
|
||||
|
||||
#if STM32_GPT_USE_TIM4
|
||||
#if !defined(STM32_TIM4_HANDLER)
|
||||
#error "STM32_TIM4_HANDLER not defined"
|
||||
#endif
|
||||
/**
|
||||
* @brief TIM4 interrupt handler.
|
||||
*
|
||||
* @isr
|
||||
*/
|
||||
CH_IRQ_HANDLER(STM32_TIM4_HANDLER) {
|
||||
|
||||
CH_IRQ_PROLOGUE();
|
||||
|
||||
gpt_lld_serve_interrupt(&GPTD4);
|
||||
|
||||
CH_IRQ_EPILOGUE();
|
||||
}
|
||||
#endif /* STM32_GPT_USE_TIM4 */
|
||||
|
||||
#if STM32_GPT_USE_TIM5
|
||||
#if !defined(STM32_TIM5_HANDLER)
|
||||
#error "STM32_TIM5_HANDLER not defined"
|
||||
#endif
|
||||
/**
|
||||
* @brief TIM5 interrupt handler.
|
||||
*
|
||||
* @isr
|
||||
*/
|
||||
CH_IRQ_HANDLER(STM32_TIM5_HANDLER) {
|
||||
|
||||
CH_IRQ_PROLOGUE();
|
||||
|
||||
gpt_lld_serve_interrupt(&GPTD5);
|
||||
|
||||
CH_IRQ_EPILOGUE();
|
||||
}
|
||||
#endif /* STM32_GPT_USE_TIM5 */
|
||||
|
||||
#if STM32_GPT_USE_TIM6
|
||||
#if !defined(STM32_TIM6_HANDLER)
|
||||
#error "STM32_TIM6_HANDLER not defined"
|
||||
#endif
|
||||
/**
|
||||
* @brief TIM6 interrupt handler.
|
||||
*
|
||||
* @isr
|
||||
*/
|
||||
CH_IRQ_HANDLER(STM32_TIM6_HANDLER) {
|
||||
|
||||
CH_IRQ_PROLOGUE();
|
||||
|
||||
gpt_lld_serve_interrupt(&GPTD6);
|
||||
|
||||
CH_IRQ_EPILOGUE();
|
||||
}
|
||||
#endif /* STM32_GPT_USE_TIM6 */
|
||||
|
||||
#if STM32_GPT_USE_TIM7
|
||||
#if !defined(STM32_TIM7_HANDLER)
|
||||
#error "STM32_TIM7_HANDLER not defined"
|
||||
#endif
|
||||
/**
|
||||
* @brief TIM7 interrupt handler.
|
||||
*
|
||||
* @isr
|
||||
*/
|
||||
CH_IRQ_HANDLER(STM32_TIM7_HANDLER) {
|
||||
|
||||
CH_IRQ_PROLOGUE();
|
||||
|
||||
gpt_lld_serve_interrupt(&GPTD7);
|
||||
|
||||
CH_IRQ_EPILOGUE();
|
||||
}
|
||||
#endif /* STM32_GPT_USE_TIM7 */
|
||||
|
||||
#if STM32_GPT_USE_TIM8
|
||||
#if !defined(STM32_TIM8_UP_HANDLER)
|
||||
#error "STM32_TIM8_UP_HANDLER not defined"
|
||||
#endif
|
||||
/**
|
||||
* @brief TIM8 interrupt handler.
|
||||
*
|
||||
* @isr
|
||||
*/
|
||||
CH_IRQ_HANDLER(STM32_TIM8_UP_HANDLER) {
|
||||
|
||||
CH_IRQ_PROLOGUE();
|
||||
|
||||
gpt_lld_serve_interrupt(&GPTD8);
|
||||
|
||||
CH_IRQ_EPILOGUE();
|
||||
}
|
||||
#endif /* STM32_GPT_USE_TIM8 */
|
||||
|
||||
#if STM32_GPT_USE_TIM9
|
||||
#if !defined(STM32_TIM9_HANDLER)
|
||||
#error "STM32_TIM9_HANDLER not defined"
|
||||
#endif
|
||||
/**
|
||||
* @brief TIM9 interrupt handler.
|
||||
*
|
||||
* @isr
|
||||
*/
|
||||
CH_IRQ_HANDLER(STM32_TIM9_HANDLER) {
|
||||
|
||||
CH_IRQ_PROLOGUE();
|
||||
|
||||
gpt_lld_serve_interrupt(&GPTD9);
|
||||
|
||||
CH_IRQ_EPILOGUE();
|
||||
}
|
||||
#endif /* STM32_GPT_USE_TIM9 */
|
||||
|
||||
#if STM32_GPT_USE_TIM11
|
||||
#if !defined(STM32_TIM11_HANDLER)
|
||||
#error "STM32_TIM11_HANDLER not defined"
|
||||
#endif
|
||||
/**
|
||||
* @brief TIM11 interrupt handler.
|
||||
*
|
||||
* @isr
|
||||
*/
|
||||
CH_IRQ_HANDLER(STM32_TIM11_HANDLER) {
|
||||
|
||||
CH_IRQ_PROLOGUE();
|
||||
|
||||
gpt_lld_serve_interrupt(&GPTD11);
|
||||
|
||||
CH_IRQ_EPILOGUE();
|
||||
}
|
||||
#endif /* STM32_GPT_USE_TIM11 */
|
||||
|
||||
#if STM32_GPT_USE_TIM12
|
||||
#if !defined(STM32_TIM12_HANDLER)
|
||||
#error "STM32_TIM12_HANDLER not defined"
|
||||
#endif
|
||||
/**
|
||||
* @brief TIM12 interrupt handler.
|
||||
*
|
||||
* @isr
|
||||
*/
|
||||
CH_IRQ_HANDLER(STM32_TIM12_HANDLER) {
|
||||
|
||||
CH_IRQ_PROLOGUE();
|
||||
|
||||
gpt_lld_serve_interrupt(&GPTD12);
|
||||
|
||||
CH_IRQ_EPILOGUE();
|
||||
}
|
||||
#endif /* STM32_GPT_USE_TIM12 */
|
||||
|
||||
#if STM32_GPT_USE_TIM14
|
||||
#if !defined(STM32_TIM14_HANDLER)
|
||||
#error "STM32_TIM14_HANDLER not defined"
|
||||
#endif
|
||||
/**
|
||||
* @brief TIM14 interrupt handler.
|
||||
*
|
||||
* @isr
|
||||
*/
|
||||
CH_IRQ_HANDLER(STM32_TIM14_HANDLER) {
|
||||
|
||||
CH_IRQ_PROLOGUE();
|
||||
|
||||
gpt_lld_serve_interrupt(&GPTD14);
|
||||
|
||||
CH_IRQ_EPILOGUE();
|
||||
}
|
||||
#endif /* STM32_GPT_USE_TIM14 */
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver exported functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Low level GPT driver initialization.
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void gpt_lld_init(void) {
|
||||
|
||||
#if STM32_GPT_USE_TIM1
|
||||
/* Driver initialization.*/
|
||||
GPTD1.tim = STM32_TIM1;
|
||||
gptObjectInit(&GPTD1);
|
||||
#endif
|
||||
|
||||
#if STM32_GPT_USE_TIM2
|
||||
/* Driver initialization.*/
|
||||
GPTD2.tim = STM32_TIM2;
|
||||
gptObjectInit(&GPTD2);
|
||||
#endif
|
||||
|
||||
#if STM32_GPT_USE_TIM3
|
||||
/* Driver initialization.*/
|
||||
GPTD3.tim = STM32_TIM3;
|
||||
gptObjectInit(&GPTD3);
|
||||
#endif
|
||||
|
||||
#if STM32_GPT_USE_TIM4
|
||||
/* Driver initialization.*/
|
||||
GPTD4.tim = STM32_TIM4;
|
||||
gptObjectInit(&GPTD4);
|
||||
#endif
|
||||
|
||||
#if STM32_GPT_USE_TIM5
|
||||
/* Driver initialization.*/
|
||||
GPTD5.tim = STM32_TIM5;
|
||||
gptObjectInit(&GPTD5);
|
||||
#endif
|
||||
|
||||
#if STM32_GPT_USE_TIM6
|
||||
/* Driver initialization.*/
|
||||
GPTD6.tim = STM32_TIM6;
|
||||
gptObjectInit(&GPTD6);
|
||||
#endif
|
||||
|
||||
#if STM32_GPT_USE_TIM7
|
||||
/* Driver initialization.*/
|
||||
GPTD7.tim = STM32_TIM7;
|
||||
gptObjectInit(&GPTD7);
|
||||
#endif
|
||||
|
||||
#if STM32_GPT_USE_TIM8
|
||||
/* Driver initialization.*/
|
||||
GPTD8.tim = STM32_TIM8;
|
||||
gptObjectInit(&GPTD8);
|
||||
#endif
|
||||
|
||||
#if STM32_GPT_USE_TIM9
|
||||
/* Driver initialization.*/
|
||||
GPTD9.tim = STM32_TIM9;
|
||||
gptObjectInit(&GPTD9);
|
||||
#endif
|
||||
|
||||
#if STM32_GPT_USE_TIM11
|
||||
/* Driver initialization.*/
|
||||
GPTD11.tim = STM32_TIM11;
|
||||
gptObjectInit(&GPTD11);
|
||||
#endif
|
||||
|
||||
#if STM32_GPT_USE_TIM12
|
||||
/* Driver initialization.*/
|
||||
GPTD12.tim = STM32_TIM12;
|
||||
gptObjectInit(&GPTD12);
|
||||
#endif
|
||||
|
||||
#if STM32_GPT_USE_TIM14
|
||||
/* Driver initialization.*/
|
||||
GPTD14.tim = STM32_TIM14;
|
||||
gptObjectInit(&GPTD14);
|
||||
#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.*/
|
||||
#if STM32_GPT_USE_TIM1
|
||||
if (&GPTD1 == gptp) {
|
||||
rccEnableTIM1(FALSE);
|
||||
rccResetTIM1();
|
||||
nvicEnableVector(STM32_TIM1_UP_NUMBER,
|
||||
CORTEX_PRIORITY_MASK(STM32_GPT_TIM1_IRQ_PRIORITY));
|
||||
#if defined(STM32_TIM1CLK)
|
||||
gptp->clock = STM32_TIM1CLK;
|
||||
#else
|
||||
gptp->clock = STM32_TIMCLK2;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
#if STM32_GPT_USE_TIM2
|
||||
if (&GPTD2 == gptp) {
|
||||
rccEnableTIM2(FALSE);
|
||||
rccResetTIM2();
|
||||
nvicEnableVector(STM32_TIM2_NUMBER,
|
||||
CORTEX_PRIORITY_MASK(STM32_GPT_TIM2_IRQ_PRIORITY));
|
||||
gptp->clock = STM32_TIMCLK1;
|
||||
}
|
||||
#endif
|
||||
#if STM32_GPT_USE_TIM3
|
||||
if (&GPTD3 == gptp) {
|
||||
rccEnableTIM3(FALSE);
|
||||
rccResetTIM3();
|
||||
nvicEnableVector(STM32_TIM3_NUMBER,
|
||||
CORTEX_PRIORITY_MASK(STM32_GPT_TIM3_IRQ_PRIORITY));
|
||||
gptp->clock = STM32_TIMCLK1;
|
||||
}
|
||||
#endif
|
||||
#if STM32_GPT_USE_TIM4
|
||||
if (&GPTD4 == gptp) {
|
||||
rccEnableTIM4(FALSE);
|
||||
rccResetTIM4();
|
||||
nvicEnableVector(STM32_TIM4_NUMBER,
|
||||
CORTEX_PRIORITY_MASK(STM32_GPT_TIM4_IRQ_PRIORITY));
|
||||
gptp->clock = STM32_TIMCLK1;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if STM32_GPT_USE_TIM5
|
||||
if (&GPTD5 == gptp) {
|
||||
rccEnableTIM5(FALSE);
|
||||
rccResetTIM5();
|
||||
nvicEnableVector(STM32_TIM5_NUMBER,
|
||||
CORTEX_PRIORITY_MASK(STM32_GPT_TIM5_IRQ_PRIORITY));
|
||||
gptp->clock = STM32_TIMCLK1;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if STM32_GPT_USE_TIM6
|
||||
if (&GPTD6 == gptp) {
|
||||
rccEnableTIM6(FALSE);
|
||||
rccResetTIM6();
|
||||
nvicEnableVector(STM32_TIM6_NUMBER,
|
||||
CORTEX_PRIORITY_MASK(STM32_GPT_TIM6_IRQ_PRIORITY));
|
||||
gptp->clock = STM32_TIMCLK1;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if STM32_GPT_USE_TIM7
|
||||
if (&GPTD7 == gptp) {
|
||||
rccEnableTIM7(FALSE);
|
||||
rccResetTIM7();
|
||||
nvicEnableVector(STM32_TIM7_NUMBER,
|
||||
CORTEX_PRIORITY_MASK(STM32_GPT_TIM7_IRQ_PRIORITY));
|
||||
gptp->clock = STM32_TIMCLK1;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if STM32_GPT_USE_TIM8
|
||||
if (&GPTD8 == gptp) {
|
||||
rccEnableTIM8(FALSE);
|
||||
rccResetTIM8();
|
||||
nvicEnableVector(STM32_TIM8_UP_NUMBER,
|
||||
CORTEX_PRIORITY_MASK(STM32_GPT_TIM8_IRQ_PRIORITY));
|
||||
#if defined(STM32_TIM8CLK)
|
||||
gptp->clock = STM32_TIM8CLK;
|
||||
#else
|
||||
gptp->clock = STM32_TIMCLK2;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
#if STM32_GPT_USE_TIM9
|
||||
if (&GPTD9 == gptp) {
|
||||
rccEnableTIM9(FALSE);
|
||||
rccResetTIM9();
|
||||
nvicEnableVector(STM32_TIM9_NUMBER,
|
||||
CORTEX_PRIORITY_MASK(STM32_GPT_TIM9_IRQ_PRIORITY));
|
||||
gptp->clock = STM32_TIMCLK2;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if STM32_GPT_USE_TIM11
|
||||
if (&GPTD11 == gptp) {
|
||||
rccEnableTIM11(FALSE);
|
||||
rccResetTIM11();
|
||||
nvicEnableVector(STM32_TIM11_NUMBER,
|
||||
CORTEX_PRIORITY_MASK(STM32_GPT_TIM11_IRQ_PRIORITY));
|
||||
gptp->clock = STM32_TIMCLK2;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if STM32_GPT_USE_TIM12
|
||||
if (&GPTD12 == gptp) {
|
||||
rccEnableTIM12(FALSE);
|
||||
rccResetTIM12();
|
||||
nvicEnableVector(STM32_TIM12_NUMBER,
|
||||
CORTEX_PRIORITY_MASK(STM32_GPT_TIM12_IRQ_PRIORITY));
|
||||
gptp->clock = STM32_TIMCLK1;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if STM32_GPT_USE_TIM14
|
||||
if (&GPTD14 == gptp) {
|
||||
rccEnableTIM14(FALSE);
|
||||
rccResetTIM14();
|
||||
nvicEnableVector(STM32_TIM14_NUMBER,
|
||||
CORTEX_PRIORITY_MASK(STM32_GPT_TIM14_IRQ_PRIORITY));
|
||||
gptp->clock = STM32_TIMCLK1;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Prescaler value calculation.*/
|
||||
psc = (uint16_t)((gptp->clock / gptp->config->frequency) - 1);
|
||||
chDbgAssert(((uint32_t)(psc + 1) * gptp->config->frequency) == gptp->clock,
|
||||
"gpt_lld_start(), #1", "invalid frequency");
|
||||
|
||||
/* Timer configuration.*/
|
||||
gptp->tim->CR1 = 0; /* Initially stopped. */
|
||||
gptp->tim->CR2 = STM32_TIM_CR2_CCDS; /* DMA on UE (if any). */
|
||||
gptp->tim->PSC = psc; /* Prescaler value. */
|
||||
gptp->tim->DIER = gptp->config->dier & /* DMA-related DIER bits. */
|
||||
STM32_TIM_DIER_IRQ_MASK;
|
||||
gptp->tim->SR = 0; /* Clear pending IRQs. */
|
||||
}
|
||||
|
||||
/**
|
||||
* @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) {
|
||||
gptp->tim->CR1 = 0; /* Timer disabled. */
|
||||
gptp->tim->DIER = 0; /* All IRQs disabled. */
|
||||
gptp->tim->SR = 0; /* Clear pending IRQs. */
|
||||
|
||||
#if STM32_GPT_USE_TIM1
|
||||
if (&GPTD1 == gptp) {
|
||||
nvicDisableVector(STM32_TIM1_UP_NUMBER);
|
||||
rccDisableTIM1(FALSE);
|
||||
}
|
||||
#endif
|
||||
#if STM32_GPT_USE_TIM2
|
||||
if (&GPTD2 == gptp) {
|
||||
nvicDisableVector(STM32_TIM2_NUMBER);
|
||||
rccDisableTIM2(FALSE);
|
||||
}
|
||||
#endif
|
||||
#if STM32_GPT_USE_TIM3
|
||||
if (&GPTD3 == gptp) {
|
||||
nvicDisableVector(STM32_TIM3_NUMBER);
|
||||
rccDisableTIM3(FALSE);
|
||||
}
|
||||
#endif
|
||||
#if STM32_GPT_USE_TIM4
|
||||
if (&GPTD4 == gptp) {
|
||||
nvicDisableVector(STM32_TIM4_NUMBER);
|
||||
rccDisableTIM4(FALSE);
|
||||
}
|
||||
#endif
|
||||
#if STM32_GPT_USE_TIM5
|
||||
if (&GPTD5 == gptp) {
|
||||
nvicDisableVector(STM32_TIM5_NUMBER);
|
||||
rccDisableTIM5(FALSE);
|
||||
}
|
||||
#endif
|
||||
#if STM32_GPT_USE_TIM6
|
||||
if (&GPTD6 == gptp) {
|
||||
nvicDisableVector(STM32_TIM6_NUMBER);
|
||||
rccDisableTIM6(FALSE);
|
||||
}
|
||||
#endif
|
||||
#if STM32_GPT_USE_TIM7
|
||||
if (&GPTD7 == gptp) {
|
||||
nvicDisableVector(STM32_TIM7_NUMBER);
|
||||
rccDisableTIM7(FALSE);
|
||||
}
|
||||
#endif
|
||||
#if STM32_GPT_USE_TIM8
|
||||
if (&GPTD8 == gptp) {
|
||||
nvicDisableVector(STM32_TIM8_UP_NUMBER);
|
||||
rccDisableTIM8(FALSE);
|
||||
}
|
||||
#endif
|
||||
#if STM32_GPT_USE_TIM9
|
||||
if (&GPTD9 == gptp) {
|
||||
nvicDisableVector(STM32_TIM9_NUMBER);
|
||||
rccDisableTIM9(FALSE);
|
||||
}
|
||||
#endif
|
||||
#if STM32_GPT_USE_TIM11
|
||||
if (&GPTD11 == gptp) {
|
||||
nvicDisableVector(STM32_TIM11_NUMBER);
|
||||
rccDisableTIM11(FALSE);
|
||||
}
|
||||
#endif
|
||||
#if STM32_GPT_USE_TIM12
|
||||
if (&GPTD12 == gptp) {
|
||||
nvicDisableVector(STM32_TIM12_NUMBER);
|
||||
rccDisableTIM12(FALSE);
|
||||
}
|
||||
#endif
|
||||
#if STM32_GPT_USE_TIM14
|
||||
if (&GPTD14 == gptp) {
|
||||
nvicDisableVector(STM32_TIM14_NUMBER);
|
||||
rccDisableTIM14(FALSE);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @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) {
|
||||
|
||||
gptp->tim->ARR = (uint32_t)(interval - 1); /* Time constant. */
|
||||
gptp->tim->EGR = STM32_TIM_EGR_UG; /* Update event. */
|
||||
gptp->tim->CNT = 0; /* Reset counter. */
|
||||
|
||||
/* NOTE: After generating the UG event it takes several clock cycles before
|
||||
SR bit 0 goes to 1. This is because the clearing of CNT has been inserted
|
||||
before the clearing of SR, to give it some time.*/
|
||||
gptp->tim->SR = 0; /* Clear pending IRQs. */
|
||||
gptp->tim->DIER |= STM32_TIM_DIER_UIE; /* Update Event IRQ enabled.*/
|
||||
gptp->tim->CR1 = STM32_TIM_CR1_URS | STM32_TIM_CR1_CEN;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Stops the timer.
|
||||
*
|
||||
* @param[in] gptp pointer to the @p GPTDriver object
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void gpt_lld_stop_timer(GPTDriver *gptp) {
|
||||
|
||||
gptp->tim->CR1 = 0; /* Initially stopped. */
|
||||
gptp->tim->SR = 0; /* Clear pending IRQs. */
|
||||
|
||||
/* All interrupts disabled.*/
|
||||
gptp->tim->DIER &= ~STM32_TIM_DIER_IRQ_MASK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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) {
|
||||
|
||||
gptp->tim->ARR = (uint32_t)(interval - 1); /* Time constant. */
|
||||
gptp->tim->EGR = STM32_TIM_EGR_UG; /* Update event. */
|
||||
gptp->tim->SR = 0; /* Clear pending IRQs. */
|
||||
gptp->tim->CR1 = STM32_TIM_CR1_OPM | STM32_TIM_CR1_URS | STM32_TIM_CR1_CEN;
|
||||
while (!(gptp->tim->SR & STM32_TIM_SR_UIF))
|
||||
;
|
||||
}
|
||||
|
||||
#endif /* HAL_USE_GPT */
|
||||
|
||||
/** @} */
|
||||
+512
@@ -0,0 +1,512 @@
|
||||
/*
|
||||
ChibiOS/RT - Copyright (C) 2006-2013 Giovanni Di Sirio
|
||||
|
||||
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 STM32/gpt_lld.h
|
||||
* @brief STM32 GPT subsystem low level driver header.
|
||||
*
|
||||
* @addtogroup GPT
|
||||
* @{
|
||||
*/
|
||||
|
||||
#ifndef _GPT_LLD_H_
|
||||
#define _GPT_LLD_H_
|
||||
|
||||
#include "stm32_tim.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(STM32_GPT_USE_TIM1) || defined(__DOXYGEN__)
|
||||
#define STM32_GPT_USE_TIM1 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(STM32_GPT_USE_TIM2) || defined(__DOXYGEN__)
|
||||
#define STM32_GPT_USE_TIM2 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(STM32_GPT_USE_TIM3) || defined(__DOXYGEN__)
|
||||
#define STM32_GPT_USE_TIM3 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(STM32_GPT_USE_TIM4) || defined(__DOXYGEN__)
|
||||
#define STM32_GPT_USE_TIM4 FALSE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief GPTD5 driver enable switch.
|
||||
* @details If set to @p TRUE the support for GPTD5 is included.
|
||||
* @note The default is @p TRUE.
|
||||
*/
|
||||
#if !defined(STM32_GPT_USE_TIM5) || defined(__DOXYGEN__)
|
||||
#define STM32_GPT_USE_TIM5 FALSE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief GPTD6 driver enable switch.
|
||||
* @details If set to @p TRUE the support for GPTD6 is included.
|
||||
* @note The default is @p TRUE.
|
||||
*/
|
||||
#if !defined(STM32_GPT_USE_TIM6) || defined(__DOXYGEN__)
|
||||
#define STM32_GPT_USE_TIM6 FALSE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief GPTD7 driver enable switch.
|
||||
* @details If set to @p TRUE the support for GPTD7 is included.
|
||||
* @note The default is @p TRUE.
|
||||
*/
|
||||
#if !defined(STM32_GPT_USE_TIM7) || defined(__DOXYGEN__)
|
||||
#define STM32_GPT_USE_TIM7 FALSE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief GPTD8 driver enable switch.
|
||||
* @details If set to @p TRUE the support for GPTD8 is included.
|
||||
* @note The default is @p TRUE.
|
||||
*/
|
||||
#if !defined(STM32_GPT_USE_TIM8) || defined(__DOXYGEN__)
|
||||
#define STM32_GPT_USE_TIM8 FALSE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief GPTD9 driver enable switch.
|
||||
* @details If set to @p TRUE the support for GPTD9 is included.
|
||||
* @note The default is @p TRUE.
|
||||
*/
|
||||
#if !defined(STM32_GPT_USE_TIM9) || defined(__DOXYGEN__)
|
||||
#define STM32_GPT_USE_TIM9 FALSE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief GPTD11 driver enable switch.
|
||||
* @details If set to @p TRUE the support for GPTD11 is included.
|
||||
* @note The default is @p TRUE.
|
||||
*/
|
||||
#if !defined(STM32_GPT_USE_TIM11) || defined(__DOXYGEN__)
|
||||
#define STM32_GPT_USE_TIM11 FALSE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief GPTD12 driver enable switch.
|
||||
* @details If set to @p TRUE the support for GPTD12 is included.
|
||||
* @note The default is @p TRUE.
|
||||
*/
|
||||
#if !defined(STM32_GPT_USE_TIM12) || defined(__DOXYGEN__)
|
||||
#define STM32_GPT_USE_TIM12 FALSE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief GPTD14 driver enable switch.
|
||||
* @details If set to @p TRUE the support for GPTD14 is included.
|
||||
* @note The default is @p TRUE.
|
||||
*/
|
||||
#if !defined(STM32_GPT_USE_TIM14) || defined(__DOXYGEN__)
|
||||
#define STM32_GPT_USE_TIM14 FALSE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief GPTD1 interrupt priority level setting.
|
||||
*/
|
||||
#if !defined(STM32_GPT_TIM1_IRQ_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define STM32_GPT_TIM1_IRQ_PRIORITY 7
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief GPTD2 interrupt priority level setting.
|
||||
*/
|
||||
#if !defined(STM32_GPT_TIM2_IRQ_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define STM32_GPT_TIM2_IRQ_PRIORITY 7
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief GPTD3 interrupt priority level setting.
|
||||
*/
|
||||
#if !defined(STM32_GPT_TIM3_IRQ_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define STM32_GPT_TIM3_IRQ_PRIORITY 7
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief GPTD4 interrupt priority level setting.
|
||||
*/
|
||||
#if !defined(STM32_GPT_TIM4_IRQ_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define STM32_GPT_TIM4_IRQ_PRIORITY 7
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief GPTD5 interrupt priority level setting.
|
||||
*/
|
||||
#if !defined(STM32_GPT_TIM5_IRQ_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define STM32_GPT_TIM5_IRQ_PRIORITY 7
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief GPTD6 interrupt priority level setting.
|
||||
*/
|
||||
#if !defined(STM32_GPT_TIM6_IRQ_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define STM32_GPT_TIM6_IRQ_PRIORITY 7
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief GPTD7 interrupt priority level setting.
|
||||
*/
|
||||
#if !defined(STM32_GPT_TIM7_IRQ_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define STM32_GPT_TIM7_IRQ_PRIORITY 7
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief GPTD8 interrupt priority level setting.
|
||||
*/
|
||||
#if !defined(STM32_GPT_TIM8_IRQ_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define STM32_GPT_TIM8_IRQ_PRIORITY 7
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief GPTD9 interrupt priority level setting.
|
||||
*/
|
||||
#if !defined(STM32_GPT_TIM9_IRQ_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define STM32_GPT_TIM9_IRQ_PRIORITY 7
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief GPTD11 interrupt priority level setting.
|
||||
*/
|
||||
#if !defined(STM32_GPT_TIM11_IRQ_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define STM32_GPT_TIM11_IRQ_PRIORITY 7
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief GPTD12 interrupt priority level setting.
|
||||
*/
|
||||
#if !defined(STM32_GPT_TIM12_IRQ_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define STM32_GPT_TIM12_IRQ_PRIORITY 7
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief GPTD14 interrupt priority level setting.
|
||||
*/
|
||||
#if !defined(STM32_GPT_TIM14_IRQ_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define STM32_GPT_TIM14_IRQ_PRIORITY 7
|
||||
#endif
|
||||
/** @} */
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Derived constants and error checks. */
|
||||
/*===========================================================================*/
|
||||
|
||||
#if STM32_GPT_USE_TIM1 && !STM32_HAS_TIM1
|
||||
#error "TIM1 not present in the selected device"
|
||||
#endif
|
||||
|
||||
#if STM32_GPT_USE_TIM2 && !STM32_HAS_TIM2
|
||||
#error "TIM2 not present in the selected device"
|
||||
#endif
|
||||
|
||||
#if STM32_GPT_USE_TIM3 && !STM32_HAS_TIM3
|
||||
#error "TIM3 not present in the selected device"
|
||||
#endif
|
||||
|
||||
#if STM32_GPT_USE_TIM4 && !STM32_HAS_TIM4
|
||||
#error "TIM4 not present in the selected device"
|
||||
#endif
|
||||
|
||||
#if STM32_GPT_USE_TIM5 && !STM32_HAS_TIM5
|
||||
#error "TIM5 not present in the selected device"
|
||||
#endif
|
||||
|
||||
#if STM32_GPT_USE_TIM6 && !STM32_HAS_TIM6
|
||||
#error "TIM6 not present in the selected device"
|
||||
#endif
|
||||
|
||||
#if STM32_GPT_USE_TIM7 && !STM32_HAS_TIM7
|
||||
#error "TIM7 not present in the selected device"
|
||||
#endif
|
||||
|
||||
#if STM32_GPT_USE_TIM8 && !STM32_HAS_TIM8
|
||||
#error "TIM8 not present in the selected device"
|
||||
#endif
|
||||
|
||||
#if STM32_GPT_USE_TIM9 && !STM32_HAS_TIM9
|
||||
#error "TIM9 not present in the selected device"
|
||||
#endif
|
||||
|
||||
#if STM32_GPT_USE_TIM11 && !STM32_HAS_TIM11
|
||||
#error "TIM11 not present in the selected device"
|
||||
#endif
|
||||
|
||||
#if STM32_GPT_USE_TIM12 && !STM32_HAS_TIM12
|
||||
#error "TIM12 not present in the selected device"
|
||||
#endif
|
||||
|
||||
#if STM32_GPT_USE_TIM14 && !STM32_HAS_TIM14
|
||||
#error "TIM14 not present in the selected device"
|
||||
#endif
|
||||
|
||||
#if !STM32_GPT_USE_TIM1 && !STM32_GPT_USE_TIM2 && \
|
||||
!STM32_GPT_USE_TIM3 && !STM32_GPT_USE_TIM4 && \
|
||||
!STM32_GPT_USE_TIM5 && !STM32_GPT_USE_TIM6 && \
|
||||
!STM32_GPT_USE_TIM7 && !STM32_GPT_USE_TIM8 && \
|
||||
!STM32_GPT_USE_TIM9 && !STM32_GPT_USE_TIM11 && \
|
||||
!STM32_GPT_USE_TIM12 && !STM32_GPT_USE_TIM14
|
||||
#error "GPT driver activated but no TIM peripheral assigned"
|
||||
#endif
|
||||
|
||||
#if STM32_GPT_USE_TIM1 && \
|
||||
!CORTEX_IS_VALID_KERNEL_PRIORITY(STM32_GPT_TIM1_IRQ_PRIORITY)
|
||||
#error "Invalid IRQ priority assigned to TIM1"
|
||||
#endif
|
||||
|
||||
#if STM32_GPT_USE_TIM2 && \
|
||||
!CORTEX_IS_VALID_KERNEL_PRIORITY(STM32_GPT_TIM2_IRQ_PRIORITY)
|
||||
#error "Invalid IRQ priority assigned to TIM2"
|
||||
#endif
|
||||
|
||||
#if STM32_GPT_USE_TIM3 && \
|
||||
!CORTEX_IS_VALID_KERNEL_PRIORITY(STM32_GPT_TIM3_IRQ_PRIORITY)
|
||||
#error "Invalid IRQ priority assigned to TIM3"
|
||||
#endif
|
||||
|
||||
#if STM32_GPT_USE_TIM4 && \
|
||||
!CORTEX_IS_VALID_KERNEL_PRIORITY(STM32_GPT_TIM4_IRQ_PRIORITY)
|
||||
#error "Invalid IRQ priority assigned to TIM4"
|
||||
#endif
|
||||
|
||||
#if STM32_GPT_USE_TIM5 && \
|
||||
!CORTEX_IS_VALID_KERNEL_PRIORITY(STM32_GPT_TIM5_IRQ_PRIORITY)
|
||||
#error "Invalid IRQ priority assigned to TIM5"
|
||||
#endif
|
||||
|
||||
#if STM32_GPT_USE_TIM6 && \
|
||||
!CORTEX_IS_VALID_KERNEL_PRIORITY(STM32_GPT_TIM6_IRQ_PRIORITY)
|
||||
#error "Invalid IRQ priority assigned to TIM6"
|
||||
#endif
|
||||
|
||||
#if STM32_GPT_USE_TIM7 && \
|
||||
!CORTEX_IS_VALID_KERNEL_PRIORITY(STM32_GPT_TIM7_IRQ_PRIORITY)
|
||||
#error "Invalid IRQ priority assigned to TIM7"
|
||||
#endif
|
||||
|
||||
#if STM32_GPT_USE_TIM8 && \
|
||||
!CORTEX_IS_VALID_KERNEL_PRIORITY(STM32_GPT_TIM8_IRQ_PRIORITY)
|
||||
#error "Invalid IRQ priority assigned to TIM8"
|
||||
#endif
|
||||
|
||||
#if STM32_GPT_USE_TIM9 && \
|
||||
!CORTEX_IS_VALID_KERNEL_PRIORITY(STM32_GPT_TIM9_IRQ_PRIORITY)
|
||||
#error "Invalid IRQ priority assigned to TIM9"
|
||||
#endif
|
||||
|
||||
#if STM32_GPT_USE_TIM11 && \
|
||||
!CORTEX_IS_VALID_KERNEL_PRIORITY(STM32_GPT_TIM11_IRQ_PRIORITY)
|
||||
#error "Invalid IRQ priority assigned to TIM11"
|
||||
#endif
|
||||
|
||||
#if STM32_GPT_USE_TIM12 && \
|
||||
!CORTEX_IS_VALID_KERNEL_PRIORITY(STM32_GPT_TIM12_IRQ_PRIORITY)
|
||||
#error "Invalid IRQ priority assigned to TIM12"
|
||||
#endif
|
||||
|
||||
#if STM32_GPT_USE_TIM14 && \
|
||||
!CORTEX_IS_VALID_KERNEL_PRIORITY(STM32_GPT_TIM14_IRQ_PRIORITY)
|
||||
#error "Invalid IRQ priority assigned to TIM14"
|
||||
#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.
|
||||
*/
|
||||
gptcallback_t callback;
|
||||
/* End of the mandatory fields.*/
|
||||
/**
|
||||
* @brief TIM DIER register initialization data.
|
||||
* @note The value of this field should normally be equal to zero.
|
||||
* @note Only the DMA-related bits can be specified in this field.
|
||||
*/
|
||||
uint32_t dier;
|
||||
} 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 Pointer to the TIMx registers block.
|
||||
*/
|
||||
stm32_tim_t *tim;
|
||||
};
|
||||
|
||||
/*===========================================================================*/
|
||||
/* 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 have been activated using @p gptStart().
|
||||
* @pre The GPT unit must have been running in continuous mode using
|
||||
* @p gptStartContinuous().
|
||||
* @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)->tim->ARR = (uint32_t)((interval) - 1))
|
||||
|
||||
/*===========================================================================*/
|
||||
/* External declarations. */
|
||||
/*===========================================================================*/
|
||||
|
||||
#if STM32_GPT_USE_TIM1 && !defined(__DOXYGEN__)
|
||||
extern GPTDriver GPTD1;
|
||||
#endif
|
||||
|
||||
#if STM32_GPT_USE_TIM2 && !defined(__DOXYGEN__)
|
||||
extern GPTDriver GPTD2;
|
||||
#endif
|
||||
|
||||
#if STM32_GPT_USE_TIM3 && !defined(__DOXYGEN__)
|
||||
extern GPTDriver GPTD3;
|
||||
#endif
|
||||
|
||||
#if STM32_GPT_USE_TIM4 && !defined(__DOXYGEN__)
|
||||
extern GPTDriver GPTD4;
|
||||
#endif
|
||||
|
||||
#if STM32_GPT_USE_TIM5 && !defined(__DOXYGEN__)
|
||||
extern GPTDriver GPTD5;
|
||||
#endif
|
||||
|
||||
#if STM32_GPT_USE_TIM6 && !defined(__DOXYGEN__)
|
||||
extern GPTDriver GPTD6;
|
||||
#endif
|
||||
|
||||
#if STM32_GPT_USE_TIM7 && !defined(__DOXYGEN__)
|
||||
extern GPTDriver GPTD7;
|
||||
#endif
|
||||
|
||||
#if STM32_GPT_USE_TIM8 && !defined(__DOXYGEN__)
|
||||
extern GPTDriver GPTD8;
|
||||
#endif
|
||||
|
||||
#if STM32_GPT_USE_TIM9 && !defined(__DOXYGEN__)
|
||||
extern GPTDriver GPTD9;
|
||||
#endif
|
||||
|
||||
#if STM32_GPT_USE_TIM11 && !defined(__DOXYGEN__)
|
||||
extern GPTDriver GPTD11;
|
||||
#endif
|
||||
|
||||
#if STM32_GPT_USE_TIM12 && !defined(__DOXYGEN__)
|
||||
extern GPTDriver GPTD12;
|
||||
#endif
|
||||
|
||||
#if STM32_GPT_USE_TIM14 && !defined(__DOXYGEN__)
|
||||
extern GPTDriver GPTD14;
|
||||
#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 /* _GPT_LLD_H_ */
|
||||
|
||||
/** @} */
|
||||
+653
@@ -0,0 +1,653 @@
|
||||
/*
|
||||
ChibiOS/RT - Copyright (C) 2006-2013 Giovanni Di Sirio
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
/*
|
||||
Concepts and parts of this file have been contributed by Fabio Utzig and
|
||||
Xo Wang.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file STM32/icu_lld.c
|
||||
* @brief STM32 ICU subsystem low level driver header.
|
||||
*
|
||||
* @addtogroup ICU
|
||||
* @{
|
||||
*/
|
||||
|
||||
#include "ch.h"
|
||||
#include "hal.h"
|
||||
|
||||
#if HAL_USE_ICU || defined(__DOXYGEN__)
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local definitions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver exported variables. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief ICUD1 driver identifier.
|
||||
* @note The driver ICUD1 allocates the complex timer TIM1 when enabled.
|
||||
*/
|
||||
#if STM32_ICU_USE_TIM1 || defined(__DOXYGEN__)
|
||||
ICUDriver ICUD1;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief ICUD2 driver identifier.
|
||||
* @note The driver ICUD1 allocates the timer TIM2 when enabled.
|
||||
*/
|
||||
#if STM32_ICU_USE_TIM2 || defined(__DOXYGEN__)
|
||||
ICUDriver ICUD2;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief ICUD3 driver identifier.
|
||||
* @note The driver ICUD1 allocates the timer TIM3 when enabled.
|
||||
*/
|
||||
#if STM32_ICU_USE_TIM3 || defined(__DOXYGEN__)
|
||||
ICUDriver ICUD3;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief ICUD4 driver identifier.
|
||||
* @note The driver ICUD4 allocates the timer TIM4 when enabled.
|
||||
*/
|
||||
#if STM32_ICU_USE_TIM4 || defined(__DOXYGEN__)
|
||||
ICUDriver ICUD4;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief ICUD5 driver identifier.
|
||||
* @note The driver ICUD5 allocates the timer TIM5 when enabled.
|
||||
*/
|
||||
#if STM32_ICU_USE_TIM5 || defined(__DOXYGEN__)
|
||||
ICUDriver ICUD5;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief ICUD8 driver identifier.
|
||||
* @note The driver ICUD8 allocates the timer TIM8 when enabled.
|
||||
*/
|
||||
#if STM32_ICU_USE_TIM8 || defined(__DOXYGEN__)
|
||||
ICUDriver ICUD8;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief ICUD9 driver identifier.
|
||||
* @note The driver ICUD9 allocates the timer TIM9 when enabled.
|
||||
*/
|
||||
#if STM32_ICU_USE_TIM9 || defined(__DOXYGEN__)
|
||||
ICUDriver ICUD9;
|
||||
#endif
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local variables and types. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Shared IRQ handler.
|
||||
*
|
||||
* @param[in] icup pointer to the @p ICUDriver object
|
||||
*/
|
||||
static void icu_lld_serve_interrupt(ICUDriver *icup) {
|
||||
uint16_t sr;
|
||||
|
||||
sr = icup->tim->SR;
|
||||
sr &= icup->tim->DIER & STM32_TIM_DIER_IRQ_MASK;
|
||||
icup->tim->SR = ~sr;
|
||||
if (icup->config->channel == ICU_CHANNEL_1) {
|
||||
if ((sr & STM32_TIM_SR_CC1IF) != 0)
|
||||
_icu_isr_invoke_period_cb(icup);
|
||||
if ((sr & STM32_TIM_SR_CC2IF) != 0)
|
||||
_icu_isr_invoke_width_cb(icup);
|
||||
} else {
|
||||
if ((sr & STM32_TIM_SR_CC1IF) != 0)
|
||||
_icu_isr_invoke_width_cb(icup);
|
||||
if ((sr & STM32_TIM_SR_CC2IF) != 0)
|
||||
_icu_isr_invoke_period_cb(icup);
|
||||
}
|
||||
if ((sr & STM32_TIM_SR_UIF) != 0)
|
||||
_icu_isr_invoke_overflow_cb(icup);
|
||||
}
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver interrupt handlers. */
|
||||
/*===========================================================================*/
|
||||
|
||||
#if STM32_ICU_USE_TIM1
|
||||
#if !defined(STM32_TIM1_UP_HANDLER)
|
||||
#error "STM32_TIM1_UP_HANDLER not defined"
|
||||
#endif
|
||||
/**
|
||||
* @brief TIM1 compare interrupt handler.
|
||||
* @note It is assumed that the various sources are only activated if the
|
||||
* associated callback pointer is not equal to @p NULL in order to not
|
||||
* perform an extra check in a potentially critical interrupt handler.
|
||||
*
|
||||
* @isr
|
||||
*/
|
||||
CH_IRQ_HANDLER(STM32_TIM1_UP_HANDLER) {
|
||||
|
||||
CH_IRQ_PROLOGUE();
|
||||
|
||||
icu_lld_serve_interrupt(&ICUD1);
|
||||
|
||||
CH_IRQ_EPILOGUE();
|
||||
}
|
||||
|
||||
#if !defined(STM32_TIM1_CC_HANDLER)
|
||||
#error "STM32_TIM1_CC_HANDLER not defined"
|
||||
#endif
|
||||
/**
|
||||
* @brief TIM1 compare interrupt handler.
|
||||
* @note It is assumed that the various sources are only activated if the
|
||||
* associated callback pointer is not equal to @p NULL in order to not
|
||||
* perform an extra check in a potentially critical interrupt handler.
|
||||
*
|
||||
* @isr
|
||||
*/
|
||||
CH_IRQ_HANDLER(STM32_TIM1_CC_HANDLER) {
|
||||
|
||||
CH_IRQ_PROLOGUE();
|
||||
|
||||
icu_lld_serve_interrupt(&ICUD1);
|
||||
|
||||
CH_IRQ_EPILOGUE();
|
||||
}
|
||||
#endif /* STM32_ICU_USE_TIM1 */
|
||||
|
||||
#if STM32_ICU_USE_TIM2
|
||||
#if !defined(STM32_TIM2_HANDLER)
|
||||
#error "STM32_TIM2_HANDLER not defined"
|
||||
#endif
|
||||
/**
|
||||
* @brief TIM2 interrupt handler.
|
||||
* @note It is assumed that the various sources are only activated if the
|
||||
* associated callback pointer is not equal to @p NULL in order to not
|
||||
* perform an extra check in a potentially critical interrupt handler.
|
||||
*
|
||||
* @isr
|
||||
*/
|
||||
CH_IRQ_HANDLER(STM32_TIM2_HANDLER) {
|
||||
|
||||
CH_IRQ_PROLOGUE();
|
||||
|
||||
icu_lld_serve_interrupt(&ICUD2);
|
||||
|
||||
CH_IRQ_EPILOGUE();
|
||||
}
|
||||
#endif /* STM32_ICU_USE_TIM2 */
|
||||
|
||||
#if STM32_ICU_USE_TIM3
|
||||
#if !defined(STM32_TIM3_HANDLER)
|
||||
#error "STM32_TIM3_HANDLER not defined"
|
||||
#endif
|
||||
/**
|
||||
* @brief TIM3 interrupt handler.
|
||||
* @note It is assumed that the various sources are only activated if the
|
||||
* associated callback pointer is not equal to @p NULL in order to not
|
||||
* perform an extra check in a potentially critical interrupt handler.
|
||||
*
|
||||
* @isr
|
||||
*/
|
||||
CH_IRQ_HANDLER(STM32_TIM3_HANDLER) {
|
||||
|
||||
CH_IRQ_PROLOGUE();
|
||||
|
||||
icu_lld_serve_interrupt(&ICUD3);
|
||||
|
||||
CH_IRQ_EPILOGUE();
|
||||
}
|
||||
#endif /* STM32_ICU_USE_TIM3 */
|
||||
|
||||
#if STM32_ICU_USE_TIM4
|
||||
#if !defined(STM32_TIM4_HANDLER)
|
||||
#error "STM32_TIM4_HANDLER not defined"
|
||||
#endif
|
||||
/**
|
||||
* @brief TIM4 interrupt handler.
|
||||
* @note It is assumed that the various sources are only activated if the
|
||||
* associated callback pointer is not equal to @p NULL in order to not
|
||||
* perform an extra check in a potentially critical interrupt handler.
|
||||
*
|
||||
* @isr
|
||||
*/
|
||||
CH_IRQ_HANDLER(STM32_TIM4_HANDLER) {
|
||||
|
||||
CH_IRQ_PROLOGUE();
|
||||
|
||||
icu_lld_serve_interrupt(&ICUD4);
|
||||
|
||||
CH_IRQ_EPILOGUE();
|
||||
}
|
||||
#endif /* STM32_ICU_USE_TIM4 */
|
||||
|
||||
#if STM32_ICU_USE_TIM5
|
||||
#if !defined(STM32_TIM5_HANDLER)
|
||||
#error "STM32_TIM5_HANDLER not defined"
|
||||
#endif
|
||||
/**
|
||||
* @brief TIM5 interrupt handler.
|
||||
* @note It is assumed that the various sources are only activated if the
|
||||
* associated callback pointer is not equal to @p NULL in order to not
|
||||
* perform an extra check in a potentially critical interrupt handler.
|
||||
*
|
||||
* @isr
|
||||
*/
|
||||
CH_IRQ_HANDLER(STM32_TIM5_HANDLER) {
|
||||
|
||||
CH_IRQ_PROLOGUE();
|
||||
|
||||
icu_lld_serve_interrupt(&ICUD5);
|
||||
|
||||
CH_IRQ_EPILOGUE();
|
||||
}
|
||||
#endif /* STM32_ICU_USE_TIM5 */
|
||||
|
||||
#if STM32_ICU_USE_TIM8
|
||||
#if !defined(STM32_TIM8_UP_HANDLER)
|
||||
#error "STM32_TIM8_UP_HANDLER not defined"
|
||||
#endif
|
||||
/**
|
||||
* @brief TIM8 compare interrupt handler.
|
||||
* @note It is assumed that the various sources are only activated if the
|
||||
* associated callback pointer is not equal to @p NULL in order to not
|
||||
* perform an extra check in a potentially critical interrupt handler.
|
||||
*
|
||||
* @isr
|
||||
*/
|
||||
CH_IRQ_HANDLER(STM32_TIM8_UP_HANDLER) {
|
||||
|
||||
CH_IRQ_PROLOGUE();
|
||||
|
||||
icu_lld_serve_interrupt(&ICUD8);
|
||||
|
||||
CH_IRQ_EPILOGUE();
|
||||
}
|
||||
|
||||
#if !defined(STM32_TIM8_CC_HANDLER)
|
||||
#error "STM32_TIM8_CC_HANDLER not defined"
|
||||
#endif
|
||||
/**
|
||||
* @brief TIM8 compare interrupt handler.
|
||||
* @note It is assumed that the various sources are only activated if the
|
||||
* associated callback pointer is not equal to @p NULL in order to not
|
||||
* perform an extra check in a potentially critical interrupt handler.
|
||||
*
|
||||
* @isr
|
||||
*/
|
||||
CH_IRQ_HANDLER(STM32_TIM8_CC_HANDLER) {
|
||||
|
||||
CH_IRQ_PROLOGUE();
|
||||
|
||||
icu_lld_serve_interrupt(&ICUD8);
|
||||
|
||||
CH_IRQ_EPILOGUE();
|
||||
}
|
||||
#endif /* STM32_ICU_USE_TIM8 */
|
||||
|
||||
#if STM32_ICU_USE_TIM9
|
||||
#if !defined(STM32_TIM9_HANDLER)
|
||||
#error "STM32_TIM9_HANDLER not defined"
|
||||
#endif
|
||||
/**
|
||||
* @brief TIM9 interrupt handler.
|
||||
* @note It is assumed that the various sources are only activated if the
|
||||
* associated callback pointer is not equal to @p NULL in order to not
|
||||
* perform an extra check in a potentially critical interrupt handler.
|
||||
*
|
||||
* @isr
|
||||
*/
|
||||
CH_IRQ_HANDLER(STM32_TIM9_HANDLER) {
|
||||
|
||||
CH_IRQ_PROLOGUE();
|
||||
|
||||
icu_lld_serve_interrupt(&ICUD9);
|
||||
|
||||
CH_IRQ_EPILOGUE();
|
||||
}
|
||||
#endif /* STM32_ICU_USE_TIM9 */
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver exported functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Low level ICU driver initialization.
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void icu_lld_init(void) {
|
||||
|
||||
#if STM32_ICU_USE_TIM1
|
||||
/* Driver initialization.*/
|
||||
icuObjectInit(&ICUD1);
|
||||
ICUD1.tim = STM32_TIM1;
|
||||
#endif
|
||||
|
||||
#if STM32_ICU_USE_TIM2
|
||||
/* Driver initialization.*/
|
||||
icuObjectInit(&ICUD2);
|
||||
ICUD2.tim = STM32_TIM2;
|
||||
#endif
|
||||
|
||||
#if STM32_ICU_USE_TIM3
|
||||
/* Driver initialization.*/
|
||||
icuObjectInit(&ICUD3);
|
||||
ICUD3.tim = STM32_TIM3;
|
||||
#endif
|
||||
|
||||
#if STM32_ICU_USE_TIM4
|
||||
/* Driver initialization.*/
|
||||
icuObjectInit(&ICUD4);
|
||||
ICUD4.tim = STM32_TIM4;
|
||||
#endif
|
||||
|
||||
#if STM32_ICU_USE_TIM5
|
||||
/* Driver initialization.*/
|
||||
icuObjectInit(&ICUD5);
|
||||
ICUD5.tim = STM32_TIM5;
|
||||
#endif
|
||||
|
||||
#if STM32_ICU_USE_TIM8
|
||||
/* Driver initialization.*/
|
||||
icuObjectInit(&ICUD8);
|
||||
ICUD8.tim = STM32_TIM8;
|
||||
#endif
|
||||
|
||||
#if STM32_ICU_USE_TIM9
|
||||
/* Driver initialization.*/
|
||||
icuObjectInit(&ICUD9);
|
||||
ICUD9.tim = STM32_TIM9;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Configures and activates the ICU peripheral.
|
||||
*
|
||||
* @param[in] icup pointer to the @p ICUDriver object
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void icu_lld_start(ICUDriver *icup) {
|
||||
uint32_t psc;
|
||||
|
||||
chDbgAssert((icup->config->channel == ICU_CHANNEL_1) ||
|
||||
(icup->config->channel == ICU_CHANNEL_2),
|
||||
"icu_lld_start(), #1", "invalid input");
|
||||
|
||||
if (icup->state == ICU_STOP) {
|
||||
/* Clock activation and timer reset.*/
|
||||
#if STM32_ICU_USE_TIM1
|
||||
if (&ICUD1 == icup) {
|
||||
rccEnableTIM1(FALSE);
|
||||
rccResetTIM1();
|
||||
nvicEnableVector(STM32_TIM1_UP_NUMBER,
|
||||
CORTEX_PRIORITY_MASK(STM32_ICU_TIM1_IRQ_PRIORITY));
|
||||
nvicEnableVector(STM32_TIM1_CC_NUMBER,
|
||||
CORTEX_PRIORITY_MASK(STM32_ICU_TIM1_IRQ_PRIORITY));
|
||||
#if defined(STM32_TIM1CLK)
|
||||
icup->clock = STM32_TIM1CLK;
|
||||
#else
|
||||
icup->clock = STM32_TIMCLK2;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
#if STM32_ICU_USE_TIM2
|
||||
if (&ICUD2 == icup) {
|
||||
rccEnableTIM2(FALSE);
|
||||
rccResetTIM2();
|
||||
nvicEnableVector(STM32_TIM2_NUMBER,
|
||||
CORTEX_PRIORITY_MASK(STM32_ICU_TIM2_IRQ_PRIORITY));
|
||||
icup->clock = STM32_TIMCLK1;
|
||||
}
|
||||
#endif
|
||||
#if STM32_ICU_USE_TIM3
|
||||
if (&ICUD3 == icup) {
|
||||
rccEnableTIM3(FALSE);
|
||||
rccResetTIM3();
|
||||
nvicEnableVector(STM32_TIM3_NUMBER,
|
||||
CORTEX_PRIORITY_MASK(STM32_ICU_TIM3_IRQ_PRIORITY));
|
||||
icup->clock = STM32_TIMCLK1;
|
||||
}
|
||||
#endif
|
||||
#if STM32_ICU_USE_TIM4
|
||||
if (&ICUD4 == icup) {
|
||||
rccEnableTIM4(FALSE);
|
||||
rccResetTIM4();
|
||||
nvicEnableVector(STM32_TIM4_NUMBER,
|
||||
CORTEX_PRIORITY_MASK(STM32_ICU_TIM4_IRQ_PRIORITY));
|
||||
icup->clock = STM32_TIMCLK1;
|
||||
}
|
||||
#endif
|
||||
#if STM32_ICU_USE_TIM5
|
||||
if (&ICUD5 == icup) {
|
||||
rccEnableTIM5(FALSE);
|
||||
rccResetTIM5();
|
||||
nvicEnableVector(STM32_TIM5_NUMBER,
|
||||
CORTEX_PRIORITY_MASK(STM32_ICU_TIM5_IRQ_PRIORITY));
|
||||
icup->clock = STM32_TIMCLK1;
|
||||
}
|
||||
#endif
|
||||
#if STM32_ICU_USE_TIM8
|
||||
if (&ICUD8 == icup) {
|
||||
rccEnableTIM8(FALSE);
|
||||
rccResetTIM8();
|
||||
nvicEnableVector(STM32_TIM8_UP_NUMBER,
|
||||
CORTEX_PRIORITY_MASK(STM32_ICU_TIM8_IRQ_PRIORITY));
|
||||
nvicEnableVector(STM32_TIM8_CC_NUMBER,
|
||||
CORTEX_PRIORITY_MASK(STM32_ICU_TIM8_IRQ_PRIORITY));
|
||||
#if defined(STM32_TIM8CLK)
|
||||
icup->clock = STM32_TIM8CLK;
|
||||
#else
|
||||
icup->clock = STM32_TIMCLK2;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
#if STM32_ICU_USE_TIM9
|
||||
if (&ICUD9 == icup) {
|
||||
rccEnableTIM9(FALSE);
|
||||
rccResetTIM9();
|
||||
nvicEnableVector(STM32_TIM9_NUMBER,
|
||||
CORTEX_PRIORITY_MASK(STM32_ICU_TIM9_IRQ_PRIORITY));
|
||||
icup->clock = STM32_TIMCLK2;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
/* Driver re-configuration scenario, it must be stopped first.*/
|
||||
icup->tim->CR1 = 0; /* Timer disabled. */
|
||||
icup->tim->CCR[0] = 0; /* Comparator 1 disabled. */
|
||||
icup->tim->CCR[1] = 0; /* Comparator 2 disabled. */
|
||||
icup->tim->CNT = 0; /* Counter reset to zero. */
|
||||
}
|
||||
|
||||
/* Timer configuration.*/
|
||||
icup->tim->SR = 0; /* Clear eventual pending IRQs. */
|
||||
icup->tim->DIER = icup->config->dier & /* DMA-related DIER settings. */
|
||||
~STM32_TIM_DIER_IRQ_MASK;
|
||||
psc = (icup->clock / icup->config->frequency) - 1;
|
||||
chDbgAssert((psc <= 0xFFFF) &&
|
||||
((psc + 1) * icup->config->frequency) == icup->clock,
|
||||
"icu_lld_start(), #1", "invalid frequency");
|
||||
icup->tim->PSC = (uint16_t)psc;
|
||||
icup->tim->ARR = 0xFFFF;
|
||||
|
||||
if (icup->config->channel == ICU_CHANNEL_1) {
|
||||
/* Selected input 1.
|
||||
CCMR1_CC1S = 01 = CH1 Input on TI1.
|
||||
CCMR1_CC2S = 10 = CH2 Input on TI1.*/
|
||||
icup->tim->CCMR1 = STM32_TIM_CCMR1_CC1S(1) | STM32_TIM_CCMR1_CC2S(2);
|
||||
|
||||
/* SMCR_TS = 101, input is TI1FP1.
|
||||
SMCR_SMS = 100, reset on rising edge.*/
|
||||
icup->tim->SMCR = STM32_TIM_SMCR_TS(5) | STM32_TIM_SMCR_SMS(4);
|
||||
|
||||
/* The CCER settings depend on the selected trigger mode.
|
||||
ICU_INPUT_ACTIVE_HIGH: Active on rising edge, idle on falling edge.
|
||||
ICU_INPUT_ACTIVE_LOW: Active on falling edge, idle on rising edge.*/
|
||||
if (icup->config->mode == ICU_INPUT_ACTIVE_HIGH)
|
||||
icup->tim->CCER = STM32_TIM_CCER_CC1E |
|
||||
STM32_TIM_CCER_CC2E | STM32_TIM_CCER_CC2P;
|
||||
else
|
||||
icup->tim->CCER = STM32_TIM_CCER_CC1E | STM32_TIM_CCER_CC1P |
|
||||
STM32_TIM_CCER_CC2E;
|
||||
|
||||
/* Direct pointers to the capture registers in order to make reading
|
||||
data faster from within callbacks.*/
|
||||
icup->wccrp = &icup->tim->CCR[1];
|
||||
icup->pccrp = &icup->tim->CCR[0];
|
||||
} else {
|
||||
/* Selected input 2.
|
||||
CCMR1_CC1S = 10 = CH1 Input on TI2.
|
||||
CCMR1_CC2S = 01 = CH2 Input on TI2.*/
|
||||
icup->tim->CCMR1 = STM32_TIM_CCMR1_CC1S(2) | STM32_TIM_CCMR1_CC2S(1);
|
||||
|
||||
/* SMCR_TS = 110, input is TI2FP2.
|
||||
SMCR_SMS = 100, reset on rising edge.*/
|
||||
icup->tim->SMCR = STM32_TIM_SMCR_TS(6) | STM32_TIM_SMCR_SMS(4);
|
||||
|
||||
/* The CCER settings depend on the selected trigger mode.
|
||||
ICU_INPUT_ACTIVE_HIGH: Active on rising edge, idle on falling edge.
|
||||
ICU_INPUT_ACTIVE_LOW: Active on falling edge, idle on rising edge.*/
|
||||
if (icup->config->mode == ICU_INPUT_ACTIVE_HIGH)
|
||||
icup->tim->CCER = STM32_TIM_CCER_CC1E | STM32_TIM_CCER_CC1P |
|
||||
STM32_TIM_CCER_CC2E;
|
||||
else
|
||||
icup->tim->CCER = STM32_TIM_CCER_CC1E |
|
||||
STM32_TIM_CCER_CC2E | STM32_TIM_CCER_CC2P;
|
||||
|
||||
/* Direct pointers to the capture registers in order to make reading
|
||||
data faster from within callbacks.*/
|
||||
icup->wccrp = &icup->tim->CCR[0];
|
||||
icup->pccrp = &icup->tim->CCR[1];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Deactivates the ICU peripheral.
|
||||
*
|
||||
* @param[in] icup pointer to the @p ICUDriver object
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void icu_lld_stop(ICUDriver *icup) {
|
||||
|
||||
if (icup->state == ICU_READY) {
|
||||
/* Clock deactivation.*/
|
||||
icup->tim->CR1 = 0; /* Timer disabled. */
|
||||
icup->tim->DIER = 0; /* All IRQs disabled. */
|
||||
icup->tim->SR = 0; /* Clear eventual pending IRQs. */
|
||||
|
||||
#if STM32_ICU_USE_TIM1
|
||||
if (&ICUD1 == icup) {
|
||||
nvicDisableVector(STM32_TIM1_UP_NUMBER);
|
||||
nvicDisableVector(STM32_TIM1_CC_NUMBER);
|
||||
rccDisableTIM1(FALSE);
|
||||
}
|
||||
#endif
|
||||
#if STM32_ICU_USE_TIM2
|
||||
if (&ICUD2 == icup) {
|
||||
nvicDisableVector(STM32_TIM2_NUMBER);
|
||||
rccDisableTIM2(FALSE);
|
||||
}
|
||||
#endif
|
||||
#if STM32_ICU_USE_TIM3
|
||||
if (&ICUD3 == icup) {
|
||||
nvicDisableVector(STM32_TIM3_NUMBER);
|
||||
rccDisableTIM3(FALSE);
|
||||
}
|
||||
#endif
|
||||
#if STM32_ICU_USE_TIM4
|
||||
if (&ICUD4 == icup) {
|
||||
nvicDisableVector(STM32_TIM4_NUMBER);
|
||||
rccDisableTIM4(FALSE);
|
||||
}
|
||||
#endif
|
||||
#if STM32_ICU_USE_TIM5
|
||||
if (&ICUD5 == icup) {
|
||||
nvicDisableVector(STM32_TIM5_NUMBER);
|
||||
rccDisableTIM5(FALSE);
|
||||
}
|
||||
#endif
|
||||
#if STM32_ICU_USE_TIM8
|
||||
if (&ICUD8 == icup) {
|
||||
nvicDisableVector(STM32_TIM8_UP_NUMBER);
|
||||
nvicDisableVector(STM32_TIM8_CC_NUMBER);
|
||||
rccDisableTIM8(FALSE);
|
||||
}
|
||||
#endif
|
||||
#if STM32_ICU_USE_TIM9
|
||||
if (&ICUD9 == icup) {
|
||||
nvicDisableVector(STM32_TIM9_NUMBER);
|
||||
rccDisableTIM9(FALSE);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enables the input capture.
|
||||
*
|
||||
* @param[in] icup pointer to the @p ICUDriver object
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void icu_lld_enable(ICUDriver *icup) {
|
||||
|
||||
icup->tim->EGR |= STM32_TIM_EGR_UG;
|
||||
icup->tim->SR = 0; /* Clear pending IRQs (if any). */
|
||||
if (icup->config->channel == ICU_CHANNEL_1) {
|
||||
if (icup->config->period_cb != NULL)
|
||||
icup->tim->DIER |= STM32_TIM_DIER_CC1IE;
|
||||
if (icup->config->width_cb != NULL)
|
||||
icup->tim->DIER |= STM32_TIM_DIER_CC2IE;
|
||||
} else {
|
||||
if (icup->config->width_cb != NULL)
|
||||
icup->tim->DIER |= STM32_TIM_DIER_CC1IE;
|
||||
if (icup->config->period_cb != NULL)
|
||||
icup->tim->DIER |= STM32_TIM_DIER_CC2IE;
|
||||
}
|
||||
if (icup->config->overflow_cb != NULL)
|
||||
icup->tim->DIER |= STM32_TIM_DIER_UIE;
|
||||
icup->tim->CR1 = STM32_TIM_CR1_URS | STM32_TIM_CR1_CEN;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Disables the input capture.
|
||||
*
|
||||
* @param[in] icup pointer to the @p ICUDriver object
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void icu_lld_disable(ICUDriver *icup) {
|
||||
|
||||
icup->tim->CR1 = 0; /* Initially stopped. */
|
||||
icup->tim->SR = 0; /* Clear pending IRQs (if any). */
|
||||
|
||||
/* All interrupts disabled.*/
|
||||
icup->tim->DIER &= ~STM32_TIM_DIER_IRQ_MASK;
|
||||
}
|
||||
|
||||
#endif /* HAL_USE_ICU */
|
||||
|
||||
/** @} */
|
||||
+412
@@ -0,0 +1,412 @@
|
||||
/*
|
||||
ChibiOS/RT - Copyright (C) 2006-2013 Giovanni Di Sirio
|
||||
|
||||
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 STM32/icu_lld.h
|
||||
* @brief STM32 ICU subsystem low level driver header.
|
||||
*
|
||||
* @addtogroup ICU
|
||||
* @{
|
||||
*/
|
||||
|
||||
#ifndef _ICU_LLD_H_
|
||||
#define _ICU_LLD_H_
|
||||
|
||||
#include "stm32_tim.h"
|
||||
|
||||
#if HAL_USE_ICU || defined(__DOXYGEN__)
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver constants. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver pre-compile time settings. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @name Configuration options
|
||||
* @{
|
||||
*/
|
||||
/**
|
||||
* @brief ICUD1 driver enable switch.
|
||||
* @details If set to @p TRUE the support for ICUD1 is included.
|
||||
* @note The default is @p TRUE.
|
||||
*/
|
||||
#if !defined(STM32_ICU_USE_TIM1) || defined(__DOXYGEN__)
|
||||
#define STM32_ICU_USE_TIM1 FALSE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief ICUD2 driver enable switch.
|
||||
* @details If set to @p TRUE the support for ICUD2 is included.
|
||||
* @note The default is @p TRUE.
|
||||
*/
|
||||
#if !defined(STM32_ICU_USE_TIM2) || defined(__DOXYGEN__)
|
||||
#define STM32_ICU_USE_TIM2 FALSE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief ICUD3 driver enable switch.
|
||||
* @details If set to @p TRUE the support for ICUD3 is included.
|
||||
* @note The default is @p TRUE.
|
||||
*/
|
||||
#if !defined(STM32_ICU_USE_TIM3) || defined(__DOXYGEN__)
|
||||
#define STM32_ICU_USE_TIM3 FALSE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief ICUD4 driver enable switch.
|
||||
* @details If set to @p TRUE the support for ICUD4 is included.
|
||||
* @note The default is @p TRUE.
|
||||
*/
|
||||
#if !defined(STM32_ICU_USE_TIM4) || defined(__DOXYGEN__)
|
||||
#define STM32_ICU_USE_TIM4 FALSE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief ICUD5 driver enable switch.
|
||||
* @details If set to @p TRUE the support for ICUD5 is included.
|
||||
* @note The default is @p TRUE.
|
||||
*/
|
||||
#if !defined(STM32_ICU_USE_TIM5) || defined(__DOXYGEN__)
|
||||
#define STM32_ICU_USE_TIM5 FALSE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief ICUD8 driver enable switch.
|
||||
* @details If set to @p TRUE the support for ICUD8 is included.
|
||||
* @note The default is @p TRUE.
|
||||
*/
|
||||
#if !defined(STM32_ICU_USE_TIM8) || defined(__DOXYGEN__)
|
||||
#define STM32_ICU_USE_TIM8 FALSE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief ICUD9 driver enable switch.
|
||||
* @details If set to @p TRUE the support for ICUD9 is included.
|
||||
* @note The default is @p TRUE.
|
||||
*/
|
||||
#if !defined(STM32_ICU_USE_TIM9) || defined(__DOXYGEN__)
|
||||
#define STM32_ICU_USE_TIM9 FALSE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief ICUD1 interrupt priority level setting.
|
||||
*/
|
||||
#if !defined(STM32_ICU_TIM1_IRQ_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define STM32_ICU_TIM1_IRQ_PRIORITY 7
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief ICUD2 interrupt priority level setting.
|
||||
*/
|
||||
#if !defined(STM32_ICU_TIM2_IRQ_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define STM32_ICU_TIM2_IRQ_PRIORITY 7
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief ICUD3 interrupt priority level setting.
|
||||
*/
|
||||
#if !defined(STM32_ICU_TIM3_IRQ_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define STM32_ICU_TIM3_IRQ_PRIORITY 7
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief ICUD4 interrupt priority level setting.
|
||||
*/
|
||||
#if !defined(STM32_ICU_TIM4_IRQ_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define STM32_ICU_TIM4_IRQ_PRIORITY 7
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief ICUD5 interrupt priority level setting.
|
||||
*/
|
||||
#if !defined(STM32_ICU_TIM5_IRQ_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define STM32_ICU_TIM5_IRQ_PRIORITY 7
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief ICUD8 interrupt priority level setting.
|
||||
*/
|
||||
#if !defined(STM32_ICU_TIM8_IRQ_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define STM32_ICU_TIM8_IRQ_PRIORITY 7
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief ICUD9 interrupt priority level setting.
|
||||
*/
|
||||
#if !defined(STM32_ICU_TIM9_IRQ_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define STM32_ICU_TIM9_IRQ_PRIORITY 7
|
||||
#endif
|
||||
/** @} */
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Derived constants and error checks. */
|
||||
/*===========================================================================*/
|
||||
|
||||
#if STM32_ICU_USE_TIM1 && !STM32_HAS_TIM1
|
||||
#error "TIM1 not present in the selected device"
|
||||
#endif
|
||||
|
||||
#if STM32_ICU_USE_TIM2 && !STM32_HAS_TIM2
|
||||
#error "TIM2 not present in the selected device"
|
||||
#endif
|
||||
|
||||
#if STM32_ICU_USE_TIM3 && !STM32_HAS_TIM3
|
||||
#error "TIM3 not present in the selected device"
|
||||
#endif
|
||||
|
||||
#if STM32_ICU_USE_TIM4 && !STM32_HAS_TIM4
|
||||
#error "TIM4 not present in the selected device"
|
||||
#endif
|
||||
|
||||
#if STM32_ICU_USE_TIM5 && !STM32_HAS_TIM5
|
||||
#error "TIM5 not present in the selected device"
|
||||
#endif
|
||||
|
||||
#if STM32_ICU_USE_TIM8 && !STM32_HAS_TIM8
|
||||
#error "TIM8 not present in the selected device"
|
||||
#endif
|
||||
|
||||
#if STM32_ICU_USE_TIM9 && !STM32_HAS_TIM9
|
||||
#error "TIM9 not present in the selected device"
|
||||
#endif
|
||||
|
||||
#if !STM32_ICU_USE_TIM1 && !STM32_ICU_USE_TIM2 && \
|
||||
!STM32_ICU_USE_TIM3 && !STM32_ICU_USE_TIM4 && \
|
||||
!STM32_ICU_USE_TIM5 && !STM32_ICU_USE_TIM8 && \
|
||||
!STM32_ICU_USE_TIM9
|
||||
#error "ICU driver activated but no TIM peripheral assigned"
|
||||
#endif
|
||||
|
||||
#if STM32_ICU_USE_TIM1 && \
|
||||
!CORTEX_IS_VALID_KERNEL_PRIORITY(STM32_ICU_TIM1_IRQ_PRIORITY)
|
||||
#error "Invalid IRQ priority assigned to TIM1"
|
||||
#endif
|
||||
|
||||
#if STM32_ICU_USE_TIM2 && \
|
||||
!CORTEX_IS_VALID_KERNEL_PRIORITY(STM32_ICU_TIM2_IRQ_PRIORITY)
|
||||
#error "Invalid IRQ priority assigned to TIM2"
|
||||
#endif
|
||||
|
||||
#if STM32_ICU_USE_TIM3 && \
|
||||
!CORTEX_IS_VALID_KERNEL_PRIORITY(STM32_ICU_TIM3_IRQ_PRIORITY)
|
||||
#error "Invalid IRQ priority assigned to TIM3"
|
||||
#endif
|
||||
|
||||
#if STM32_ICU_USE_TIM4 && \
|
||||
!CORTEX_IS_VALID_KERNEL_PRIORITY(STM32_ICU_TIM4_IRQ_PRIORITY)
|
||||
#error "Invalid IRQ priority assigned to TIM4"
|
||||
#endif
|
||||
|
||||
#if STM32_ICU_USE_TIM5 && \
|
||||
!CORTEX_IS_VALID_KERNEL_PRIORITY(STM32_ICU_TIM5_IRQ_PRIORITY)
|
||||
#error "Invalid IRQ priority assigned to TIM5"
|
||||
#endif
|
||||
|
||||
#if STM32_ICU_USE_TIM8 && \
|
||||
!CORTEX_IS_VALID_KERNEL_PRIORITY(STM32_ICU_TIM8_IRQ_PRIORITY)
|
||||
#error "Invalid IRQ priority assigned to TIM8"
|
||||
#endif
|
||||
|
||||
#if STM32_ICU_USE_TIM9 && \
|
||||
!CORTEX_IS_VALID_KERNEL_PRIORITY(STM32_ICU_TIM9_IRQ_PRIORITY)
|
||||
#error "Invalid IRQ priority assigned to TIM9"
|
||||
#endif
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver data structures and types. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief ICU driver mode.
|
||||
*/
|
||||
typedef enum {
|
||||
ICU_INPUT_ACTIVE_HIGH = 0, /**< Trigger on rising edge. */
|
||||
ICU_INPUT_ACTIVE_LOW = 1, /**< Trigger on falling edge. */
|
||||
} icumode_t;
|
||||
|
||||
/**
|
||||
* @brief ICU frequency type.
|
||||
*/
|
||||
typedef uint32_t icufreq_t;
|
||||
|
||||
/**
|
||||
* @brief ICU channel type.
|
||||
*/
|
||||
typedef enum {
|
||||
ICU_CHANNEL_1 = 0, /**< Use TIMxCH1. */
|
||||
ICU_CHANNEL_2 = 1, /**< Use TIMxCH2. */
|
||||
} icuchannel_t;
|
||||
|
||||
/**
|
||||
* @brief ICU counter type.
|
||||
*/
|
||||
typedef uint16_t icucnt_t;
|
||||
|
||||
/**
|
||||
* @brief Driver configuration structure.
|
||||
* @note It could be empty on some architectures.
|
||||
*/
|
||||
typedef struct {
|
||||
/**
|
||||
* @brief Driver mode.
|
||||
*/
|
||||
icumode_t mode;
|
||||
/**
|
||||
* @brief Timer clock in Hz.
|
||||
* @note The low level can use assertions in order to catch invalid
|
||||
* frequency specifications.
|
||||
*/
|
||||
icufreq_t frequency;
|
||||
/**
|
||||
* @brief Callback for pulse width measurement.
|
||||
*/
|
||||
icucallback_t width_cb;
|
||||
/**
|
||||
* @brief Callback for cycle period measurement.
|
||||
*/
|
||||
icucallback_t period_cb;
|
||||
/**
|
||||
* @brief Callback for timer overflow.
|
||||
*/
|
||||
icucallback_t overflow_cb;
|
||||
/* End of the mandatory fields.*/
|
||||
/**
|
||||
* @brief Timer input channel to be used.
|
||||
* @note Only inputs TIMx 1 and 2 are supported.
|
||||
*/
|
||||
icuchannel_t channel;
|
||||
/**
|
||||
* @brief TIM DIER register initialization data.
|
||||
* @note The value of this field should normally be equal to zero.
|
||||
* @note Only the DMA-related bits can be specified in this field.
|
||||
*/
|
||||
uint32_t dier;
|
||||
} ICUConfig;
|
||||
|
||||
/**
|
||||
* @brief Structure representing an ICU driver.
|
||||
*/
|
||||
struct ICUDriver {
|
||||
/**
|
||||
* @brief Driver state.
|
||||
*/
|
||||
icustate_t state;
|
||||
/**
|
||||
* @brief Current configuration data.
|
||||
*/
|
||||
const ICUConfig *config;
|
||||
#if defined(ICU_DRIVER_EXT_FIELDS)
|
||||
ICU_DRIVER_EXT_FIELDS
|
||||
#endif
|
||||
/* End of the mandatory fields.*/
|
||||
/**
|
||||
* @brief Timer base clock.
|
||||
*/
|
||||
uint32_t clock;
|
||||
/**
|
||||
* @brief Pointer to the TIMx registers block.
|
||||
*/
|
||||
stm32_tim_t *tim;
|
||||
/**
|
||||
* @brief CCR register used for width capture.
|
||||
*/
|
||||
volatile uint32_t *wccrp;
|
||||
/**
|
||||
* @brief CCR register used for period capture.
|
||||
*/
|
||||
volatile uint32_t *pccrp;
|
||||
};
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver macros. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Returns the width of the latest pulse.
|
||||
* @details The pulse width is defined as number of ticks between the start
|
||||
* edge and the stop edge.
|
||||
*
|
||||
* @param[in] icup pointer to the @p ICUDriver object
|
||||
* @return The number of ticks.
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
#define icu_lld_get_width(icup) (*((icup)->wccrp) + 1)
|
||||
|
||||
/**
|
||||
* @brief Returns the width of the latest cycle.
|
||||
* @details The cycle width is defined as number of ticks between a start
|
||||
* edge and the next start edge.
|
||||
*
|
||||
* @param[in] icup pointer to the @p ICUDriver object
|
||||
* @return The number of ticks.
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
#define icu_lld_get_period(icup) (*((icup)->pccrp) + 1)
|
||||
|
||||
/*===========================================================================*/
|
||||
/* External declarations. */
|
||||
/*===========================================================================*/
|
||||
|
||||
#if STM32_ICU_USE_TIM1 && !defined(__DOXYGEN__)
|
||||
extern ICUDriver ICUD1;
|
||||
#endif
|
||||
|
||||
#if STM32_ICU_USE_TIM2 && !defined(__DOXYGEN__)
|
||||
extern ICUDriver ICUD2;
|
||||
#endif
|
||||
|
||||
#if STM32_ICU_USE_TIM3 && !defined(__DOXYGEN__)
|
||||
extern ICUDriver ICUD3;
|
||||
#endif
|
||||
|
||||
#if STM32_ICU_USE_TIM4 && !defined(__DOXYGEN__)
|
||||
extern ICUDriver ICUD4;
|
||||
#endif
|
||||
|
||||
#if STM32_ICU_USE_TIM5 && !defined(__DOXYGEN__)
|
||||
extern ICUDriver ICUD5;
|
||||
#endif
|
||||
|
||||
#if STM32_ICU_USE_TIM8 && !defined(__DOXYGEN__)
|
||||
extern ICUDriver ICUD8;
|
||||
#endif
|
||||
|
||||
#if STM32_ICU_USE_TIM9 && !defined(__DOXYGEN__)
|
||||
extern ICUDriver ICUD9;
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
void icu_lld_init(void);
|
||||
void icu_lld_start(ICUDriver *icup);
|
||||
void icu_lld_stop(ICUDriver *icup);
|
||||
void icu_lld_enable(ICUDriver *icup);
|
||||
void icu_lld_disable(ICUDriver *icup);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* HAL_USE_ICU */
|
||||
|
||||
#endif /* _ICU_LLD_H_ */
|
||||
|
||||
/** @} */
|
||||
+711
@@ -0,0 +1,711 @@
|
||||
/*
|
||||
ChibiOS/RT - Copyright (C) 2006-2013 Giovanni Di Sirio
|
||||
|
||||
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 STM32/pwm_lld.c
|
||||
* @brief STM32 PWM subsystem low level driver header.
|
||||
*
|
||||
* @addtogroup PWM
|
||||
* @{
|
||||
*/
|
||||
|
||||
#include "ch.h"
|
||||
#include "hal.h"
|
||||
|
||||
#if HAL_USE_PWM || defined(__DOXYGEN__)
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local definitions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver exported variables. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief PWMD1 driver identifier.
|
||||
* @note The driver PWMD1 allocates the complex timer TIM1 when enabled.
|
||||
*/
|
||||
#if STM32_PWM_USE_TIM1 || defined(__DOXYGEN__)
|
||||
PWMDriver PWMD1;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief PWMD2 driver identifier.
|
||||
* @note The driver PWMD2 allocates the timer TIM2 when enabled.
|
||||
*/
|
||||
#if STM32_PWM_USE_TIM2 || defined(__DOXYGEN__)
|
||||
PWMDriver PWMD2;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief PWMD3 driver identifier.
|
||||
* @note The driver PWMD3 allocates the timer TIM3 when enabled.
|
||||
*/
|
||||
#if STM32_PWM_USE_TIM3 || defined(__DOXYGEN__)
|
||||
PWMDriver PWMD3;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief PWMD4 driver identifier.
|
||||
* @note The driver PWMD4 allocates the timer TIM4 when enabled.
|
||||
*/
|
||||
#if STM32_PWM_USE_TIM4 || defined(__DOXYGEN__)
|
||||
PWMDriver PWMD4;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief PWMD5 driver identifier.
|
||||
* @note The driver PWMD5 allocates the timer TIM5 when enabled.
|
||||
*/
|
||||
#if STM32_PWM_USE_TIM5 || defined(__DOXYGEN__)
|
||||
PWMDriver PWMD5;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief PWMD8 driver identifier.
|
||||
* @note The driver PWMD8 allocates the timer TIM8 when enabled.
|
||||
*/
|
||||
#if STM32_PWM_USE_TIM8 || defined(__DOXYGEN__)
|
||||
PWMDriver PWMD8;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief PWMD9 driver identifier.
|
||||
* @note The driver PWMD9 allocates the timer TIM9 when enabled.
|
||||
*/
|
||||
#if STM32_PWM_USE_TIM9 || defined(__DOXYGEN__)
|
||||
PWMDriver PWMD9;
|
||||
#endif
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local variables and types. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
#if STM32_PWM_USE_TIM2 || STM32_PWM_USE_TIM3 || STM32_PWM_USE_TIM4 || \
|
||||
STM32_PWM_USE_TIM5 || STM32_PWM_USE_TIM9 || defined(__DOXYGEN__)
|
||||
/**
|
||||
* @brief Common TIM2...TIM5,TIM9 IRQ handler.
|
||||
* @note It is assumed that the various sources are only activated if the
|
||||
* associated callback pointer is not equal to @p NULL in order to not
|
||||
* perform an extra check in a potentially critical interrupt handler.
|
||||
*
|
||||
* @param[in] pwmp pointer to a @p PWMDriver object
|
||||
*/
|
||||
static void pwm_lld_serve_interrupt(PWMDriver *pwmp) {
|
||||
uint16_t sr;
|
||||
|
||||
sr = pwmp->tim->SR;
|
||||
sr &= pwmp->tim->DIER & STM32_TIM_DIER_IRQ_MASK;
|
||||
pwmp->tim->SR = ~sr;
|
||||
if ((sr & STM32_TIM_SR_CC1IF) != 0)
|
||||
pwmp->config->channels[0].callback(pwmp);
|
||||
if ((sr & STM32_TIM_SR_CC2IF) != 0)
|
||||
pwmp->config->channels[1].callback(pwmp);
|
||||
if ((sr & STM32_TIM_SR_CC3IF) != 0)
|
||||
pwmp->config->channels[2].callback(pwmp);
|
||||
if ((sr & STM32_TIM_SR_CC4IF) != 0)
|
||||
pwmp->config->channels[3].callback(pwmp);
|
||||
if ((sr & STM32_TIM_SR_UIF) != 0)
|
||||
pwmp->config->callback(pwmp);
|
||||
}
|
||||
#endif /* STM32_PWM_USE_TIM2 || ... || STM32_PWM_USE_TIM5 */
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver interrupt handlers. */
|
||||
/*===========================================================================*/
|
||||
|
||||
#if STM32_PWM_USE_TIM1
|
||||
#if !defined(STM32_TIM1_UP_HANDLER)
|
||||
#error "STM32_TIM1_UP_HANDLER not defined"
|
||||
#endif
|
||||
/**
|
||||
* @brief TIM1 update interrupt handler.
|
||||
* @note It is assumed that this interrupt is only activated if the callback
|
||||
* pointer is not equal to @p NULL in order to not perform an extra
|
||||
* check in a potentially critical interrupt handler.
|
||||
*
|
||||
* @isr
|
||||
*/
|
||||
CH_IRQ_HANDLER(STM32_TIM1_UP_HANDLER) {
|
||||
|
||||
CH_IRQ_PROLOGUE();
|
||||
|
||||
STM32_TIM1->SR = ~STM32_TIM_SR_UIF;
|
||||
PWMD1.config->callback(&PWMD1);
|
||||
|
||||
CH_IRQ_EPILOGUE();
|
||||
}
|
||||
|
||||
#if !defined(STM32_TIM1_CC_HANDLER)
|
||||
#error "STM32_TIM1_CC_HANDLER not defined"
|
||||
#endif
|
||||
/**
|
||||
* @brief TIM1 compare interrupt handler.
|
||||
* @note It is assumed that the various sources are only activated if the
|
||||
* associated callback pointer is not equal to @p NULL in order to not
|
||||
* perform an extra check in a potentially critical interrupt handler.
|
||||
*
|
||||
* @isr
|
||||
*/
|
||||
CH_IRQ_HANDLER(STM32_TIM1_CC_HANDLER) {
|
||||
uint16_t sr;
|
||||
|
||||
CH_IRQ_PROLOGUE();
|
||||
|
||||
sr = STM32_TIM1->SR & STM32_TIM1->DIER & STM32_TIM_DIER_IRQ_MASK;
|
||||
STM32_TIM1->SR = ~sr;
|
||||
if ((sr & STM32_TIM_SR_CC1IF) != 0)
|
||||
PWMD1.config->channels[0].callback(&PWMD1);
|
||||
if ((sr & STM32_TIM_SR_CC2IF) != 0)
|
||||
PWMD1.config->channels[1].callback(&PWMD1);
|
||||
if ((sr & STM32_TIM_SR_CC3IF) != 0)
|
||||
PWMD1.config->channels[2].callback(&PWMD1);
|
||||
if ((sr & STM32_TIM_SR_CC4IF) != 0)
|
||||
PWMD1.config->channels[3].callback(&PWMD1);
|
||||
|
||||
CH_IRQ_EPILOGUE();
|
||||
}
|
||||
#endif /* STM32_PWM_USE_TIM1 */
|
||||
|
||||
#if STM32_PWM_USE_TIM2
|
||||
#if !defined(STM32_TIM2_HANDLER)
|
||||
#error "STM32_TIM2_HANDLER not defined"
|
||||
#endif
|
||||
/**
|
||||
* @brief TIM2 interrupt handler.
|
||||
*
|
||||
* @isr
|
||||
*/
|
||||
CH_IRQ_HANDLER(STM32_TIM2_HANDLER) {
|
||||
|
||||
CH_IRQ_PROLOGUE();
|
||||
|
||||
pwm_lld_serve_interrupt(&PWMD2);
|
||||
|
||||
CH_IRQ_EPILOGUE();
|
||||
}
|
||||
#endif /* STM32_PWM_USE_TIM2 */
|
||||
|
||||
#if STM32_PWM_USE_TIM3
|
||||
#if !defined(STM32_TIM3_HANDLER)
|
||||
#error "STM32_TIM3_HANDLER not defined"
|
||||
#endif
|
||||
/**
|
||||
* @brief TIM3 interrupt handler.
|
||||
*
|
||||
* @isr
|
||||
*/
|
||||
CH_IRQ_HANDLER(STM32_TIM3_HANDLER) {
|
||||
|
||||
CH_IRQ_PROLOGUE();
|
||||
|
||||
pwm_lld_serve_interrupt(&PWMD3);
|
||||
|
||||
CH_IRQ_EPILOGUE();
|
||||
}
|
||||
#endif /* STM32_PWM_USE_TIM3 */
|
||||
|
||||
#if STM32_PWM_USE_TIM4
|
||||
#if !defined(STM32_TIM4_HANDLER)
|
||||
#error "STM32_TIM4_HANDLER not defined"
|
||||
#endif
|
||||
/**
|
||||
* @brief TIM4 interrupt handler.
|
||||
*
|
||||
* @isr
|
||||
*/
|
||||
CH_IRQ_HANDLER(STM32_TIM4_HANDLER) {
|
||||
|
||||
CH_IRQ_PROLOGUE();
|
||||
|
||||
pwm_lld_serve_interrupt(&PWMD4);
|
||||
|
||||
CH_IRQ_EPILOGUE();
|
||||
}
|
||||
#endif /* STM32_PWM_USE_TIM4 */
|
||||
|
||||
#if STM32_PWM_USE_TIM5
|
||||
#if !defined(STM32_TIM5_HANDLER)
|
||||
#error "STM32_TIM5_HANDLER not defined"
|
||||
#endif
|
||||
/**
|
||||
* @brief TIM5 interrupt handler.
|
||||
*
|
||||
* @isr
|
||||
*/
|
||||
CH_IRQ_HANDLER(STM32_TIM5_HANDLER) {
|
||||
|
||||
CH_IRQ_PROLOGUE();
|
||||
|
||||
pwm_lld_serve_interrupt(&PWMD5);
|
||||
|
||||
CH_IRQ_EPILOGUE();
|
||||
}
|
||||
#endif /* STM32_PWM_USE_TIM5 */
|
||||
|
||||
#if STM32_PWM_USE_TIM8
|
||||
#if !defined(STM32_TIM8_UP_HANDLER)
|
||||
#error "STM32_TIM8_UP_HANDLER not defined"
|
||||
#endif
|
||||
/**
|
||||
* @brief TIM8 update interrupt handler.
|
||||
* @note It is assumed that this interrupt is only activated if the callback
|
||||
* pointer is not equal to @p NULL in order to not perform an extra
|
||||
* check in a potentially critical interrupt handler.
|
||||
*
|
||||
* @isr
|
||||
*/
|
||||
CH_IRQ_HANDLER(STM32_TIM8_UP_HANDLER) {
|
||||
|
||||
CH_IRQ_PROLOGUE();
|
||||
|
||||
STM32_TIM8->SR = ~TIM_SR_UIF;
|
||||
PWMD8.config->callback(&PWMD8);
|
||||
|
||||
CH_IRQ_EPILOGUE();
|
||||
}
|
||||
|
||||
#if !defined(STM32_TIM8_CC_HANDLER)
|
||||
#error "STM32_TIM8_CC_HANDLER not defined"
|
||||
#endif
|
||||
/**
|
||||
* @brief TIM8 compare interrupt handler.
|
||||
* @note It is assumed that the various sources are only activated if the
|
||||
* associated callback pointer is not equal to @p NULL in order to not
|
||||
* perform an extra check in a potentially critical interrupt handler.
|
||||
*
|
||||
* @isr
|
||||
*/
|
||||
CH_IRQ_HANDLER(STM32_TIM8_CC_HANDLER) {
|
||||
uint16_t sr;
|
||||
|
||||
CH_IRQ_PROLOGUE();
|
||||
|
||||
sr = STM32_TIM8->SR & STM32_TIM8->DIER & STM32_TIM_DIER_IRQ_MASK;
|
||||
STM32_TIM8->SR = ~sr;
|
||||
if ((sr & STM32_TIM_SR_CC1IF) != 0)
|
||||
PWMD8.config->channels[0].callback(&PWMD8);
|
||||
if ((sr & STM32_TIM_SR_CC2IF) != 0)
|
||||
PWMD8.config->channels[1].callback(&PWMD8);
|
||||
if ((sr & STM32_TIM_SR_CC3IF) != 0)
|
||||
PWMD8.config->channels[2].callback(&PWMD8);
|
||||
if ((sr & STM32_TIM_SR_CC4IF) != 0)
|
||||
PWMD8.config->channels[3].callback(&PWMD8);
|
||||
|
||||
CH_IRQ_EPILOGUE();
|
||||
}
|
||||
#endif /* STM32_PWM_USE_TIM8 */
|
||||
|
||||
#if STM32_PWM_USE_TIM9
|
||||
#if !defined(STM32_TIM9_HANDLER)
|
||||
#error "STM32_TIM9_HANDLER not defined"
|
||||
#endif
|
||||
/**
|
||||
* @brief TIM9 interrupt handler.
|
||||
*
|
||||
* @isr
|
||||
*/
|
||||
CH_IRQ_HANDLER(STM32_TIM9_HANDLER) {
|
||||
|
||||
CH_IRQ_PROLOGUE();
|
||||
|
||||
pwm_lld_serve_interrupt(&PWMD9);
|
||||
|
||||
CH_IRQ_EPILOGUE();
|
||||
}
|
||||
#endif /* STM32_PWM_USE_TIM9 */
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver exported functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Low level PWM driver initialization.
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void pwm_lld_init(void) {
|
||||
|
||||
#if STM32_PWM_USE_TIM1
|
||||
/* Driver initialization.*/
|
||||
pwmObjectInit(&PWMD1);
|
||||
PWMD1.tim = STM32_TIM1;
|
||||
#endif
|
||||
|
||||
#if STM32_PWM_USE_TIM2
|
||||
/* Driver initialization.*/
|
||||
pwmObjectInit(&PWMD2);
|
||||
PWMD2.tim = STM32_TIM2;
|
||||
#endif
|
||||
|
||||
#if STM32_PWM_USE_TIM3
|
||||
/* Driver initialization.*/
|
||||
pwmObjectInit(&PWMD3);
|
||||
PWMD3.tim = STM32_TIM3;
|
||||
#endif
|
||||
|
||||
#if STM32_PWM_USE_TIM4
|
||||
/* Driver initialization.*/
|
||||
pwmObjectInit(&PWMD4);
|
||||
PWMD4.tim = STM32_TIM4;
|
||||
#endif
|
||||
|
||||
#if STM32_PWM_USE_TIM5
|
||||
/* Driver initialization.*/
|
||||
pwmObjectInit(&PWMD5);
|
||||
PWMD5.tim = STM32_TIM5;
|
||||
#endif
|
||||
|
||||
#if STM32_PWM_USE_TIM8
|
||||
/* Driver initialization.*/
|
||||
pwmObjectInit(&PWMD8);
|
||||
PWMD8.tim = STM32_TIM8;
|
||||
#endif
|
||||
|
||||
#if STM32_PWM_USE_TIM9
|
||||
/* Driver initialization.*/
|
||||
pwmObjectInit(&PWMD9);
|
||||
PWMD9.tim = STM32_TIM9;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Configures and activates the PWM peripheral.
|
||||
* @note Starting a driver that is already in the @p PWM_READY state
|
||||
* disables all the active channels.
|
||||
*
|
||||
* @param[in] pwmp pointer to a @p PWMDriver object
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void pwm_lld_start(PWMDriver *pwmp) {
|
||||
uint32_t psc;
|
||||
uint32_t ccer;
|
||||
|
||||
if (pwmp->state == PWM_STOP) {
|
||||
/* Clock activation and timer reset.*/
|
||||
#if STM32_PWM_USE_TIM1
|
||||
if (&PWMD1 == pwmp) {
|
||||
rccEnableTIM1(FALSE);
|
||||
rccResetTIM1();
|
||||
nvicEnableVector(STM32_TIM1_UP_NUMBER,
|
||||
CORTEX_PRIORITY_MASK(STM32_PWM_TIM1_IRQ_PRIORITY));
|
||||
nvicEnableVector(STM32_TIM1_CC_NUMBER,
|
||||
CORTEX_PRIORITY_MASK(STM32_PWM_TIM1_IRQ_PRIORITY));
|
||||
#if defined(STM32_TIM1CLK)
|
||||
pwmp->clock = STM32_TIM1CLK;
|
||||
#else
|
||||
pwmp->clock = STM32_TIMCLK2;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
#if STM32_PWM_USE_TIM2
|
||||
if (&PWMD2 == pwmp) {
|
||||
rccEnableTIM2(FALSE);
|
||||
rccResetTIM2();
|
||||
nvicEnableVector(STM32_TIM2_NUMBER,
|
||||
CORTEX_PRIORITY_MASK(STM32_PWM_TIM2_IRQ_PRIORITY));
|
||||
pwmp->clock = STM32_TIMCLK1;
|
||||
}
|
||||
#endif
|
||||
#if STM32_PWM_USE_TIM3
|
||||
if (&PWMD3 == pwmp) {
|
||||
rccEnableTIM3(FALSE);
|
||||
rccResetTIM3();
|
||||
nvicEnableVector(STM32_TIM3_NUMBER,
|
||||
CORTEX_PRIORITY_MASK(STM32_PWM_TIM3_IRQ_PRIORITY));
|
||||
pwmp->clock = STM32_TIMCLK1;
|
||||
}
|
||||
#endif
|
||||
#if STM32_PWM_USE_TIM4
|
||||
if (&PWMD4 == pwmp) {
|
||||
rccEnableTIM4(FALSE);
|
||||
rccResetTIM4();
|
||||
nvicEnableVector(STM32_TIM4_NUMBER,
|
||||
CORTEX_PRIORITY_MASK(STM32_PWM_TIM4_IRQ_PRIORITY));
|
||||
pwmp->clock = STM32_TIMCLK1;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if STM32_PWM_USE_TIM5
|
||||
if (&PWMD5 == pwmp) {
|
||||
rccEnableTIM5(FALSE);
|
||||
rccResetTIM5();
|
||||
nvicEnableVector(STM32_TIM5_NUMBER,
|
||||
CORTEX_PRIORITY_MASK(STM32_PWM_TIM5_IRQ_PRIORITY));
|
||||
pwmp->clock = STM32_TIMCLK1;
|
||||
}
|
||||
#endif
|
||||
#if STM32_PWM_USE_TIM8
|
||||
if (&PWMD8 == pwmp) {
|
||||
rccEnableTIM8(FALSE);
|
||||
rccResetTIM8();
|
||||
nvicEnableVector(STM32_TIM8_UP_NUMBER,
|
||||
CORTEX_PRIORITY_MASK(STM32_PWM_TIM8_IRQ_PRIORITY));
|
||||
nvicEnableVector(STM32_TIM8_CC_NUMBER,
|
||||
CORTEX_PRIORITY_MASK(STM32_PWM_TIM8_IRQ_PRIORITY));
|
||||
#if defined(STM32_TIM8CLK)
|
||||
pwmp->clock = STM32_TIM8CLK;
|
||||
#else
|
||||
pwmp->clock = STM32_TIMCLK2;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
#if STM32_PWM_USE_TIM9
|
||||
if (&PWMD9 == pwmp) {
|
||||
rccEnableTIM9(FALSE);
|
||||
rccResetTIM9();
|
||||
nvicEnableVector(STM32_TIM9_NUMBER,
|
||||
CORTEX_PRIORITY_MASK(STM32_PWM_TIM9_IRQ_PRIORITY));
|
||||
pwmp->clock = STM32_TIMCLK2;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* All channels configured in PWM1 mode with preload enabled and will
|
||||
stay that way until the driver is stopped.*/
|
||||
pwmp->tim->CCMR1 = STM32_TIM_CCMR1_OC1M(6) | STM32_TIM_CCMR1_OC1PE |
|
||||
STM32_TIM_CCMR1_OC2M(6) | STM32_TIM_CCMR1_OC2PE;
|
||||
pwmp->tim->CCMR2 = STM32_TIM_CCMR2_OC3M(6) | STM32_TIM_CCMR2_OC3PE |
|
||||
STM32_TIM_CCMR2_OC4M(6) | STM32_TIM_CCMR2_OC4PE;
|
||||
}
|
||||
else {
|
||||
/* Driver re-configuration scenario, it must be stopped first.*/
|
||||
pwmp->tim->CR1 = 0; /* Timer disabled. */
|
||||
pwmp->tim->CCR[0] = 0; /* Comparator 1 disabled. */
|
||||
pwmp->tim->CCR[1] = 0; /* Comparator 2 disabled. */
|
||||
pwmp->tim->CCR[2] = 0; /* Comparator 3 disabled. */
|
||||
pwmp->tim->CCR[3] = 0; /* Comparator 4 disabled. */
|
||||
pwmp->tim->CNT = 0; /* Counter reset to zero. */
|
||||
}
|
||||
|
||||
/* Timer configuration.*/
|
||||
psc = (pwmp->clock / pwmp->config->frequency) - 1;
|
||||
chDbgAssert((psc <= 0xFFFF) &&
|
||||
((psc + 1) * pwmp->config->frequency) == pwmp->clock,
|
||||
"pwm_lld_start(), #1", "invalid frequency");
|
||||
pwmp->tim->PSC = (uint16_t)psc;
|
||||
pwmp->tim->ARR = (uint16_t)(pwmp->period - 1);
|
||||
pwmp->tim->CR2 = pwmp->config->cr2;
|
||||
|
||||
/* Output enables and polarities setup.*/
|
||||
ccer = 0;
|
||||
switch (pwmp->config->channels[0].mode & PWM_OUTPUT_MASK) {
|
||||
case PWM_OUTPUT_ACTIVE_LOW:
|
||||
ccer |= STM32_TIM_CCER_CC1P;
|
||||
case PWM_OUTPUT_ACTIVE_HIGH:
|
||||
ccer |= STM32_TIM_CCER_CC1E;
|
||||
default:
|
||||
;
|
||||
}
|
||||
switch (pwmp->config->channels[1].mode & PWM_OUTPUT_MASK) {
|
||||
case PWM_OUTPUT_ACTIVE_LOW:
|
||||
ccer |= STM32_TIM_CCER_CC2P;
|
||||
case PWM_OUTPUT_ACTIVE_HIGH:
|
||||
ccer |= STM32_TIM_CCER_CC2E;
|
||||
default:
|
||||
;
|
||||
}
|
||||
switch (pwmp->config->channels[2].mode & PWM_OUTPUT_MASK) {
|
||||
case PWM_OUTPUT_ACTIVE_LOW:
|
||||
ccer |= STM32_TIM_CCER_CC3P;
|
||||
case PWM_OUTPUT_ACTIVE_HIGH:
|
||||
ccer |= STM32_TIM_CCER_CC3E;
|
||||
default:
|
||||
;
|
||||
}
|
||||
switch (pwmp->config->channels[3].mode & PWM_OUTPUT_MASK) {
|
||||
case PWM_OUTPUT_ACTIVE_LOW:
|
||||
ccer |= STM32_TIM_CCER_CC4P;
|
||||
case PWM_OUTPUT_ACTIVE_HIGH:
|
||||
ccer |= STM32_TIM_CCER_CC4E;
|
||||
default:
|
||||
;
|
||||
}
|
||||
#if STM32_PWM_USE_ADVANCED
|
||||
#if STM32_PWM_USE_TIM1 && !STM32_PWM_USE_TIM8
|
||||
if (&PWMD1 == pwmp) {
|
||||
#endif
|
||||
#if !STM32_PWM_USE_TIM1 && STM32_PWM_USE_TIM8
|
||||
if (&PWMD8 == pwmp) {
|
||||
#endif
|
||||
#if STM32_PWM_USE_TIM1 && STM32_PWM_USE_TIM8
|
||||
if ((&PWMD1 == pwmp) || (&PWMD8 == pwmp)) {
|
||||
#endif
|
||||
switch (pwmp->config->channels[0].mode & PWM_COMPLEMENTARY_OUTPUT_MASK) {
|
||||
case PWM_COMPLEMENTARY_OUTPUT_ACTIVE_LOW:
|
||||
ccer |= STM32_TIM_CCER_CC1NP;
|
||||
case PWM_COMPLEMENTARY_OUTPUT_ACTIVE_HIGH:
|
||||
ccer |= STM32_TIM_CCER_CC1NE;
|
||||
default:
|
||||
;
|
||||
}
|
||||
switch (pwmp->config->channels[1].mode & PWM_COMPLEMENTARY_OUTPUT_MASK) {
|
||||
case PWM_COMPLEMENTARY_OUTPUT_ACTIVE_LOW:
|
||||
ccer |= STM32_TIM_CCER_CC2NP;
|
||||
case PWM_COMPLEMENTARY_OUTPUT_ACTIVE_HIGH:
|
||||
ccer |= STM32_TIM_CCER_CC2NE;
|
||||
default:
|
||||
;
|
||||
}
|
||||
switch (pwmp->config->channels[2].mode & PWM_COMPLEMENTARY_OUTPUT_MASK) {
|
||||
case PWM_COMPLEMENTARY_OUTPUT_ACTIVE_LOW:
|
||||
ccer |= STM32_TIM_CCER_CC3NP;
|
||||
case PWM_COMPLEMENTARY_OUTPUT_ACTIVE_HIGH:
|
||||
ccer |= STM32_TIM_CCER_CC3NE;
|
||||
default:
|
||||
;
|
||||
}
|
||||
}
|
||||
#endif /* STM32_PWM_USE_ADVANCED*/
|
||||
|
||||
pwmp->tim->CCER = ccer;
|
||||
pwmp->tim->EGR = STM32_TIM_EGR_UG; /* Update event. */
|
||||
pwmp->tim->SR = 0; /* Clear pending IRQs. */
|
||||
pwmp->tim->DIER = (pwmp->config->callback == NULL ? 0 : STM32_TIM_DIER_UIE) |
|
||||
(pwmp->config->dier & ~STM32_TIM_DIER_IRQ_MASK);
|
||||
#if STM32_PWM_USE_TIM1 || STM32_PWM_USE_TIM8
|
||||
#if STM32_PWM_USE_ADVANCED
|
||||
pwmp->tim->BDTR = pwmp->config->bdtr | STM32_TIM_BDTR_MOE;
|
||||
#else
|
||||
pwmp->tim->BDTR = STM32_TIM_BDTR_MOE;
|
||||
#endif
|
||||
#endif
|
||||
/* Timer configured and started.*/
|
||||
pwmp->tim->CR1 = STM32_TIM_CR1_ARPE | STM32_TIM_CR1_URS |
|
||||
STM32_TIM_CR1_CEN;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Deactivates the PWM peripheral.
|
||||
*
|
||||
* @param[in] pwmp pointer to a @p PWMDriver object
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void pwm_lld_stop(PWMDriver *pwmp) {
|
||||
|
||||
/* If in ready state then disables the PWM clock.*/
|
||||
if (pwmp->state == PWM_READY) {
|
||||
pwmp->tim->CR1 = 0; /* Timer disabled. */
|
||||
pwmp->tim->DIER = 0; /* All IRQs disabled. */
|
||||
pwmp->tim->SR = 0; /* Clear eventual pending IRQs. */
|
||||
#if STM32_PWM_USE_TIM1 || STM32_PWM_USE_TIM8
|
||||
pwmp->tim->BDTR = 0;
|
||||
#endif
|
||||
|
||||
#if STM32_PWM_USE_TIM1
|
||||
if (&PWMD1 == pwmp) {
|
||||
nvicDisableVector(STM32_TIM1_UP_NUMBER);
|
||||
nvicDisableVector(STM32_TIM1_CC_NUMBER);
|
||||
rccDisableTIM1(FALSE);
|
||||
}
|
||||
#endif
|
||||
#if STM32_PWM_USE_TIM2
|
||||
if (&PWMD2 == pwmp) {
|
||||
nvicDisableVector(STM32_TIM2_NUMBER);
|
||||
rccDisableTIM2(FALSE);
|
||||
}
|
||||
#endif
|
||||
#if STM32_PWM_USE_TIM3
|
||||
if (&PWMD3 == pwmp) {
|
||||
nvicDisableVector(STM32_TIM3_NUMBER);
|
||||
rccDisableTIM3(FALSE);
|
||||
}
|
||||
#endif
|
||||
#if STM32_PWM_USE_TIM4
|
||||
if (&PWMD4 == pwmp) {
|
||||
nvicDisableVector(STM32_TIM4_NUMBER);
|
||||
rccDisableTIM4(FALSE);
|
||||
}
|
||||
#endif
|
||||
#if STM32_PWM_USE_TIM5
|
||||
if (&PWMD5 == pwmp) {
|
||||
nvicDisableVector(STM32_TIM5_NUMBER);
|
||||
rccDisableTIM5(FALSE);
|
||||
}
|
||||
#endif
|
||||
#if STM32_PWM_USE_TIM8
|
||||
if (&PWMD8 == pwmp) {
|
||||
nvicDisableVector(STM32_TIM8_UP_NUMBER);
|
||||
nvicDisableVector(STM32_TIM8_CC_NUMBER);
|
||||
rccDisableTIM8(FALSE);
|
||||
}
|
||||
#endif
|
||||
#if STM32_PWM_USE_TIM9
|
||||
if (&PWMD9 == pwmp) {
|
||||
nvicDisableVector(STM32_TIM9_NUMBER);
|
||||
rccDisableTIM9(FALSE);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enables a PWM channel.
|
||||
* @pre The PWM unit must have been activated using @p pwmStart().
|
||||
* @post The channel is active using the specified configuration.
|
||||
* @note The function has effect at the next cycle start.
|
||||
*
|
||||
* @param[in] pwmp pointer to a @p PWMDriver object
|
||||
* @param[in] channel PWM channel identifier (0...PWM_CHANNELS-1)
|
||||
* @param[in] width PWM pulse width as clock pulses number
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void pwm_lld_enable_channel(PWMDriver *pwmp,
|
||||
pwmchannel_t channel,
|
||||
pwmcnt_t width) {
|
||||
|
||||
pwmp->tim->CCR[channel] = width; /* New duty cycle. */
|
||||
/* If there is a callback defined for the channel then the associated
|
||||
interrupt must be enabled.*/
|
||||
if (pwmp->config->channels[channel].callback != NULL) {
|
||||
uint32_t dier = pwmp->tim->DIER;
|
||||
/* If the IRQ is not already enabled care must be taken to clear it,
|
||||
it is probably already pending because the timer is running.*/
|
||||
if ((dier & (2 << channel)) == 0) {
|
||||
pwmp->tim->DIER = dier | (2 << channel);
|
||||
pwmp->tim->SR = ~(2 << channel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Disables a PWM channel.
|
||||
* @pre The PWM unit must have been activated using @p pwmStart().
|
||||
* @post The channel is disabled and its output line returned to the
|
||||
* idle state.
|
||||
* @note The function has effect at the next cycle start.
|
||||
*
|
||||
* @param[in] pwmp pointer to a @p PWMDriver object
|
||||
* @param[in] channel PWM channel identifier (0...PWM_CHANNELS-1)
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void pwm_lld_disable_channel(PWMDriver *pwmp, pwmchannel_t channel) {
|
||||
|
||||
pwmp->tim->CCR[channel] = 0;
|
||||
pwmp->tim->DIER &= ~(2 << channel);
|
||||
}
|
||||
|
||||
#endif /* HAL_USE_PWM */
|
||||
|
||||
/** @} */
|
||||
+480
@@ -0,0 +1,480 @@
|
||||
/*
|
||||
ChibiOS/RT - Copyright (C) 2006-2013 Giovanni Di Sirio
|
||||
|
||||
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 STM32/pwm_lld.h
|
||||
* @brief STM32 PWM subsystem low level driver header.
|
||||
*
|
||||
* @addtogroup PWM
|
||||
* @{
|
||||
*/
|
||||
|
||||
#ifndef _PWM_LLD_H_
|
||||
#define _PWM_LLD_H_
|
||||
|
||||
#include "stm32_tim.h"
|
||||
|
||||
#if HAL_USE_PWM || defined(__DOXYGEN__)
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver constants. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Number of PWM channels per PWM driver.
|
||||
*/
|
||||
#define PWM_CHANNELS 4
|
||||
|
||||
/**
|
||||
* @brief Complementary output modes mask.
|
||||
* @note This is an STM32-specific setting.
|
||||
*/
|
||||
#define PWM_COMPLEMENTARY_OUTPUT_MASK 0xF0
|
||||
|
||||
/**
|
||||
* @brief Complementary output not driven.
|
||||
* @note This is an STM32-specific setting.
|
||||
*/
|
||||
#define PWM_COMPLEMENTARY_OUTPUT_DISABLED 0x00
|
||||
|
||||
/**
|
||||
* @brief Complementary output, active is logic level one.
|
||||
* @note This is an STM32-specific setting.
|
||||
* @note This setting is only available if the configuration option
|
||||
* @p STM32_PWM_USE_ADVANCED is set to TRUE and only for advanced
|
||||
* timers TIM1 and TIM8.
|
||||
*/
|
||||
#define PWM_COMPLEMENTARY_OUTPUT_ACTIVE_HIGH 0x10
|
||||
|
||||
/**
|
||||
* @brief Complementary output, active is logic level zero.
|
||||
* @note This is an STM32-specific setting.
|
||||
* @note This setting is only available if the configuration option
|
||||
* @p STM32_PWM_USE_ADVANCED is set to TRUE and only for advanced
|
||||
* timers TIM1 and TIM8.
|
||||
*/
|
||||
#define PWM_COMPLEMENTARY_OUTPUT_ACTIVE_LOW 0x20
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver pre-compile time settings. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @name Configuration options
|
||||
* @{
|
||||
*/
|
||||
/**
|
||||
* @brief If advanced timer features switch.
|
||||
* @details If set to @p TRUE the advanced features for TIM1 and TIM8 are
|
||||
* enabled.
|
||||
* @note The default is @p TRUE.
|
||||
*/
|
||||
#if !defined(STM32_PWM_USE_ADVANCED) || defined(__DOXYGEN__)
|
||||
#define STM32_PWM_USE_ADVANCED FALSE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief PWMD1 driver enable switch.
|
||||
* @details If set to @p TRUE the support for PWMD1 is included.
|
||||
* @note The default is @p TRUE.
|
||||
*/
|
||||
#if !defined(STM32_PWM_USE_TIM1) || defined(__DOXYGEN__)
|
||||
#define STM32_PWM_USE_TIM1 FALSE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief PWMD2 driver enable switch.
|
||||
* @details If set to @p TRUE the support for PWMD2 is included.
|
||||
* @note The default is @p TRUE.
|
||||
*/
|
||||
#if !defined(STM32_PWM_USE_TIM2) || defined(__DOXYGEN__)
|
||||
#define STM32_PWM_USE_TIM2 FALSE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief PWMD3 driver enable switch.
|
||||
* @details If set to @p TRUE the support for PWMD3 is included.
|
||||
* @note The default is @p TRUE.
|
||||
*/
|
||||
#if !defined(STM32_PWM_USE_TIM3) || defined(__DOXYGEN__)
|
||||
#define STM32_PWM_USE_TIM3 FALSE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief PWMD4 driver enable switch.
|
||||
* @details If set to @p TRUE the support for PWMD4 is included.
|
||||
* @note The default is @p TRUE.
|
||||
*/
|
||||
#if !defined(STM32_PWM_USE_TIM4) || defined(__DOXYGEN__)
|
||||
#define STM32_PWM_USE_TIM4 FALSE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief PWMD5 driver enable switch.
|
||||
* @details If set to @p TRUE the support for PWMD5 is included.
|
||||
* @note The default is @p TRUE.
|
||||
*/
|
||||
#if !defined(STM32_PWM_USE_TIM5) || defined(__DOXYGEN__)
|
||||
#define STM32_PWM_USE_TIM5 FALSE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief PWMD8 driver enable switch.
|
||||
* @details If set to @p TRUE the support for PWMD8 is included.
|
||||
* @note The default is @p TRUE.
|
||||
*/
|
||||
#if !defined(STM32_PWM_USE_TIM8) || defined(__DOXYGEN__)
|
||||
#define STM32_PWM_USE_TIM8 FALSE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief PWMD9 driver enable switch.
|
||||
* @details If set to @p TRUE the support for PWMD9 is included.
|
||||
* @note The default is @p TRUE.
|
||||
*/
|
||||
#if !defined(STM32_PWM_USE_TIM9) || defined(__DOXYGEN__)
|
||||
#define STM32_PWM_USE_TIM9 FALSE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief PWMD1 interrupt priority level setting.
|
||||
*/
|
||||
#if !defined(STM32_PWM_TIM1_IRQ_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define STM32_PWM_TIM1_IRQ_PRIORITY 7
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief PWMD2 interrupt priority level setting.
|
||||
*/
|
||||
#if !defined(STM32_PWM_TIM2_IRQ_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define STM32_PWM_TIM2_IRQ_PRIORITY 7
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief PWMD3 interrupt priority level setting.
|
||||
*/
|
||||
#if !defined(STM32_PWM_TIM3_IRQ_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define STM32_PWM_TIM3_IRQ_PRIORITY 7
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief PWMD4 interrupt priority level setting.
|
||||
*/
|
||||
#if !defined(STM32_PWM_TIM4_IRQ_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define STM32_PWM_TIM4_IRQ_PRIORITY 7
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief PWMD5 interrupt priority level setting.
|
||||
*/
|
||||
#if !defined(STM32_PWM_TIM5_IRQ_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define STM32_PWM_TIM5_IRQ_PRIORITY 7
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief PWMD8 interrupt priority level setting.
|
||||
*/
|
||||
#if !defined(STM32_PWM_TIM8_IRQ_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define STM32_PWM_TIM8_IRQ_PRIORITY 7
|
||||
#endif
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @brief PWMD9 interrupt priority level setting.
|
||||
*/
|
||||
#if !defined(STM32_PWM_TIM9_IRQ_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define STM32_PWM_TIM9_IRQ_PRIORITY 7
|
||||
#endif
|
||||
/** @} */
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Configuration checks. */
|
||||
/*===========================================================================*/
|
||||
|
||||
#if STM32_PWM_USE_TIM1 && !STM32_HAS_TIM1
|
||||
#error "TIM1 not present in the selected device"
|
||||
#endif
|
||||
|
||||
#if STM32_PWM_USE_TIM2 && !STM32_HAS_TIM2
|
||||
#error "TIM2 not present in the selected device"
|
||||
#endif
|
||||
|
||||
#if STM32_PWM_USE_TIM3 && !STM32_HAS_TIM3
|
||||
#error "TIM3 not present in the selected device"
|
||||
#endif
|
||||
|
||||
#if STM32_PWM_USE_TIM4 && !STM32_HAS_TIM4
|
||||
#error "TIM4 not present in the selected device"
|
||||
#endif
|
||||
|
||||
#if STM32_PWM_USE_TIM5 && !STM32_HAS_TIM5
|
||||
#error "TIM5 not present in the selected device"
|
||||
#endif
|
||||
|
||||
#if STM32_PWM_USE_TIM8 && !STM32_HAS_TIM8
|
||||
#error "TIM8 not present in the selected device"
|
||||
#endif
|
||||
|
||||
#if STM32_PWM_USE_TIM9 && !STM32_HAS_TIM9
|
||||
#error "TIM9 not present in the selected device"
|
||||
#endif
|
||||
|
||||
#if !STM32_PWM_USE_TIM1 && !STM32_PWM_USE_TIM2 && \
|
||||
!STM32_PWM_USE_TIM3 && !STM32_PWM_USE_TIM4 && \
|
||||
!STM32_PWM_USE_TIM5 && !STM32_PWM_USE_TIM8 && \
|
||||
!STM32_PWM_USE_TIM9
|
||||
#error "PWM driver activated but no TIM peripheral assigned"
|
||||
#endif
|
||||
|
||||
#if STM32_PWM_USE_ADVANCED && !STM32_PWM_USE_TIM1 && !STM32_PWM_USE_TIM8
|
||||
#error "advanced mode selected but no advanced timer assigned"
|
||||
#endif
|
||||
|
||||
#if STM32_PWM_USE_TIM1 && \
|
||||
!CORTEX_IS_VALID_KERNEL_PRIORITY(STM32_PWM_TIM1_IRQ_PRIORITY)
|
||||
#error "Invalid IRQ priority assigned to TIM1"
|
||||
#endif
|
||||
|
||||
#if STM32_PWM_USE_TIM2 && \
|
||||
!CORTEX_IS_VALID_KERNEL_PRIORITY(STM32_PWM_TIM2_IRQ_PRIORITY)
|
||||
#error "Invalid IRQ priority assigned to TIM2"
|
||||
#endif
|
||||
|
||||
#if STM32_PWM_USE_TIM3 && \
|
||||
!CORTEX_IS_VALID_KERNEL_PRIORITY(STM32_PWM_TIM3_IRQ_PRIORITY)
|
||||
#error "Invalid IRQ priority assigned to TIM3"
|
||||
#endif
|
||||
|
||||
#if STM32_PWM_USE_TIM4 && \
|
||||
!CORTEX_IS_VALID_KERNEL_PRIORITY(STM32_PWM_TIM4_IRQ_PRIORITY)
|
||||
#error "Invalid IRQ priority assigned to TIM4"
|
||||
#endif
|
||||
|
||||
#if STM32_PWM_USE_TIM5 && \
|
||||
!CORTEX_IS_VALID_KERNEL_PRIORITY(STM32_PWM_TIM5_IRQ_PRIORITY)
|
||||
#error "Invalid IRQ priority assigned to TIM5"
|
||||
#endif
|
||||
|
||||
#if STM32_PWM_USE_TIM8 && \
|
||||
!CORTEX_IS_VALID_KERNEL_PRIORITY(STM32_PWM_TIM8_IRQ_PRIORITY)
|
||||
#error "Invalid IRQ priority assigned to TIM8"
|
||||
#endif
|
||||
|
||||
#if STM32_PWM_USE_TIM9 && \
|
||||
!CORTEX_IS_VALID_KERNEL_PRIORITY(STM32_PWM_TIM9_IRQ_PRIORITY)
|
||||
#error "Invalid IRQ priority assigned to TIM9"
|
||||
#endif
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver data structures and types. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief PWM mode type.
|
||||
*/
|
||||
typedef uint32_t pwmmode_t;
|
||||
|
||||
/**
|
||||
* @brief PWM channel type.
|
||||
*/
|
||||
typedef uint8_t pwmchannel_t;
|
||||
|
||||
/**
|
||||
* @brief PWM counter type.
|
||||
*/
|
||||
typedef uint16_t pwmcnt_t;
|
||||
|
||||
/**
|
||||
* @brief PWM driver channel configuration structure.
|
||||
*/
|
||||
typedef struct {
|
||||
/**
|
||||
* @brief Channel active logic level.
|
||||
*/
|
||||
pwmmode_t mode;
|
||||
/**
|
||||
* @brief Channel callback pointer.
|
||||
* @note This callback is invoked on the channel compare event. If set to
|
||||
* @p NULL then the callback is disabled.
|
||||
*/
|
||||
pwmcallback_t callback;
|
||||
/* End of the mandatory fields.*/
|
||||
} PWMChannelConfig;
|
||||
|
||||
/**
|
||||
* @brief PWM driver configuration structure.
|
||||
*/
|
||||
typedef struct {
|
||||
/**
|
||||
* @brief Timer clock in Hz.
|
||||
* @note The low level can use assertions in order to catch invalid
|
||||
* frequency specifications.
|
||||
*/
|
||||
uint32_t frequency;
|
||||
/**
|
||||
* @brief PWM period in ticks.
|
||||
* @note The low level can use assertions in order to catch invalid
|
||||
* period specifications.
|
||||
*/
|
||||
pwmcnt_t period;
|
||||
/**
|
||||
* @brief Periodic callback pointer.
|
||||
* @note This callback is invoked on PWM counter reset. If set to
|
||||
* @p NULL then the callback is disabled.
|
||||
*/
|
||||
pwmcallback_t callback;
|
||||
/**
|
||||
* @brief Channels configurations.
|
||||
*/
|
||||
PWMChannelConfig channels[PWM_CHANNELS];
|
||||
/* End of the mandatory fields.*/
|
||||
/**
|
||||
* @brief TIM CR2 register initialization data.
|
||||
* @note The value of this field should normally be equal to zero.
|
||||
*/
|
||||
uint32_t cr2;
|
||||
#if STM32_PWM_USE_ADVANCED || defined(__DOXYGEN__)
|
||||
/**
|
||||
* @brief TIM BDTR (break & dead-time) register initialization data.
|
||||
* @note The value of this field should normally be equal to zero.
|
||||
*/ \
|
||||
uint32_t bdtr;
|
||||
#endif
|
||||
/**
|
||||
* @brief TIM DIER register initialization data.
|
||||
* @note The value of this field should normally be equal to zero.
|
||||
* @note Only the DMA-related bits can be specified in this field.
|
||||
*/
|
||||
uint32_t dier;
|
||||
} PWMConfig;
|
||||
|
||||
/**
|
||||
* @brief Structure representing a PWM driver.
|
||||
*/
|
||||
struct PWMDriver {
|
||||
/**
|
||||
* @brief Driver state.
|
||||
*/
|
||||
pwmstate_t state;
|
||||
/**
|
||||
* @brief Current driver configuration data.
|
||||
*/
|
||||
const PWMConfig *config;
|
||||
/**
|
||||
* @brief Current PWM period in ticks.
|
||||
*/
|
||||
pwmcnt_t period;
|
||||
#if defined(PWM_DRIVER_EXT_FIELDS)
|
||||
PWM_DRIVER_EXT_FIELDS
|
||||
#endif
|
||||
/* End of the mandatory fields.*/
|
||||
/**
|
||||
* @brief Timer base clock.
|
||||
*/
|
||||
uint32_t clock;
|
||||
/**
|
||||
* @brief Pointer to the TIMx registers block.
|
||||
*/
|
||||
stm32_tim_t *tim;
|
||||
};
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver macros. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Changes the period the PWM peripheral.
|
||||
* @details This function changes the period of a PWM unit that has already
|
||||
* been activated using @p pwmStart().
|
||||
* @pre The PWM unit must have been activated using @p pwmStart().
|
||||
* @post The PWM unit period is changed to the new value.
|
||||
* @note The function has effect at the next cycle start.
|
||||
* @note If a period is specified that is shorter than the pulse width
|
||||
* programmed in one of the channels then the behavior is not
|
||||
* guaranteed.
|
||||
*
|
||||
* @param[in] pwmp pointer to a @p PWMDriver object
|
||||
* @param[in] period new cycle time in ticks
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
#define pwm_lld_change_period(pwmp, period) \
|
||||
((pwmp)->tim->ARR = (uint16_t)((period) - 1))
|
||||
|
||||
/**
|
||||
* @brief Returns a PWM channel status.
|
||||
* @pre The PWM unit must have been activated using @p pwmStart().
|
||||
*
|
||||
* @param[in] pwmp pointer to a @p PWMDriver object
|
||||
* @param[in] channel PWM channel identifier (0...PWM_CHANNELS-1)
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
#define pwm_lld_is_channel_enabled(pwmp, channel) \
|
||||
(((pwmp)->tim->CCR[channel] != 0) || \
|
||||
(((pwmp)->tim->DIER & (2 << channel)) != 0))
|
||||
|
||||
/*===========================================================================*/
|
||||
/* External declarations. */
|
||||
/*===========================================================================*/
|
||||
|
||||
#if STM32_PWM_USE_TIM1 && !defined(__DOXYGEN__)
|
||||
extern PWMDriver PWMD1;
|
||||
#endif
|
||||
|
||||
#if STM32_PWM_USE_TIM2 && !defined(__DOXYGEN__)
|
||||
extern PWMDriver PWMD2;
|
||||
#endif
|
||||
|
||||
#if STM32_PWM_USE_TIM3 && !defined(__DOXYGEN__)
|
||||
extern PWMDriver PWMD3;
|
||||
#endif
|
||||
|
||||
#if STM32_PWM_USE_TIM4 && !defined(__DOXYGEN__)
|
||||
extern PWMDriver PWMD4;
|
||||
#endif
|
||||
|
||||
#if STM32_PWM_USE_TIM5 && !defined(__DOXYGEN__)
|
||||
extern PWMDriver PWMD5;
|
||||
#endif
|
||||
|
||||
#if STM32_PWM_USE_TIM8 && !defined(__DOXYGEN__)
|
||||
extern PWMDriver PWMD8;
|
||||
#endif
|
||||
|
||||
#if STM32_PWM_USE_TIM9 && !defined(__DOXYGEN__)
|
||||
extern PWMDriver PWMD9;
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
void pwm_lld_init(void);
|
||||
void pwm_lld_start(PWMDriver *pwmp);
|
||||
void pwm_lld_stop(PWMDriver *pwmp);
|
||||
void pwm_lld_enable_channel(PWMDriver *pwmp,
|
||||
pwmchannel_t channel,
|
||||
pwmcnt_t width);
|
||||
void pwm_lld_disable_channel(PWMDriver *pwmp, pwmchannel_t channel);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* HAL_USE_PWM */
|
||||
|
||||
#endif /* _PWM_LLD_H_ */
|
||||
|
||||
/** @} */
|
||||
+448
@@ -0,0 +1,448 @@
|
||||
/*
|
||||
ChibiOS/RT - Copyright (C) 2006-2013 Giovanni Di Sirio
|
||||
|
||||
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 stm32_tim.h
|
||||
* @brief STM32 TIM registers layout header.
|
||||
* @note This file requires definitions from the ST STM32 header file.
|
||||
*
|
||||
* @addtogroup STM32_TIMv1
|
||||
* @{
|
||||
*/
|
||||
|
||||
#ifndef _STM32_TIM_H_
|
||||
#define _STM32_TIM_H_
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver constants. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @name TIM_CR1 register
|
||||
* @{
|
||||
*/
|
||||
#define STM32_TIM_CR1_CEN (1U << 0)
|
||||
#define STM32_TIM_CR1_UDIS (1U << 1)
|
||||
#define STM32_TIM_CR1_URS (1U << 2)
|
||||
#define STM32_TIM_CR1_OPM (1U << 3)
|
||||
#define STM32_TIM_CR1_DIR (1U << 4)
|
||||
|
||||
#define STM32_TIM_CR1_CMS_MASK (3U << 5)
|
||||
#define STM32_TIM_CR1_CMS(n) ((n) << 5)
|
||||
|
||||
#define STM32_TIM_CR1_ARPE (1U << 7)
|
||||
|
||||
#define STM32_TIM_CR1_CKD_MASK (3U << 8)
|
||||
#define STM32_TIM_CR1_CKD(n) ((n) << 8)
|
||||
|
||||
#define STM32_TIM_CR1_UIFREMAP (1U << 11)
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name TIM_CR2 register
|
||||
* @{
|
||||
*/
|
||||
#define STM32_TIM_CR2_CCPC (1U << 0)
|
||||
#define STM32_TIM_CR2_CCUS (1U << 2)
|
||||
#define STM32_TIM_CR2_CCDS (1U << 3)
|
||||
|
||||
#define STM32_TIM_CR2_MMS_MASK (7U << 4)
|
||||
#define STM32_TIM_CR2_MMS(n) ((n) << 4)
|
||||
|
||||
#define STM32_TIM_CR2_TI1S (1U << 7)
|
||||
#define STM32_TIM_CR2_OIS1 (1U << 8)
|
||||
#define STM32_TIM_CR2_OIS1N (1U << 9)
|
||||
#define STM32_TIM_CR2_OIS2 (1U << 10)
|
||||
#define STM32_TIM_CR2_OIS2N (1U << 11)
|
||||
#define STM32_TIM_CR2_OIS3 (1U << 12)
|
||||
#define STM32_TIM_CR2_OIS3N (1U << 13)
|
||||
#define STM32_TIM_CR2_OIS4 (1U << 14)
|
||||
#define STM32_TIM_CR2_OIS5 (1U << 16)
|
||||
#define STM32_TIM_CR2_OIS6 (1U << 18)
|
||||
|
||||
#define STM32_TIM_CR2_MMS2_MASK (15U << 20)
|
||||
#define STM32_TIM_CR2_MMS2(n) ((n) << 20)
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name TIM_SMCR register
|
||||
* @{
|
||||
*/
|
||||
#define STM32_TIM_SMCR_SMS_MASK ((7U << 0) | (1U << 16))
|
||||
#define STM32_TIM_SMCR_SMS(n) ((((n) & 7) << 0) | \
|
||||
(((n) >> 3) << 16))
|
||||
|
||||
#define STM32_TIM_SMCR_OCCS (1U << 3)
|
||||
|
||||
#define STM32_TIM_SMCR_TS_MASK (7U << 4)
|
||||
#define STM32_TIM_SMCR_TS(n) ((n) << 4)
|
||||
|
||||
#define STM32_TIM_SMCR_MSM (1U << 7)
|
||||
|
||||
#define STM32_TIM_SMCR_ETF_MASK (15U << 8)
|
||||
#define STM32_TIM_SMCR_ETF(n) ((n) << 8)
|
||||
|
||||
#define STM32_TIM_SMCR_ETPS_MASK (3U << 12)
|
||||
#define STM32_TIM_SMCR_ETPS(n) ((n) << 12)
|
||||
|
||||
#define STM32_TIM_SMCR_ECE (1U << 14)
|
||||
#define STM32_TIM_SMCR_ETP (1U << 15)
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name TIM_DIER register
|
||||
* @{
|
||||
*/
|
||||
#define STM32_TIM_DIER_UIE (1U << 0)
|
||||
#define STM32_TIM_DIER_CC1IE (1U << 1)
|
||||
#define STM32_TIM_DIER_CC2IE (1U << 2)
|
||||
#define STM32_TIM_DIER_CC3IE (1U << 3)
|
||||
#define STM32_TIM_DIER_CC4IE (1U << 4)
|
||||
#define STM32_TIM_DIER_COMIE (1U << 5)
|
||||
#define STM32_TIM_DIER_TIE (1U << 6)
|
||||
#define STM32_TIM_DIER_BIE (1U << 7)
|
||||
#define STM32_TIM_DIER_UDE (1U << 8)
|
||||
#define STM32_TIM_DIER_CC1DE (1U << 9)
|
||||
#define STM32_TIM_DIER_CC2DE (1U << 10)
|
||||
#define STM32_TIM_DIER_CC3DE (1U << 11)
|
||||
#define STM32_TIM_DIER_CC4DE (1U << 12)
|
||||
#define STM32_TIM_DIER_COMDE (1U << 13)
|
||||
#define STM32_TIM_DIER_TDE (1U << 14)
|
||||
|
||||
#define STM32_TIM_DIER_IRQ_MASK (STM32_TIM_DIER_UIE | \
|
||||
STM32_TIM_DIER_CC1IE | \
|
||||
STM32_TIM_DIER_CC2IE | \
|
||||
STM32_TIM_DIER_CC3IE | \
|
||||
STM32_TIM_DIER_CC4IE | \
|
||||
STM32_TIM_DIER_COMIE | \
|
||||
STM32_TIM_DIER_TIE | \
|
||||
STM32_TIM_DIER_BIE)
|
||||
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name TIM_SR register
|
||||
* @{
|
||||
*/
|
||||
#define STM32_TIM_SR_UIF (1U << 0)
|
||||
#define STM32_TIM_SR_CC1IF (1U << 1)
|
||||
#define STM32_TIM_SR_CC2IF (1U << 2)
|
||||
#define STM32_TIM_SR_CC3IF (1U << 3)
|
||||
#define STM32_TIM_SR_CC4IF (1U << 4)
|
||||
#define STM32_TIM_SR_COMIF (1U << 5)
|
||||
#define STM32_TIM_SR_TIF (1U << 6)
|
||||
#define STM32_TIM_SR_BIF (1U << 7)
|
||||
#define STM32_TIM_SR_B2IF (1U << 8)
|
||||
#define STM32_TIM_SR_CC1OF (1U << 9)
|
||||
#define STM32_TIM_SR_CC2OF (1U << 10)
|
||||
#define STM32_TIM_SR_CC3OF (1U << 11)
|
||||
#define STM32_TIM_SR_CC4OF (1U << 12)
|
||||
#define STM32_TIM_SR_CC5IF (1U << 16)
|
||||
#define STM32_TIM_SR_CC6IF (1U << 17)
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name TIM_EGR register
|
||||
* @{
|
||||
*/
|
||||
#define STM32_TIM_EGR_UG (1U << 0)
|
||||
#define STM32_TIM_EGR_CC1G (1U << 1)
|
||||
#define STM32_TIM_EGR_CC2G (1U << 2)
|
||||
#define STM32_TIM_EGR_CC3G (1U << 3)
|
||||
#define STM32_TIM_EGR_CC4G (1U << 4)
|
||||
#define STM32_TIM_EGR_COMG (1U << 5)
|
||||
#define STM32_TIM_EGR_TG (1U << 6)
|
||||
#define STM32_TIM_EGR_BG (1U << 7)
|
||||
#define STM32_TIM_EGR_B2G (1U << 8)
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name TIM_CCMR1 register (output)
|
||||
* @{
|
||||
*/
|
||||
#define STM32_TIM_CCMR1_CC1S_MASK (3U << 0)
|
||||
#define STM32_TIM_CCMR1_CC1S(n) ((n) << 0)
|
||||
|
||||
#define STM32_TIM_CCMR1_OC1FE (1U << 2)
|
||||
#define STM32_TIM_CCMR1_OC1PE (1U << 3)
|
||||
|
||||
#define STM32_TIM_CCMR1_OC1M_MASK ((7U << 4) | (1U << 16))
|
||||
#define STM32_TIM_CCMR1_OC1M(n) ((((n) & 7) << 4) | \
|
||||
(((n) >> 3) << 16))
|
||||
|
||||
#define STM32_TIM_CCMR1_OC1CE (1U << 7)
|
||||
|
||||
#define STM32_TIM_CCMR1_CC2S_MASK (3U << 8)
|
||||
#define STM32_TIM_CCMR1_CC2S(n) ((n) << 8)
|
||||
|
||||
#define STM32_TIM_CCMR1_OC2FE (1U << 10)
|
||||
#define STM32_TIM_CCMR1_OC2PE (1U << 11)
|
||||
|
||||
#define STM32_TIM_CCMR1_OC2M_MASK ((7U << 12) | (1U << 24))
|
||||
#define STM32_TIM_CCMR1_OC2M(n) ((((n) & 7) << 12) | \
|
||||
(((n) >> 3) << 24))
|
||||
|
||||
#define STM32_TIM_CCMR1_OC2CE (1U << 15)
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name CCMR1 register (input)
|
||||
* @{
|
||||
*/
|
||||
#define STM32_TIM_CCMR1_IC1PSC_MASK (3U << 2)
|
||||
#define STM32_TIM_CCMR1_IC1PSC(n) ((n) << 2)
|
||||
|
||||
#define STM32_TIM_CCMR1_IC1F_MASK (15U << 4)
|
||||
#define STM32_TIM_CCMR1_IC1F(n) ((n) << 4)
|
||||
|
||||
#define STM32_TIM_CCMR1_IC2PSC_MASK (3U << 10)
|
||||
#define STM32_TIM_CCMR1_IC2PSC(n) ((n) << 10)
|
||||
|
||||
#define STM32_TIM_CCMR1_IC2F_MASK (15U << 12)
|
||||
#define STM32_TIM_CCMR1_IC2F(n) ((n) << 12)
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name TIM_CCMR2 register (output)
|
||||
* @{
|
||||
*/
|
||||
#define STM32_TIM_CCMR2_CC3S_MASK (3U << 0)
|
||||
#define STM32_TIM_CCMR2_CC3S(n) ((n) << 0)
|
||||
|
||||
#define STM32_TIM_CCMR2_OC3FE (1U << 2)
|
||||
#define STM32_TIM_CCMR2_OC3PE (1U << 3)
|
||||
|
||||
#define STM32_TIM_CCMR2_OC3M_MASK ((7U << 4) | (1U << 16))
|
||||
#define STM32_TIM_CCMR2_OC3M(n) ((((n) & 7) << 4) | \
|
||||
(((n) >> 3) << 16))
|
||||
|
||||
#define STM32_TIM_CCMR2_OC3CE (1U << 7)
|
||||
|
||||
#define STM32_TIM_CCMR2_CC4S_MASK (3U << 8)
|
||||
#define STM32_TIM_CCMR2_CC4S(n) ((n) << 8)
|
||||
|
||||
#define STM32_TIM_CCMR2_OC4FE (1U << 10)
|
||||
#define STM32_TIM_CCMR2_OC4PE (1U << 11)
|
||||
|
||||
#define STM32_TIM_CCMR2_OC4M_MASK ((7U << 12) | (1U << 24))
|
||||
#define STM32_TIM_CCMR2_OC4M(n) ((((n) & 7) << 12) | \
|
||||
(((n) >> 3) << 24))
|
||||
|
||||
#define STM32_TIM_CCMR2_OC4CE (1U << 15)
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name TIM_CCMR2 register (input)
|
||||
* @{
|
||||
*/
|
||||
#define STM32_TIM_CCMR2_IC3PSC_MASK (3U << 2)
|
||||
#define STM32_TIM_CCMR2_IC3PSC(n) ((n) << 2)
|
||||
|
||||
#define STM32_TIM_CCMR2_IC3F_MASK (15U << 4)
|
||||
#define STM32_TIM_CCMR2_IC3F(n) ((n) << 4)
|
||||
|
||||
#define STM32_TIM_CCMR2_IC4PSC_MASK (3U << 10)
|
||||
#define STM32_TIM_CCMR2_IC4PSC(n) ((n) << 10)
|
||||
|
||||
#define STM32_TIM_CCMR2_IC4F_MASK (15U << 12)
|
||||
#define STM32_TIM_CCMR2_IC4F(n) ((n) << 12)
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name TIM_CCER register
|
||||
* @{
|
||||
*/
|
||||
#define STM32_TIM_CCER_CC1E (1U << 0)
|
||||
#define STM32_TIM_CCER_CC1P (1U << 1)
|
||||
#define STM32_TIM_CCER_CC1NE (1U << 2)
|
||||
#define STM32_TIM_CCER_CC1NP (1U << 3)
|
||||
#define STM32_TIM_CCER_CC2E (1U << 4)
|
||||
#define STM32_TIM_CCER_CC2P (1U << 5)
|
||||
#define STM32_TIM_CCER_CC2NE (1U << 6)
|
||||
#define STM32_TIM_CCER_CC2NP (1U << 7)
|
||||
#define STM32_TIM_CCER_CC3E (1U << 8)
|
||||
#define STM32_TIM_CCER_CC3P (1U << 9)
|
||||
#define STM32_TIM_CCER_CC3NE (1U << 10)
|
||||
#define STM32_TIM_CCER_CC3NP (1U << 11)
|
||||
#define STM32_TIM_CCER_CC4E (1U << 12)
|
||||
#define STM32_TIM_CCER_CC4P (1U << 13)
|
||||
#define STM32_TIM_CCER_CC4NP (1U << 15)
|
||||
#define STM32_TIM_CCER_CC5E (1U << 16)
|
||||
#define STM32_TIM_CCER_CC5P (1U << 17)
|
||||
#define STM32_TIM_CCER_CC6E (1U << 20)
|
||||
#define STM32_TIM_CCER_CC6P (1U << 21)
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name TIM_CNT register
|
||||
* @{
|
||||
*/
|
||||
#define STM32_TIM_CNT_UIFCPY (1U << 31)
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name TIM_BDTR register
|
||||
* @{
|
||||
*/
|
||||
#define STM32_TIM_BDTR_DTG_MASK (255U << 0)
|
||||
#define STM32_TIM_BDTR_DTG(n) ((n) << 0)
|
||||
|
||||
#define STM32_TIM_BDTR_LOCK_MASK (3U << 8)
|
||||
#define STM32_TIM_BDTR_LOCK(n) ((n) << 8)
|
||||
|
||||
#define STM32_TIM_BDTR_OSSI (1U << 10)
|
||||
#define STM32_TIM_BDTR_OSSR (1U << 11)
|
||||
#define STM32_TIM_BDTR_BKE (1U << 12)
|
||||
#define STM32_TIM_BDTR_BKP (1U << 13)
|
||||
#define STM32_TIM_BDTR_AOE (1U << 14)
|
||||
#define STM32_TIM_BDTR_MOE (1U << 15)
|
||||
|
||||
#define STM32_TIM_BDTR_BKF_MASK (15U << 16)
|
||||
#define STM32_TIM_BDTR_BKF(n) ((n) << 16)
|
||||
#define STM32_TIM_BDTR_BK2F_MASK (15U << 20)
|
||||
#define STM32_TIM_BDTR_BK2F(n) ((n) << 20)
|
||||
|
||||
#define STM32_TIM_BDTR_BK2E (1U << 24)
|
||||
#define STM32_TIM_BDTR_BK2P (1U << 25)
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name TIM_DCR register
|
||||
* @{
|
||||
*/
|
||||
#define STM32_TIM_DCR_DBA_MASK (31U << 0)
|
||||
#define STM32_TIM_DCR_DBA(n) ((n) << 0)
|
||||
|
||||
#define STM32_TIM_DCR_DBL_MASK (31U << 8)
|
||||
#define STM32_TIM_DCR_DBL(b) ((n) << 8)
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name TIM16_OR register
|
||||
* @{
|
||||
*/
|
||||
#define STM32_TIM16_OR_TI1_RMP_MASK (3U << 6)
|
||||
#define STM32_TIM16_OR_TI1_RMP(n) ((n) << 6)
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name TIM_OR register
|
||||
* @{
|
||||
*/
|
||||
#define STM32_TIM_OR_ETR_RMP_MASK (15U << 0)
|
||||
#define STM32_TIM_OR_ETR_RMP(n) ((n) << 0)
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name TIM_CCMR3 register
|
||||
* @{
|
||||
*/
|
||||
#define STM32_TIM_CCMR3_OC5FE (1U << 2)
|
||||
#define STM32_TIM_CCMR3_OC5PE (1U << 3)
|
||||
|
||||
#define STM32_TIM_CCMR3_OC5M_MASK ((7U << 4) | (1U << 16))
|
||||
#define STM32_TIM_CCMR3_OC5M(n) ((((n) & 7) << 4) | \
|
||||
(((n) >> 2) << 16))
|
||||
|
||||
#define STM32_TIM_CCMR3_OC5CE (1U << 7)
|
||||
|
||||
#define STM32_TIM_CCMR3_OC6FE (1U << 10)
|
||||
#define STM32_TIM_CCMR3_OC6PE (1U << 11)
|
||||
|
||||
#define STM32_TIM_CCMR3_OC6M_MASK ((7U << 12) | (1U << 24))
|
||||
#define STM32_TIM_CCMR3_OC6M(n) ((((n) & 7) << 12) | \
|
||||
(((n) >> 2) << 24))
|
||||
|
||||
#define STM32_TIM_CCMR3_OC6CE (1U << 15)
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name TIM units references
|
||||
* @{
|
||||
*/
|
||||
#define STM32_TIM1 ((stm32_tim_t *)TIM1_BASE)
|
||||
#define STM32_TIM2 ((stm32_tim_t *)TIM2_BASE)
|
||||
#define STM32_TIM3 ((stm32_tim_t *)TIM3_BASE)
|
||||
#define STM32_TIM4 ((stm32_tim_t *)TIM4_BASE)
|
||||
#define STM32_TIM5 ((stm32_tim_t *)TIM5_BASE)
|
||||
#define STM32_TIM6 ((stm32_tim_t *)TIM6_BASE)
|
||||
#define STM32_TIM7 ((stm32_tim_t *)TIM7_BASE)
|
||||
#define STM32_TIM8 ((stm32_tim_t *)TIM8_BASE)
|
||||
#define STM32_TIM9 ((stm32_tim_t *)TIM9_BASE)
|
||||
#define STM32_TIM10 ((stm32_tim_t *)TIM10_BASE)
|
||||
#define STM32_TIM11 ((stm32_tim_t *)TIM11_BASE)
|
||||
#define STM32_TIM12 ((stm32_tim_t *)TIM12_BASE)
|
||||
#define STM32_TIM13 ((stm32_tim_t *)TIM13_BASE)
|
||||
#define STM32_TIM14 ((stm32_tim_t *)TIM14_BASE)
|
||||
#define STM32_TIM15 ((stm32_tim_t *)TIM15_BASE)
|
||||
#define STM32_TIM16 ((stm32_tim_t *)TIM16_BASE)
|
||||
#define STM32_TIM17 ((stm32_tim_t *)TIM17_BASE)
|
||||
#define STM32_TIM18 ((stm32_tim_t *)TIM18_BASE)
|
||||
#define STM32_TIM19 ((stm32_tim_t *)TIM19_BASE)
|
||||
/** @} */
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver pre-compile time settings. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Derived constants and error checks. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver data structures and types. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief STM32 TIM registers block.
|
||||
* @note This is the most general known form, not all timers have
|
||||
* necessarily all registers and bits.
|
||||
*/
|
||||
typedef struct {
|
||||
volatile uint32_t CR1;
|
||||
volatile uint32_t CR2;
|
||||
volatile uint32_t SMCR;
|
||||
volatile uint32_t DIER;
|
||||
volatile uint32_t SR;
|
||||
volatile uint32_t EGR;
|
||||
volatile uint32_t CCMR1;
|
||||
volatile uint32_t CCMR2;
|
||||
volatile uint32_t CCER;
|
||||
volatile uint32_t CNT;
|
||||
volatile uint32_t PSC;
|
||||
volatile uint32_t ARR;
|
||||
volatile uint32_t RCR;
|
||||
volatile uint32_t CCR[4];
|
||||
volatile uint32_t BDTR;
|
||||
volatile uint32_t DCR;
|
||||
volatile uint32_t DMAR;
|
||||
volatile uint32_t OR;
|
||||
volatile uint32_t CCMR3;
|
||||
volatile uint32_t CCR5;
|
||||
volatile uint32_t CCR6;
|
||||
} stm32_tim_t;
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver macros. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* External declarations. */
|
||||
/*===========================================================================*/
|
||||
|
||||
#endif /* _STM32_TIM_H_ */
|
||||
|
||||
/** @} */
|
||||
@@ -0,0 +1,538 @@
|
||||
/*
|
||||
ChibiOS/RT - Copyright (C) 2006-2013 Giovanni Di Sirio
|
||||
|
||||
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 STM32/USARTv1/serial_lld.c
|
||||
* @brief STM32 low level serial driver code.
|
||||
*
|
||||
* @addtogroup SERIAL
|
||||
* @{
|
||||
*/
|
||||
|
||||
#include "ch.h"
|
||||
#include "hal.h"
|
||||
|
||||
#if HAL_USE_SERIAL || defined(__DOXYGEN__)
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local definitions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver exported variables. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/** @brief USART1 serial driver identifier.*/
|
||||
#if STM32_SERIAL_USE_USART1 || defined(__DOXYGEN__)
|
||||
SerialDriver SD1;
|
||||
#endif
|
||||
|
||||
/** @brief USART2 serial driver identifier.*/
|
||||
#if STM32_SERIAL_USE_USART2 || defined(__DOXYGEN__)
|
||||
SerialDriver SD2;
|
||||
#endif
|
||||
|
||||
/** @brief USART3 serial driver identifier.*/
|
||||
#if STM32_SERIAL_USE_USART3 || defined(__DOXYGEN__)
|
||||
SerialDriver SD3;
|
||||
#endif
|
||||
|
||||
/** @brief UART4 serial driver identifier.*/
|
||||
#if STM32_SERIAL_USE_UART4 || defined(__DOXYGEN__)
|
||||
SerialDriver SD4;
|
||||
#endif
|
||||
|
||||
/** @brief UART5 serial driver identifier.*/
|
||||
#if STM32_SERIAL_USE_UART5 || defined(__DOXYGEN__)
|
||||
SerialDriver SD5;
|
||||
#endif
|
||||
|
||||
/** @brief USART6 serial driver identifier.*/
|
||||
#if STM32_SERIAL_USE_USART6 || defined(__DOXYGEN__)
|
||||
SerialDriver SD6;
|
||||
#endif
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local variables and types. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/** @brief Driver default configuration.*/
|
||||
static const SerialConfig default_config =
|
||||
{
|
||||
SERIAL_DEFAULT_BITRATE,
|
||||
0,
|
||||
USART_CR2_STOP1_BITS | USART_CR2_LINEN,
|
||||
0
|
||||
};
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief USART initialization.
|
||||
* @details This function must be invoked with interrupts disabled.
|
||||
*
|
||||
* @param[in] sdp pointer to a @p SerialDriver object
|
||||
* @param[in] config the architecture-dependent serial driver configuration
|
||||
*/
|
||||
static void usart_init(SerialDriver *sdp, const SerialConfig *config) {
|
||||
USART_TypeDef *u = sdp->usart;
|
||||
|
||||
/* Baud rate setting.*/
|
||||
#if STM32_HAS_USART6
|
||||
if ((sdp->usart == USART1) || (sdp->usart == USART6))
|
||||
#else
|
||||
if (sdp->usart == USART1)
|
||||
#endif
|
||||
u->BRR = STM32_PCLK2 / config->speed;
|
||||
else
|
||||
u->BRR = STM32_PCLK1 / config->speed;
|
||||
|
||||
/* Note that some bits are enforced.*/
|
||||
u->CR2 = config->cr2 | USART_CR2_LBDIE;
|
||||
u->CR3 = config->cr3 | USART_CR3_EIE;
|
||||
u->CR1 = config->cr1 | USART_CR1_UE | USART_CR1_PEIE |
|
||||
USART_CR1_RXNEIE | USART_CR1_TE |
|
||||
USART_CR1_RE;
|
||||
u->SR = 0;
|
||||
(void)u->SR; /* SR reset step 1.*/
|
||||
(void)u->DR; /* SR reset step 2.*/
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief USART de-initialization.
|
||||
* @details This function must be invoked with interrupts disabled.
|
||||
*
|
||||
* @param[in] u pointer to an USART I/O block
|
||||
*/
|
||||
static void usart_deinit(USART_TypeDef *u) {
|
||||
|
||||
u->CR1 = 0;
|
||||
u->CR2 = 0;
|
||||
u->CR3 = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Error handling routine.
|
||||
*
|
||||
* @param[in] sdp pointer to a @p SerialDriver object
|
||||
* @param[in] sr USART SR register value
|
||||
*/
|
||||
static void set_error(SerialDriver *sdp, uint16_t sr) {
|
||||
flagsmask_t sts = 0;
|
||||
|
||||
if (sr & USART_SR_ORE)
|
||||
sts |= SD_OVERRUN_ERROR;
|
||||
if (sr & USART_SR_PE)
|
||||
sts |= SD_PARITY_ERROR;
|
||||
if (sr & USART_SR_FE)
|
||||
sts |= SD_FRAMING_ERROR;
|
||||
if (sr & USART_SR_NE)
|
||||
sts |= SD_NOISE_ERROR;
|
||||
chnAddFlagsI(sdp, sts);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Common IRQ handler.
|
||||
*
|
||||
* @param[in] sdp communication channel associated to the USART
|
||||
*/
|
||||
static void serve_interrupt(SerialDriver *sdp) {
|
||||
USART_TypeDef *u = sdp->usart;
|
||||
uint16_t cr1 = u->CR1;
|
||||
uint16_t sr = u->SR;
|
||||
|
||||
/* Special case, LIN break detection.*/
|
||||
if (sr & USART_SR_LBD) {
|
||||
chSysLockFromIsr();
|
||||
chnAddFlagsI(sdp, SD_BREAK_DETECTED);
|
||||
chSysUnlockFromIsr();
|
||||
u->SR = ~USART_SR_LBD;
|
||||
}
|
||||
|
||||
/* Data available.*/
|
||||
chSysLockFromIsr();
|
||||
while (sr & (USART_SR_RXNE | USART_SR_ORE | USART_SR_NE | USART_SR_FE |
|
||||
USART_SR_PE)) {
|
||||
uint8_t b;
|
||||
|
||||
/* Error condition detection.*/
|
||||
if (sr & (USART_SR_ORE | USART_SR_NE | USART_SR_FE | USART_SR_PE))
|
||||
set_error(sdp, sr);
|
||||
b = u->DR;
|
||||
if (sr & USART_SR_RXNE)
|
||||
sdIncomingDataI(sdp, b);
|
||||
sr = u->SR;
|
||||
}
|
||||
chSysUnlockFromIsr();
|
||||
|
||||
/* Transmission buffer empty.*/
|
||||
if ((cr1 & USART_CR1_TXEIE) && (sr & USART_SR_TXE)) {
|
||||
msg_t b;
|
||||
chSysLockFromIsr();
|
||||
b = chOQGetI(&sdp->oqueue);
|
||||
if (b < Q_OK) {
|
||||
chnAddFlagsI(sdp, CHN_OUTPUT_EMPTY);
|
||||
u->CR1 = (cr1 & ~USART_CR1_TXEIE) | USART_CR1_TCIE;
|
||||
}
|
||||
else
|
||||
u->DR = b;
|
||||
chSysUnlockFromIsr();
|
||||
}
|
||||
|
||||
/* Physical transmission end.*/
|
||||
if (sr & USART_SR_TC) {
|
||||
chSysLockFromIsr();
|
||||
if (chOQIsEmptyI(&sdp->oqueue))
|
||||
chnAddFlagsI(sdp, CHN_TRANSMISSION_END);
|
||||
u->CR1 = cr1 & ~USART_CR1_TCIE;
|
||||
u->SR = ~USART_SR_TC;
|
||||
chSysUnlockFromIsr();
|
||||
}
|
||||
}
|
||||
|
||||
#if STM32_SERIAL_USE_USART1 || defined(__DOXYGEN__)
|
||||
static void notify1(GenericQueue *qp) {
|
||||
|
||||
(void)qp;
|
||||
USART1->CR1 |= USART_CR1_TXEIE;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if STM32_SERIAL_USE_USART2 || defined(__DOXYGEN__)
|
||||
static void notify2(GenericQueue *qp) {
|
||||
|
||||
(void)qp;
|
||||
USART2->CR1 |= USART_CR1_TXEIE;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if STM32_SERIAL_USE_USART3 || defined(__DOXYGEN__)
|
||||
static void notify3(GenericQueue *qp) {
|
||||
|
||||
(void)qp;
|
||||
USART3->CR1 |= USART_CR1_TXEIE;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if STM32_SERIAL_USE_UART4 || defined(__DOXYGEN__)
|
||||
static void notify4(GenericQueue *qp) {
|
||||
|
||||
(void)qp;
|
||||
UART4->CR1 |= USART_CR1_TXEIE;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if STM32_SERIAL_USE_UART5 || defined(__DOXYGEN__)
|
||||
static void notify5(GenericQueue *qp) {
|
||||
|
||||
(void)qp;
|
||||
UART5->CR1 |= USART_CR1_TXEIE;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if STM32_SERIAL_USE_USART6 || defined(__DOXYGEN__)
|
||||
static void notify6(GenericQueue *qp) {
|
||||
|
||||
(void)qp;
|
||||
USART6->CR1 |= USART_CR1_TXEIE;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver interrupt handlers. */
|
||||
/*===========================================================================*/
|
||||
|
||||
#if STM32_SERIAL_USE_USART1 || defined(__DOXYGEN__)
|
||||
#if !defined(STM32_USART1_HANDLER)
|
||||
#error "STM32_USART1_HANDLER not defined"
|
||||
#endif
|
||||
/**
|
||||
* @brief USART1 interrupt handler.
|
||||
*
|
||||
* @isr
|
||||
*/
|
||||
CH_IRQ_HANDLER(STM32_USART1_HANDLER) {
|
||||
|
||||
CH_IRQ_PROLOGUE();
|
||||
|
||||
serve_interrupt(&SD1);
|
||||
|
||||
CH_IRQ_EPILOGUE();
|
||||
}
|
||||
#endif
|
||||
|
||||
#if STM32_SERIAL_USE_USART2 || defined(__DOXYGEN__)
|
||||
#if !defined(STM32_USART2_HANDLER)
|
||||
#error "STM32_USART2_HANDLER not defined"
|
||||
#endif
|
||||
/**
|
||||
* @brief USART2 interrupt handler.
|
||||
*
|
||||
* @isr
|
||||
*/
|
||||
CH_IRQ_HANDLER(STM32_USART2_HANDLER) {
|
||||
|
||||
CH_IRQ_PROLOGUE();
|
||||
|
||||
serve_interrupt(&SD2);
|
||||
|
||||
CH_IRQ_EPILOGUE();
|
||||
}
|
||||
#endif
|
||||
|
||||
#if STM32_SERIAL_USE_USART3 || defined(__DOXYGEN__)
|
||||
#if !defined(STM32_USART3_HANDLER)
|
||||
#error "STM32_USART3_HANDLER not defined"
|
||||
#endif
|
||||
/**
|
||||
* @brief USART3 interrupt handler.
|
||||
*
|
||||
* @isr
|
||||
*/
|
||||
CH_IRQ_HANDLER(STM32_USART3_HANDLER) {
|
||||
|
||||
CH_IRQ_PROLOGUE();
|
||||
|
||||
serve_interrupt(&SD3);
|
||||
|
||||
CH_IRQ_EPILOGUE();
|
||||
}
|
||||
#endif
|
||||
|
||||
#if STM32_SERIAL_USE_UART4 || defined(__DOXYGEN__)
|
||||
#if !defined(STM32_UART4_HANDLER)
|
||||
#error "STM32_UART4_HANDLER not defined"
|
||||
#endif
|
||||
/**
|
||||
* @brief UART4 interrupt handler.
|
||||
*
|
||||
* @isr
|
||||
*/
|
||||
CH_IRQ_HANDLER(STM32_UART4_HANDLER) {
|
||||
|
||||
CH_IRQ_PROLOGUE();
|
||||
|
||||
serve_interrupt(&SD4);
|
||||
|
||||
CH_IRQ_EPILOGUE();
|
||||
}
|
||||
#endif
|
||||
|
||||
#if STM32_SERIAL_USE_UART5 || defined(__DOXYGEN__)
|
||||
#if !defined(STM32_UART5_HANDLER)
|
||||
#error "STM32_UART5_HANDLER not defined"
|
||||
#endif
|
||||
/**
|
||||
* @brief UART5 interrupt handler.
|
||||
*
|
||||
* @isr
|
||||
*/
|
||||
CH_IRQ_HANDLER(STM32_UART5_HANDLER) {
|
||||
|
||||
CH_IRQ_PROLOGUE();
|
||||
|
||||
serve_interrupt(&SD5);
|
||||
|
||||
CH_IRQ_EPILOGUE();
|
||||
}
|
||||
#endif
|
||||
|
||||
#if STM32_SERIAL_USE_USART6 || defined(__DOXYGEN__)
|
||||
#if !defined(STM32_USART6_HANDLER)
|
||||
#error "STM32_USART6_HANDLER not defined"
|
||||
#endif
|
||||
/**
|
||||
* @brief USART1 interrupt handler.
|
||||
*
|
||||
* @isr
|
||||
*/
|
||||
CH_IRQ_HANDLER(STM32_USART6_HANDLER) {
|
||||
|
||||
CH_IRQ_PROLOGUE();
|
||||
|
||||
serve_interrupt(&SD6);
|
||||
|
||||
CH_IRQ_EPILOGUE();
|
||||
}
|
||||
#endif
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver exported functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Low level serial driver initialization.
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void sd_lld_init(void) {
|
||||
|
||||
#if STM32_SERIAL_USE_USART1
|
||||
sdObjectInit(&SD1, NULL, notify1);
|
||||
SD1.usart = USART1;
|
||||
#endif
|
||||
|
||||
#if STM32_SERIAL_USE_USART2
|
||||
sdObjectInit(&SD2, NULL, notify2);
|
||||
SD2.usart = USART2;
|
||||
#endif
|
||||
|
||||
#if STM32_SERIAL_USE_USART3
|
||||
sdObjectInit(&SD3, NULL, notify3);
|
||||
SD3.usart = USART3;
|
||||
#endif
|
||||
|
||||
#if STM32_SERIAL_USE_UART4
|
||||
sdObjectInit(&SD4, NULL, notify4);
|
||||
SD4.usart = UART4;
|
||||
#endif
|
||||
|
||||
#if STM32_SERIAL_USE_UART5
|
||||
sdObjectInit(&SD5, NULL, notify5);
|
||||
SD5.usart = UART5;
|
||||
#endif
|
||||
|
||||
#if STM32_SERIAL_USE_USART6
|
||||
sdObjectInit(&SD6, NULL, notify6);
|
||||
SD6.usart = USART6;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @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) {
|
||||
#if STM32_SERIAL_USE_USART1
|
||||
if (&SD1 == sdp) {
|
||||
rccEnableUSART1(FALSE);
|
||||
nvicEnableVector(STM32_USART1_NUMBER,
|
||||
CORTEX_PRIORITY_MASK(STM32_SERIAL_USART1_PRIORITY));
|
||||
}
|
||||
#endif
|
||||
#if STM32_SERIAL_USE_USART2
|
||||
if (&SD2 == sdp) {
|
||||
rccEnableUSART2(FALSE);
|
||||
nvicEnableVector(STM32_USART2_NUMBER,
|
||||
CORTEX_PRIORITY_MASK(STM32_SERIAL_USART2_PRIORITY));
|
||||
}
|
||||
#endif
|
||||
#if STM32_SERIAL_USE_USART3
|
||||
if (&SD3 == sdp) {
|
||||
rccEnableUSART3(FALSE);
|
||||
nvicEnableVector(STM32_USART3_NUMBER,
|
||||
CORTEX_PRIORITY_MASK(STM32_SERIAL_USART3_PRIORITY));
|
||||
}
|
||||
#endif
|
||||
#if STM32_SERIAL_USE_UART4
|
||||
if (&SD4 == sdp) {
|
||||
rccEnableUART4(FALSE);
|
||||
nvicEnableVector(STM32_UART4_NUMBER,
|
||||
CORTEX_PRIORITY_MASK(STM32_SERIAL_UART4_PRIORITY));
|
||||
}
|
||||
#endif
|
||||
#if STM32_SERIAL_USE_UART5
|
||||
if (&SD5 == sdp) {
|
||||
rccEnableUART5(FALSE);
|
||||
nvicEnableVector(STM32_UART5_NUMBER,
|
||||
CORTEX_PRIORITY_MASK(STM32_SERIAL_UART5_PRIORITY));
|
||||
}
|
||||
#endif
|
||||
#if STM32_SERIAL_USE_USART6
|
||||
if (&SD6 == sdp) {
|
||||
rccEnableUSART6(FALSE);
|
||||
nvicEnableVector(STM32_USART6_NUMBER,
|
||||
CORTEX_PRIORITY_MASK(STM32_SERIAL_USART6_PRIORITY));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
usart_init(sdp, config);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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) {
|
||||
usart_deinit(sdp->usart);
|
||||
#if STM32_SERIAL_USE_USART1
|
||||
if (&SD1 == sdp) {
|
||||
rccDisableUSART1(FALSE);
|
||||
nvicDisableVector(STM32_USART1_NUMBER);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
#if STM32_SERIAL_USE_USART2
|
||||
if (&SD2 == sdp) {
|
||||
rccDisableUSART2(FALSE);
|
||||
nvicDisableVector(STM32_USART2_NUMBER);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
#if STM32_SERIAL_USE_USART3
|
||||
if (&SD3 == sdp) {
|
||||
rccDisableUSART3(FALSE);
|
||||
nvicDisableVector(STM32_USART3_NUMBER);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
#if STM32_SERIAL_USE_UART4
|
||||
if (&SD4 == sdp) {
|
||||
rccDisableUART4(FALSE);
|
||||
nvicDisableVector(STM32_UART4_NUMBER);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
#if STM32_SERIAL_USE_UART5
|
||||
if (&SD5 == sdp) {
|
||||
rccDisableUART5(FALSE);
|
||||
nvicDisableVector(STM32_UART5_NUMBER);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
#if STM32_SERIAL_USE_USART6
|
||||
if (&SD6 == sdp) {
|
||||
rccDisableUSART6(FALSE);
|
||||
nvicDisableVector(STM32_USART6_NUMBER);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* HAL_USE_SERIAL */
|
||||
|
||||
/** @} */
|
||||
@@ -0,0 +1,303 @@
|
||||
/*
|
||||
ChibiOS/RT - Copyright (C) 2006-2013 Giovanni Di Sirio
|
||||
|
||||
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 STM32/USARTv1/serial_lld.h
|
||||
* @brief STM32 low level serial driver header.
|
||||
*
|
||||
* @addtogroup SERIAL
|
||||
* @{
|
||||
*/
|
||||
|
||||
#ifndef _SERIAL_LLD_H_
|
||||
#define _SERIAL_LLD_H_
|
||||
|
||||
#if HAL_USE_SERIAL || defined(__DOXYGEN__)
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver constants. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver pre-compile time settings. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @name Configuration options
|
||||
* @{
|
||||
*/
|
||||
/**
|
||||
* @brief USART1 driver enable switch.
|
||||
* @details If set to @p TRUE the support for USART1 is included.
|
||||
* @note The default is @p TRUE.
|
||||
*/
|
||||
#if !defined(STM32_SERIAL_USE_USART1) || defined(__DOXYGEN__)
|
||||
#define STM32_SERIAL_USE_USART1 FALSE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief USART2 driver enable switch.
|
||||
* @details If set to @p TRUE the support for USART2 is included.
|
||||
* @note The default is @p TRUE.
|
||||
*/
|
||||
#if !defined(STM32_SERIAL_USE_USART2) || defined(__DOXYGEN__)
|
||||
#define STM32_SERIAL_USE_USART2 FALSE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief USART3 driver enable switch.
|
||||
* @details If set to @p TRUE the support for USART3 is included.
|
||||
* @note The default is @p TRUE.
|
||||
*/
|
||||
#if !defined(STM32_SERIAL_USE_USART3) || defined(__DOXYGEN__)
|
||||
#define STM32_SERIAL_USE_USART3 FALSE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief UART4 driver enable switch.
|
||||
* @details If set to @p TRUE the support for UART4 is included.
|
||||
* @note The default is @p TRUE.
|
||||
*/
|
||||
#if !defined(STM32_SERIAL_USE_UART4) || defined(__DOXYGEN__)
|
||||
#define STM32_SERIAL_USE_UART4 FALSE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief UART5 driver enable switch.
|
||||
* @details If set to @p TRUE the support for UART5 is included.
|
||||
* @note The default is @p TRUE.
|
||||
*/
|
||||
#if !defined(STM32_SERIAL_USE_UART5) || defined(__DOXYGEN__)
|
||||
#define STM32_SERIAL_USE_UART5 FALSE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief USART6 driver enable switch.
|
||||
* @details If set to @p TRUE the support for USART6 is included.
|
||||
* @note The default is @p TRUE.
|
||||
*/
|
||||
#if !defined(STM32_SERIAL_USE_USART6) || defined(__DOXYGEN__)
|
||||
#define STM32_SERIAL_USE_USART6 FALSE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief USART1 interrupt priority level setting.
|
||||
*/
|
||||
#if !defined(STM32_SERIAL_USART1_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define STM32_SERIAL_USART1_PRIORITY 12
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief USART2 interrupt priority level setting.
|
||||
*/
|
||||
#if !defined(STM32_SERIAL_USART2_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define STM32_SERIAL_USART2_PRIORITY 12
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief USART3 interrupt priority level setting.
|
||||
*/
|
||||
#if !defined(STM32_SERIAL_USART3_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define STM32_SERIAL_USART3_PRIORITY 12
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief UART4 interrupt priority level setting.
|
||||
*/
|
||||
#if !defined(STM32_SERIAL_UART4_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define STM32_SERIAL_UART4_PRIORITY 12
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief UART5 interrupt priority level setting.
|
||||
*/
|
||||
#if !defined(STM32_SERIAL_UART5_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define STM32_SERIAL_UART5_PRIORITY 12
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief USART6 interrupt priority level setting.
|
||||
*/
|
||||
#if !defined(STM32_SERIAL_USART6_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define STM32_SERIAL_USART6_PRIORITY 12
|
||||
#endif
|
||||
/** @} */
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Derived constants and error checks. */
|
||||
/*===========================================================================*/
|
||||
|
||||
#if STM32_SERIAL_USE_USART1 && !STM32_HAS_USART1
|
||||
#error "USART1 not present in the selected device"
|
||||
#endif
|
||||
|
||||
#if STM32_SERIAL_USE_USART2 && !STM32_HAS_USART2
|
||||
#error "USART2 not present in the selected device"
|
||||
#endif
|
||||
|
||||
#if STM32_SERIAL_USE_USART3 && !STM32_HAS_USART3
|
||||
#error "USART3 not present in the selected device"
|
||||
#endif
|
||||
|
||||
#if STM32_SERIAL_USE_UART4 && !STM32_HAS_UART4
|
||||
#error "UART4 not present in the selected device"
|
||||
#endif
|
||||
|
||||
#if STM32_SERIAL_USE_UART5 && !STM32_HAS_UART5
|
||||
#error "UART5 not present in the selected device"
|
||||
#endif
|
||||
|
||||
#if STM32_SERIAL_USE_USART6 && !STM32_HAS_USART6
|
||||
#error "USART6 not present in the selected device"
|
||||
#endif
|
||||
|
||||
#if !STM32_SERIAL_USE_USART1 && !STM32_SERIAL_USE_USART2 && \
|
||||
!STM32_SERIAL_USE_USART3 && !STM32_SERIAL_USE_UART4 && \
|
||||
!STM32_SERIAL_USE_UART5 && !STM32_SERIAL_USE_USART6
|
||||
#error "SERIAL driver activated but no USART/UART peripheral assigned"
|
||||
#endif
|
||||
|
||||
#if STM32_SERIAL_USE_USART1 && \
|
||||
!CORTEX_IS_VALID_KERNEL_PRIORITY(STM32_SERIAL_USART1_PRIORITY)
|
||||
#error "Invalid IRQ priority assigned to USART1"
|
||||
#endif
|
||||
|
||||
#if STM32_SERIAL_USE_USART2 && \
|
||||
!CORTEX_IS_VALID_KERNEL_PRIORITY(STM32_SERIAL_USART2_PRIORITY)
|
||||
#error "Invalid IRQ priority assigned to USART2"
|
||||
#endif
|
||||
|
||||
#if STM32_SERIAL_USE_USART3 && \
|
||||
!CORTEX_IS_VALID_KERNEL_PRIORITY(STM32_SERIAL_USART3_PRIORITY)
|
||||
#error "Invalid IRQ priority assigned to USART3"
|
||||
#endif
|
||||
|
||||
#if STM32_SERIAL_USE_UART4 && \
|
||||
!CORTEX_IS_VALID_KERNEL_PRIORITY(STM32_SERIAL_UART4_PRIORITY)
|
||||
#error "Invalid IRQ priority assigned to UART4"
|
||||
#endif
|
||||
|
||||
#if STM32_SERIAL_USE_UART5 && \
|
||||
!CORTEX_IS_VALID_KERNEL_PRIORITY(STM32_SERIAL_UART5_PRIORITY)
|
||||
#error "Invalid IRQ priority assigned to UART5"
|
||||
#endif
|
||||
|
||||
#if STM32_SERIAL_USE_USART6 && \
|
||||
!CORTEX_IS_VALID_KERNEL_PRIORITY(STM32_SERIAL_USART6_PRIORITY)
|
||||
#error "Invalid IRQ priority assigned to USART6"
|
||||
#endif
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver data structures and types. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief STM32 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 This structure content is architecture dependent, each driver
|
||||
* implementation defines its own version and the custom static
|
||||
* initializers.
|
||||
*/
|
||||
typedef struct {
|
||||
/**
|
||||
* @brief Bit rate.
|
||||
*/
|
||||
uint32_t speed;
|
||||
/* End of the mandatory fields.*/
|
||||
/**
|
||||
* @brief Initialization value for the CR1 register.
|
||||
*/
|
||||
uint16_t cr1;
|
||||
/**
|
||||
* @brief Initialization value for the CR2 register.
|
||||
*/
|
||||
uint16_t cr2;
|
||||
/**
|
||||
* @brief Initialization value for the CR3 register.
|
||||
*/
|
||||
uint16_t cr3;
|
||||
} SerialConfig;
|
||||
|
||||
/**
|
||||
* @brief @p SerialDriver specific data.
|
||||
*/
|
||||
#define _serial_driver_data \
|
||||
_base_asynchronous_channel_data \
|
||||
/* Driver state.*/ \
|
||||
sdstate_t state; \
|
||||
/* Input queue.*/ \
|
||||
InputQueue iqueue; \
|
||||
/* Output queue.*/ \
|
||||
OutputQueue 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 USART registers block.*/ \
|
||||
USART_TypeDef *usart;
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver macros. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*
|
||||
* Extra USARTs definitions here (missing from the ST header file).
|
||||
*/
|
||||
#define USART_CR2_STOP1_BITS (0 << 12) /**< @brief CR2 1 stop bit value.*/
|
||||
#define USART_CR2_STOP0P5_BITS (1 << 12) /**< @brief CR2 0.5 stop bit value.*/
|
||||
#define USART_CR2_STOP2_BITS (2 << 12) /**< @brief CR2 2 stop bit value.*/
|
||||
#define USART_CR2_STOP1P5_BITS (3 << 12) /**< @brief CR2 1.5 stop bit value.*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* External declarations. */
|
||||
/*===========================================================================*/
|
||||
|
||||
#if STM32_SERIAL_USE_USART1 && !defined(__DOXYGEN__)
|
||||
extern SerialDriver SD1;
|
||||
#endif
|
||||
#if STM32_SERIAL_USE_USART2 && !defined(__DOXYGEN__)
|
||||
extern SerialDriver SD2;
|
||||
#endif
|
||||
#if STM32_SERIAL_USE_USART3 && !defined(__DOXYGEN__)
|
||||
extern SerialDriver SD3;
|
||||
#endif
|
||||
#if STM32_SERIAL_USE_UART4 && !defined(__DOXYGEN__)
|
||||
extern SerialDriver SD4;
|
||||
#endif
|
||||
#if STM32_SERIAL_USE_UART5 && !defined(__DOXYGEN__)
|
||||
extern SerialDriver SD5;
|
||||
#endif
|
||||
#if STM32_SERIAL_USE_USART6 && !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 /* _SERIAL_LLD_H_ */
|
||||
|
||||
/** @} */
|
||||
+834
@@ -0,0 +1,834 @@
|
||||
/*
|
||||
ChibiOS/RT - Copyright (C) 2006-2013 Giovanni Di Sirio
|
||||
|
||||
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 STM32/USARTv1/uart_lld.c
|
||||
* @brief STM32 low level UART driver code.
|
||||
*
|
||||
* @addtogroup UART
|
||||
* @{
|
||||
*/
|
||||
|
||||
#include "ch.h"
|
||||
#include "hal.h"
|
||||
|
||||
#if HAL_USE_UART || defined(__DOXYGEN__)
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local definitions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
#define USART1_RX_DMA_CHANNEL \
|
||||
STM32_DMA_GETCHANNEL(STM32_UART_USART1_RX_DMA_STREAM, \
|
||||
STM32_USART1_RX_DMA_CHN)
|
||||
|
||||
#define USART1_TX_DMA_CHANNEL \
|
||||
STM32_DMA_GETCHANNEL(STM32_UART_USART1_TX_DMA_STREAM, \
|
||||
STM32_USART1_TX_DMA_CHN)
|
||||
|
||||
#define USART2_RX_DMA_CHANNEL \
|
||||
STM32_DMA_GETCHANNEL(STM32_UART_USART2_RX_DMA_STREAM, \
|
||||
STM32_USART2_RX_DMA_CHN)
|
||||
|
||||
#define USART2_TX_DMA_CHANNEL \
|
||||
STM32_DMA_GETCHANNEL(STM32_UART_USART2_TX_DMA_STREAM, \
|
||||
STM32_USART2_TX_DMA_CHN)
|
||||
|
||||
#define USART3_RX_DMA_CHANNEL \
|
||||
STM32_DMA_GETCHANNEL(STM32_UART_USART3_RX_DMA_STREAM, \
|
||||
STM32_USART3_RX_DMA_CHN)
|
||||
|
||||
#define USART3_TX_DMA_CHANNEL \
|
||||
STM32_DMA_GETCHANNEL(STM32_UART_USART3_TX_DMA_STREAM, \
|
||||
STM32_USART3_TX_DMA_CHN)
|
||||
|
||||
#define UART4_RX_DMA_CHANNEL \
|
||||
STM32_DMA_GETCHANNEL(STM32_UART_UART4_RX_DMA_STREAM, \
|
||||
STM32_UART4_RX_DMA_CHN)
|
||||
|
||||
#define UART4_TX_DMA_CHANNEL \
|
||||
STM32_DMA_GETCHANNEL(STM32_UART_UART4_TX_DMA_STREAM, \
|
||||
STM32_UART4_TX_DMA_CHN)
|
||||
|
||||
#define UART5_RX_DMA_CHANNEL \
|
||||
STM32_DMA_GETCHANNEL(STM32_UART_UART5_RX_DMA_STREAM, \
|
||||
STM32_UART5_RX_DMA_CHN)
|
||||
|
||||
#define UART5_TX_DMA_CHANNEL \
|
||||
STM32_DMA_GETCHANNEL(STM32_UART_UART5_TX_DMA_STREAM, \
|
||||
STM32_UART5_TX_DMA_CHN)
|
||||
|
||||
#define USART6_RX_DMA_CHANNEL \
|
||||
STM32_DMA_GETCHANNEL(STM32_UART_USART6_RX_DMA_STREAM, \
|
||||
STM32_USART6_RX_DMA_CHN)
|
||||
|
||||
#define USART6_TX_DMA_CHANNEL \
|
||||
STM32_DMA_GETCHANNEL(STM32_UART_USART6_TX_DMA_STREAM, \
|
||||
STM32_USART6_TX_DMA_CHN)
|
||||
|
||||
#define STM32_UART45_CR2_CHECK_MASK \
|
||||
(USART_CR2_STOP_0 | USART_CR2_CLKEN | USART_CR2_CPOL | USART_CR2_CPHA | \
|
||||
USART_CR2_LBCL)
|
||||
|
||||
#define STM32_UART45_CR3_CHECK_MASK \
|
||||
(USART_CR3_CTSIE | USART_CR3_CTSE | USART_CR3_RTSE | USART_CR3_SCEN | \
|
||||
USART_CR3_NACK)
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver exported variables. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/** @brief USART1 UART driver identifier.*/
|
||||
#if STM32_UART_USE_USART1 || defined(__DOXYGEN__)
|
||||
UARTDriver UARTD1;
|
||||
#endif
|
||||
|
||||
/** @brief USART2 UART driver identifier.*/
|
||||
#if STM32_UART_USE_USART2 || defined(__DOXYGEN__)
|
||||
UARTDriver UARTD2;
|
||||
#endif
|
||||
|
||||
/** @brief USART3 UART driver identifier.*/
|
||||
#if STM32_UART_USE_USART3 || defined(__DOXYGEN__)
|
||||
UARTDriver UARTD3;
|
||||
#endif
|
||||
|
||||
/** @brief UART4 UART driver identifier.*/
|
||||
#if STM32_UART_USE_UART4 || defined(__DOXYGEN__)
|
||||
UARTDriver UARTD4;
|
||||
#endif
|
||||
|
||||
/** @brief UART5 UART driver identifier.*/
|
||||
#if STM32_UART_USE_UART5 || defined(__DOXYGEN__)
|
||||
UARTDriver UARTD5;
|
||||
#endif
|
||||
|
||||
/** @brief USART6 UART driver identifier.*/
|
||||
#if STM32_UART_USE_USART6 || defined(__DOXYGEN__)
|
||||
UARTDriver UARTD6;
|
||||
#endif
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local variables and types. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Status bits translation.
|
||||
*
|
||||
* @param[in] sr USART SR register value
|
||||
*
|
||||
* @return The error flags.
|
||||
*/
|
||||
static uartflags_t translate_errors(uint16_t sr) {
|
||||
uartflags_t sts = 0;
|
||||
|
||||
if (sr & USART_SR_ORE)
|
||||
sts |= UART_OVERRUN_ERROR;
|
||||
if (sr & USART_SR_PE)
|
||||
sts |= UART_PARITY_ERROR;
|
||||
if (sr & USART_SR_FE)
|
||||
sts |= UART_FRAMING_ERROR;
|
||||
if (sr & USART_SR_NE)
|
||||
sts |= UART_NOISE_ERROR;
|
||||
if (sr & USART_SR_LBD)
|
||||
sts |= UART_BREAK_DETECTED;
|
||||
return sts;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Puts the receiver in the UART_RX_IDLE state.
|
||||
*
|
||||
* @param[in] uartp pointer to the @p UARTDriver object
|
||||
*/
|
||||
static void set_rx_idle_loop(UARTDriver *uartp) {
|
||||
uint32_t mode;
|
||||
|
||||
/* RX DMA channel preparation, if the char callback is defined then the
|
||||
TCIE interrupt is enabled too.*/
|
||||
if (uartp->config->rxchar_cb == NULL)
|
||||
mode = STM32_DMA_CR_DIR_P2M | STM32_DMA_CR_CIRC;
|
||||
else
|
||||
mode = STM32_DMA_CR_DIR_P2M | STM32_DMA_CR_CIRC | STM32_DMA_CR_TCIE;
|
||||
dmaStreamSetMemory0(uartp->dmarx, &uartp->rxbuf);
|
||||
dmaStreamSetTransactionSize(uartp->dmarx, 1);
|
||||
dmaStreamSetMode(uartp->dmarx, uartp->dmamode | mode);
|
||||
dmaStreamEnable(uartp->dmarx);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief USART de-initialization.
|
||||
* @details This function must be invoked with interrupts disabled.
|
||||
*
|
||||
* @param[in] uartp pointer to the @p UARTDriver object
|
||||
*/
|
||||
static void usart_stop(UARTDriver *uartp) {
|
||||
|
||||
/* Stops RX and TX DMA channels.*/
|
||||
dmaStreamDisable(uartp->dmarx);
|
||||
dmaStreamDisable(uartp->dmatx);
|
||||
|
||||
/* Stops USART operations.*/
|
||||
uartp->usart->CR1 = 0;
|
||||
uartp->usart->CR2 = 0;
|
||||
uartp->usart->CR3 = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief USART initialization.
|
||||
* @details This function must be invoked with interrupts disabled.
|
||||
*
|
||||
* @param[in] uartp pointer to the @p UARTDriver object
|
||||
*/
|
||||
static void usart_start(UARTDriver *uartp) {
|
||||
uint16_t cr1;
|
||||
USART_TypeDef *u = uartp->usart;
|
||||
|
||||
/* Defensive programming, starting from a clean state.*/
|
||||
usart_stop(uartp);
|
||||
|
||||
/* Baud rate setting.*/
|
||||
#if STM32_HAS_USART6
|
||||
if ((uartp->usart == USART1) || (uartp->usart == USART6))
|
||||
#else
|
||||
if (uartp->usart == USART1)
|
||||
#endif
|
||||
u->BRR = STM32_PCLK2 / uartp->config->speed;
|
||||
else
|
||||
u->BRR = STM32_PCLK1 / uartp->config->speed;
|
||||
|
||||
/* Resetting eventual pending status flags.*/
|
||||
(void)u->SR; /* SR reset step 1.*/
|
||||
(void)u->DR; /* SR reset step 2.*/
|
||||
u->SR = 0;
|
||||
|
||||
/* Note that some bits are enforced because required for correct driver
|
||||
operations.*/
|
||||
u->CR2 = uartp->config->cr2 | USART_CR2_LBDIE;
|
||||
u->CR3 = uartp->config->cr3 | USART_CR3_DMAT | USART_CR3_DMAR |
|
||||
USART_CR3_EIE;
|
||||
|
||||
/* Mustn't ever set TCIE here - if done, it causes an immediate
|
||||
interrupt.*/
|
||||
cr1 = USART_CR1_UE | USART_CR1_PEIE | USART_CR1_TE | USART_CR1_RE;
|
||||
u->CR1 = uartp->config->cr1 | cr1;
|
||||
|
||||
/* Starting the receiver idle loop.*/
|
||||
set_rx_idle_loop(uartp);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief RX DMA common service routine.
|
||||
*
|
||||
* @param[in] uartp pointer to the @p UARTDriver object
|
||||
* @param[in] flags pre-shifted content of the ISR register
|
||||
*/
|
||||
static void uart_lld_serve_rx_end_irq(UARTDriver *uartp, uint32_t flags) {
|
||||
|
||||
/* DMA errors handling.*/
|
||||
#if defined(STM32_UART_DMA_ERROR_HOOK)
|
||||
if ((flags & (STM32_DMA_ISR_TEIF | STM32_DMA_ISR_DMEIF)) != 0) {
|
||||
STM32_UART_DMA_ERROR_HOOK(uartp);
|
||||
}
|
||||
#else
|
||||
(void)flags;
|
||||
#endif
|
||||
|
||||
if (uartp->rxstate == UART_RX_IDLE) {
|
||||
/* Receiver in idle state, a callback is generated, if enabled, for each
|
||||
received character and then the driver stays in the same state.*/
|
||||
if (uartp->config->rxchar_cb != NULL)
|
||||
uartp->config->rxchar_cb(uartp, uartp->rxbuf);
|
||||
}
|
||||
else {
|
||||
/* Receiver in active state, a callback is generated, if enabled, after
|
||||
a completed transfer.*/
|
||||
dmaStreamDisable(uartp->dmarx);
|
||||
uartp->rxstate = UART_RX_COMPLETE;
|
||||
if (uartp->config->rxend_cb != NULL)
|
||||
uartp->config->rxend_cb(uartp);
|
||||
|
||||
/* If the callback didn't explicitly change state then the receiver
|
||||
automatically returns to the idle state.*/
|
||||
if (uartp->rxstate == UART_RX_COMPLETE) {
|
||||
uartp->rxstate = UART_RX_IDLE;
|
||||
set_rx_idle_loop(uartp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief TX DMA common service routine.
|
||||
*
|
||||
* @param[in] uartp pointer to the @p UARTDriver object
|
||||
* @param[in] flags pre-shifted content of the ISR register
|
||||
*/
|
||||
static void uart_lld_serve_tx_end_irq(UARTDriver *uartp, uint32_t flags) {
|
||||
|
||||
/* DMA errors handling.*/
|
||||
#if defined(STM32_UART_DMA_ERROR_HOOK)
|
||||
if ((flags & (STM32_DMA_ISR_TEIF | STM32_DMA_ISR_DMEIF)) != 0) {
|
||||
STM32_UART_DMA_ERROR_HOOK(uartp);
|
||||
}
|
||||
#else
|
||||
(void)flags;
|
||||
#endif
|
||||
|
||||
dmaStreamDisable(uartp->dmatx);
|
||||
|
||||
/* Only enable TC interrupt if there's a callback attached to it.
|
||||
We have to do it here, rather than earlier, because TC flag is set
|
||||
until transmission starts.*/
|
||||
if (uartp->config->txend2_cb != NULL)
|
||||
uartp->usart->CR1 |= USART_CR1_TCIE;
|
||||
|
||||
/* A callback is generated, if enabled, after a completed transfer.*/
|
||||
uartp->txstate = UART_TX_COMPLETE;
|
||||
if (uartp->config->txend1_cb != NULL)
|
||||
uartp->config->txend1_cb(uartp);
|
||||
|
||||
/* If the callback didn't explicitly change state then the transmitter
|
||||
automatically returns to the idle state.*/
|
||||
if (uartp->txstate == UART_TX_COMPLETE)
|
||||
uartp->txstate = UART_TX_IDLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief USART common service routine.
|
||||
*
|
||||
* @param[in] uartp pointer to the @p UARTDriver object
|
||||
*/
|
||||
static void serve_usart_irq(UARTDriver *uartp) {
|
||||
uint16_t sr;
|
||||
USART_TypeDef *u = uartp->usart;
|
||||
uint32_t cr1 = u->CR1;
|
||||
|
||||
sr = u->SR; /* SR reset step 1.*/
|
||||
(void)u->DR; /* SR reset step 2.*/
|
||||
|
||||
if (sr & (USART_SR_LBD | USART_SR_ORE | USART_SR_NE |
|
||||
USART_SR_FE | USART_SR_PE)) {
|
||||
u->SR = ~USART_SR_LBD;
|
||||
if (uartp->config->rxerr_cb != NULL)
|
||||
uartp->config->rxerr_cb(uartp, translate_errors(sr));
|
||||
}
|
||||
|
||||
if ((sr & USART_SR_TC) && (cr1 & USART_CR1_TCIE)) {
|
||||
/* TC interrupt cleared and disabled.*/
|
||||
u->SR = ~USART_SR_TC;
|
||||
u->CR1 = cr1 & ~USART_CR1_TCIE;
|
||||
|
||||
/* End of transmission, a callback is generated.*/
|
||||
if (uartp->config->txend2_cb != NULL)
|
||||
uartp->config->txend2_cb(uartp);
|
||||
}
|
||||
}
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver interrupt handlers. */
|
||||
/*===========================================================================*/
|
||||
|
||||
#if STM32_UART_USE_USART1 || defined(__DOXYGEN__)
|
||||
#if !defined(STM32_USART1_HANDLER)
|
||||
#error "STM32_USART1_HANDLER not defined"
|
||||
#endif
|
||||
/**
|
||||
* @brief USART1 IRQ handler.
|
||||
*
|
||||
* @isr
|
||||
*/
|
||||
CH_IRQ_HANDLER(STM32_USART1_HANDLER) {
|
||||
|
||||
CH_IRQ_PROLOGUE();
|
||||
|
||||
serve_usart_irq(&UARTD1);
|
||||
|
||||
CH_IRQ_EPILOGUE();
|
||||
}
|
||||
#endif /* STM32_UART_USE_USART1 */
|
||||
|
||||
#if STM32_UART_USE_USART2 || defined(__DOXYGEN__)
|
||||
#if !defined(STM32_USART2_HANDLER)
|
||||
#error "STM32_USART2_HANDLER not defined"
|
||||
#endif
|
||||
/**
|
||||
* @brief USART2 IRQ handler.
|
||||
*
|
||||
* @isr
|
||||
*/
|
||||
CH_IRQ_HANDLER(STM32_USART2_HANDLER) {
|
||||
|
||||
CH_IRQ_PROLOGUE();
|
||||
|
||||
serve_usart_irq(&UARTD2);
|
||||
|
||||
CH_IRQ_EPILOGUE();
|
||||
}
|
||||
#endif /* STM32_UART_USE_USART2 */
|
||||
|
||||
#if STM32_UART_USE_USART3 || defined(__DOXYGEN__)
|
||||
#if !defined(STM32_USART3_HANDLER)
|
||||
#error "STM32_USART3_HANDLER not defined"
|
||||
#endif
|
||||
/**
|
||||
* @brief USART3 IRQ handler.
|
||||
*
|
||||
* @isr
|
||||
*/
|
||||
CH_IRQ_HANDLER(STM32_USART3_HANDLER) {
|
||||
|
||||
CH_IRQ_PROLOGUE();
|
||||
|
||||
serve_usart_irq(&UARTD3);
|
||||
|
||||
CH_IRQ_EPILOGUE();
|
||||
}
|
||||
#endif /* STM32_UART_USE_USART3 */
|
||||
|
||||
#if STM32_UART_USE_UART4 || defined(__DOXYGEN__)
|
||||
#if !defined(STM32_UART4_HANDLER)
|
||||
#error "STM32_UART4_HANDLER not defined"
|
||||
#endif
|
||||
/**
|
||||
* @brief UART4 IRQ handler.
|
||||
*
|
||||
* @isr
|
||||
*/
|
||||
CH_IRQ_HANDLER(STM32_UART4_HANDLER) {
|
||||
|
||||
CH_IRQ_PROLOGUE();
|
||||
|
||||
serve_usart_irq(&UARTD4);
|
||||
|
||||
CH_IRQ_EPILOGUE();
|
||||
}
|
||||
#endif /* STM32_UART_USE_UART4 */
|
||||
|
||||
#if STM32_UART_USE_UART5 || defined(__DOXYGEN__)
|
||||
#if !defined(STM32_UART5_HANDLER)
|
||||
#error "STM32_UART5_HANDLER not defined"
|
||||
#endif
|
||||
/**
|
||||
* @brief UART5 IRQ handler.
|
||||
*
|
||||
* @isr
|
||||
*/
|
||||
CH_IRQ_HANDLER(STM32_UART5_HANDLER) {
|
||||
|
||||
CH_IRQ_PROLOGUE();
|
||||
|
||||
serve_usart_irq(&UARTD5);
|
||||
|
||||
CH_IRQ_EPILOGUE();
|
||||
}
|
||||
#endif /* STM32_UART_USE_UART5 */
|
||||
|
||||
#if STM32_UART_USE_USART6 || defined(__DOXYGEN__)
|
||||
#if !defined(STM32_USART6_HANDLER)
|
||||
#error "STM32_USART6_HANDLER not defined"
|
||||
#endif
|
||||
/**
|
||||
* @brief USART6 IRQ handler.
|
||||
*
|
||||
* @isr
|
||||
*/
|
||||
CH_IRQ_HANDLER(STM32_USART6_HANDLER) {
|
||||
|
||||
CH_IRQ_PROLOGUE();
|
||||
|
||||
serve_usart_irq(&UARTD6);
|
||||
|
||||
CH_IRQ_EPILOGUE();
|
||||
}
|
||||
#endif /* STM32_UART_USE_USART6 */
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver exported functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Low level UART driver initialization.
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void uart_lld_init(void) {
|
||||
|
||||
#if STM32_UART_USE_USART1
|
||||
uartObjectInit(&UARTD1);
|
||||
UARTD1.usart = USART1;
|
||||
UARTD1.dmamode = STM32_DMA_CR_DMEIE | STM32_DMA_CR_TEIE;
|
||||
UARTD1.dmarx = STM32_DMA_STREAM(STM32_UART_USART1_RX_DMA_STREAM);
|
||||
UARTD1.dmatx = STM32_DMA_STREAM(STM32_UART_USART1_TX_DMA_STREAM);
|
||||
#endif
|
||||
|
||||
#if STM32_UART_USE_USART2
|
||||
uartObjectInit(&UARTD2);
|
||||
UARTD2.usart = USART2;
|
||||
UARTD2.dmamode = STM32_DMA_CR_DMEIE | STM32_DMA_CR_TEIE;
|
||||
UARTD2.dmarx = STM32_DMA_STREAM(STM32_UART_USART2_RX_DMA_STREAM);
|
||||
UARTD2.dmatx = STM32_DMA_STREAM(STM32_UART_USART2_TX_DMA_STREAM);
|
||||
#endif
|
||||
|
||||
#if STM32_UART_USE_USART3
|
||||
uartObjectInit(&UARTD3);
|
||||
UARTD3.usart = USART3;
|
||||
UARTD3.dmamode = STM32_DMA_CR_DMEIE | STM32_DMA_CR_TEIE;
|
||||
UARTD3.dmarx = STM32_DMA_STREAM(STM32_UART_USART3_RX_DMA_STREAM);
|
||||
UARTD3.dmatx = STM32_DMA_STREAM(STM32_UART_USART3_TX_DMA_STREAM);
|
||||
#endif
|
||||
|
||||
#if STM32_UART_USE_UART4
|
||||
uartObjectInit(&UARTD4);
|
||||
UARTD4.usart = UART4;
|
||||
UARTD4.dmamode = STM32_DMA_CR_DMEIE | STM32_DMA_CR_TEIE;
|
||||
UARTD4.dmarx = STM32_DMA_STREAM(STM32_UART_UART4_RX_DMA_STREAM);
|
||||
UARTD4.dmatx = STM32_DMA_STREAM(STM32_UART_UART4_TX_DMA_STREAM);
|
||||
#endif
|
||||
|
||||
#if STM32_UART_USE_UART5
|
||||
uartObjectInit(&UARTD5);
|
||||
UARTD5.usart = UART5;
|
||||
UARTD5.dmamode = STM32_DMA_CR_DMEIE | STM32_DMA_CR_TEIE;
|
||||
UARTD5.dmarx = STM32_DMA_STREAM(STM32_UART_UART5_RX_DMA_STREAM);
|
||||
UARTD5.dmatx = STM32_DMA_STREAM(STM32_UART_UART5_TX_DMA_STREAM);
|
||||
#endif
|
||||
|
||||
#if STM32_UART_USE_USART6
|
||||
uartObjectInit(&UARTD6);
|
||||
UARTD6.usart = USART6;
|
||||
UARTD6.dmarx = STM32_DMA_STREAM(STM32_UART_USART6_RX_DMA_STREAM);
|
||||
UARTD6.dmatx = STM32_DMA_STREAM(STM32_UART_USART6_TX_DMA_STREAM);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Configures and activates the UART peripheral.
|
||||
*
|
||||
* @param[in] uartp pointer to the @p UARTDriver object
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void uart_lld_start(UARTDriver *uartp) {
|
||||
|
||||
if (uartp->state == UART_STOP) {
|
||||
#if STM32_UART_USE_USART1
|
||||
if (&UARTD1 == uartp) {
|
||||
bool_t b;
|
||||
b = dmaStreamAllocate(uartp->dmarx,
|
||||
STM32_UART_USART1_IRQ_PRIORITY,
|
||||
(stm32_dmaisr_t)uart_lld_serve_rx_end_irq,
|
||||
(void *)uartp);
|
||||
chDbgAssert(!b, "uart_lld_start(), #1", "stream already allocated");
|
||||
b = dmaStreamAllocate(uartp->dmatx,
|
||||
STM32_UART_USART1_IRQ_PRIORITY,
|
||||
(stm32_dmaisr_t)uart_lld_serve_tx_end_irq,
|
||||
(void *)uartp);
|
||||
chDbgAssert(!b, "uart_lld_start(), #2", "stream already allocated");
|
||||
rccEnableUSART1(FALSE);
|
||||
nvicEnableVector(STM32_USART1_NUMBER,
|
||||
CORTEX_PRIORITY_MASK(STM32_UART_USART1_IRQ_PRIORITY));
|
||||
uartp->dmamode |= STM32_DMA_CR_CHSEL(USART1_RX_DMA_CHANNEL) |
|
||||
STM32_DMA_CR_PL(STM32_UART_USART1_DMA_PRIORITY);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if STM32_UART_USE_USART2
|
||||
if (&UARTD2 == uartp) {
|
||||
bool_t b;
|
||||
b = dmaStreamAllocate(uartp->dmarx,
|
||||
STM32_UART_USART2_IRQ_PRIORITY,
|
||||
(stm32_dmaisr_t)uart_lld_serve_rx_end_irq,
|
||||
(void *)uartp);
|
||||
chDbgAssert(!b, "uart_lld_start(), #3", "stream already allocated");
|
||||
b = dmaStreamAllocate(uartp->dmatx,
|
||||
STM32_UART_USART2_IRQ_PRIORITY,
|
||||
(stm32_dmaisr_t)uart_lld_serve_tx_end_irq,
|
||||
(void *)uartp);
|
||||
chDbgAssert(!b, "uart_lld_start(), #4", "stream already allocated");
|
||||
rccEnableUSART2(FALSE);
|
||||
nvicEnableVector(STM32_USART2_NUMBER,
|
||||
CORTEX_PRIORITY_MASK(STM32_UART_USART2_IRQ_PRIORITY));
|
||||
uartp->dmamode |= STM32_DMA_CR_CHSEL(USART2_RX_DMA_CHANNEL) |
|
||||
STM32_DMA_CR_PL(STM32_UART_USART2_DMA_PRIORITY);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if STM32_UART_USE_USART3
|
||||
if (&UARTD3 == uartp) {
|
||||
bool_t b;
|
||||
b = dmaStreamAllocate(uartp->dmarx,
|
||||
STM32_UART_USART3_IRQ_PRIORITY,
|
||||
(stm32_dmaisr_t)uart_lld_serve_rx_end_irq,
|
||||
(void *)uartp);
|
||||
chDbgAssert(!b, "uart_lld_start(), #5", "stream already allocated");
|
||||
b = dmaStreamAllocate(uartp->dmatx,
|
||||
STM32_UART_USART3_IRQ_PRIORITY,
|
||||
(stm32_dmaisr_t)uart_lld_serve_tx_end_irq,
|
||||
(void *)uartp);
|
||||
chDbgAssert(!b, "uart_lld_start(), #6", "stream already allocated");
|
||||
rccEnableUSART3(FALSE);
|
||||
nvicEnableVector(STM32_USART3_NUMBER,
|
||||
CORTEX_PRIORITY_MASK(STM32_UART_USART3_IRQ_PRIORITY));
|
||||
uartp->dmamode |= STM32_DMA_CR_CHSEL(USART3_RX_DMA_CHANNEL) |
|
||||
STM32_DMA_CR_PL(STM32_UART_USART3_DMA_PRIORITY);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if STM32_UART_USE_UART4
|
||||
if (&UARTD4 == uartp) {
|
||||
bool_t b;
|
||||
|
||||
chDbgAssert((uartp->config->cr2 & STM32_UART45_CR2_CHECK_MASK) == 0,
|
||||
"uart_lld_start(), #7",
|
||||
"specified invalid bits in UART4 CR2 register settings");
|
||||
chDbgAssert((uartp->config->cr3 & STM32_UART45_CR3_CHECK_MASK) == 0,
|
||||
"uart_lld_start(), #8",
|
||||
"specified invalid bits in UART4 CR3 register settings");
|
||||
|
||||
b = dmaStreamAllocate(uartp->dmarx,
|
||||
STM32_UART_UART4_IRQ_PRIORITY,
|
||||
(stm32_dmaisr_t)uart_lld_serve_rx_end_irq,
|
||||
(void *)uartp);
|
||||
chDbgAssert(!b, "uart_lld_start(), #9", "stream already allocated");
|
||||
b = dmaStreamAllocate(uartp->dmatx,
|
||||
STM32_UART_UART4_IRQ_PRIORITY,
|
||||
(stm32_dmaisr_t)uart_lld_serve_tx_end_irq,
|
||||
(void *)uartp);
|
||||
chDbgAssert(!b, "uart_lld_start(), #10", "stream already allocated");
|
||||
rccEnableUART4(FALSE);
|
||||
nvicEnableVector(STM32_UART4_NUMBER,
|
||||
CORTEX_PRIORITY_MASK(STM32_UART_UART4_IRQ_PRIORITY));
|
||||
uartp->dmamode |= STM32_DMA_CR_CHSEL(UART4_RX_DMA_CHANNEL) |
|
||||
STM32_DMA_CR_PL(STM32_UART_UART4_DMA_PRIORITY);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if STM32_UART_USE_UART5
|
||||
if (&UARTD5 == uartp) {
|
||||
bool_t b;
|
||||
|
||||
chDbgAssert((uartp->config->cr2 & STM32_UART45_CR2_CHECK_MASK) == 0,
|
||||
"uart_lld_start(), #11",
|
||||
"specified invalid bits in UART5 CR2 register settings");
|
||||
chDbgAssert((uartp->config->cr3 & STM32_UART45_CR3_CHECK_MASK) == 0,
|
||||
"uart_lld_start(), #12",
|
||||
"specified invalid bits in UART5 CR3 register settings");
|
||||
|
||||
b = dmaStreamAllocate(uartp->dmarx,
|
||||
STM32_UART_UART5_IRQ_PRIORITY,
|
||||
(stm32_dmaisr_t)uart_lld_serve_rx_end_irq,
|
||||
(void *)uartp);
|
||||
chDbgAssert(!b, "uart_lld_start(), #13", "stream already allocated");
|
||||
b = dmaStreamAllocate(uartp->dmatx,
|
||||
STM32_UART_UART5_IRQ_PRIORITY,
|
||||
(stm32_dmaisr_t)uart_lld_serve_tx_end_irq,
|
||||
(void *)uartp);
|
||||
chDbgAssert(!b, "uart_lld_start(), #14", "stream already allocated");
|
||||
rccEnableUART5(FALSE);
|
||||
nvicEnableVector(STM32_UART5_NUMBER,
|
||||
CORTEX_PRIORITY_MASK(STM32_UART_UART5_IRQ_PRIORITY));
|
||||
uartp->dmamode |= STM32_DMA_CR_CHSEL(UART5_RX_DMA_CHANNEL) |
|
||||
STM32_DMA_CR_PL(STM32_UART_UART5_DMA_PRIORITY);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if STM32_UART_USE_USART6
|
||||
if (&UARTD6 == uartp) {
|
||||
bool_t b;
|
||||
b = dmaStreamAllocate(uartp->dmarx,
|
||||
STM32_UART_USART6_IRQ_PRIORITY,
|
||||
(stm32_dmaisr_t)uart_lld_serve_rx_end_irq,
|
||||
(void *)uartp);
|
||||
chDbgAssert(!b, "uart_lld_start(), #15", "stream already allocated");
|
||||
b = dmaStreamAllocate(uartp->dmatx,
|
||||
STM32_UART_USART6_IRQ_PRIORITY,
|
||||
(stm32_dmaisr_t)uart_lld_serve_tx_end_irq,
|
||||
(void *)uartp);
|
||||
chDbgAssert(!b, "uart_lld_start(), #16", "stream already allocated");
|
||||
rccEnableUSART6(FALSE);
|
||||
nvicEnableVector(STM32_USART6_NUMBER,
|
||||
CORTEX_PRIORITY_MASK(STM32_UART_USART6_IRQ_PRIORITY));
|
||||
uartp->dmamode |= STM32_DMA_CR_CHSEL(USART6_RX_DMA_CHANNEL) |
|
||||
STM32_DMA_CR_PL(STM32_UART_USART6_DMA_PRIORITY);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Static DMA setup, the transfer size depends on the USART settings,
|
||||
it is 16 bits if M=1 and PCE=0 else it is 8 bits.*/
|
||||
if ((uartp->config->cr1 & (USART_CR1_M | USART_CR1_PCE)) == USART_CR1_M)
|
||||
uartp->dmamode |= STM32_DMA_CR_PSIZE_HWORD | STM32_DMA_CR_MSIZE_HWORD;
|
||||
dmaStreamSetPeripheral(uartp->dmarx, &uartp->usart->DR);
|
||||
dmaStreamSetPeripheral(uartp->dmatx, &uartp->usart->DR);
|
||||
uartp->rxbuf = 0;
|
||||
}
|
||||
|
||||
uartp->rxstate = UART_RX_IDLE;
|
||||
uartp->txstate = UART_TX_IDLE;
|
||||
usart_start(uartp);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Deactivates the UART peripheral.
|
||||
*
|
||||
* @param[in] uartp pointer to the @p UARTDriver object
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void uart_lld_stop(UARTDriver *uartp) {
|
||||
|
||||
if (uartp->state == UART_READY) {
|
||||
usart_stop(uartp);
|
||||
dmaStreamRelease(uartp->dmarx);
|
||||
dmaStreamRelease(uartp->dmatx);
|
||||
|
||||
#if STM32_UART_USE_USART1
|
||||
if (&UARTD1 == uartp) {
|
||||
nvicDisableVector(STM32_USART1_NUMBER);
|
||||
rccDisableUSART1(FALSE);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if STM32_UART_USE_USART2
|
||||
if (&UARTD2 == uartp) {
|
||||
nvicDisableVector(STM32_USART2_NUMBER);
|
||||
rccDisableUSART2(FALSE);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if STM32_UART_USE_USART3
|
||||
if (&UARTD3 == uartp) {
|
||||
nvicDisableVector(STM32_USART3_NUMBER);
|
||||
rccDisableUSART3(FALSE);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if STM32_UART_USE_UART4
|
||||
if (&UARTD4 == uartp) {
|
||||
nvicDisableVector(STM32_UART4_NUMBER);
|
||||
rccDisableUART4(FALSE);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if STM32_UART_USE_UART5
|
||||
if (&UARTD5 == uartp) {
|
||||
nvicDisableVector(STM32_UART5_NUMBER);
|
||||
rccDisableUART5(FALSE);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if STM32_UART_USE_USART6
|
||||
if (&UARTD6 == uartp) {
|
||||
nvicDisableVector(STM32_USART6_NUMBER);
|
||||
rccDisableUSART6(FALSE);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Starts a transmission on the UART peripheral.
|
||||
* @note The buffers are organized as uint8_t arrays for data sizes below
|
||||
* or equal to 8 bits else it is organized as uint16_t arrays.
|
||||
*
|
||||
* @param[in] uartp pointer to the @p UARTDriver object
|
||||
* @param[in] n number of data frames to send
|
||||
* @param[in] txbuf the pointer to the transmit buffer
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void uart_lld_start_send(UARTDriver *uartp, size_t n, const void *txbuf) {
|
||||
|
||||
/* TX DMA channel preparation and start.*/
|
||||
dmaStreamSetMemory0(uartp->dmatx, txbuf);
|
||||
dmaStreamSetTransactionSize(uartp->dmatx, n);
|
||||
dmaStreamSetMode(uartp->dmatx, uartp->dmamode | STM32_DMA_CR_DIR_M2P |
|
||||
STM32_DMA_CR_MINC | STM32_DMA_CR_TCIE);
|
||||
dmaStreamEnable(uartp->dmatx);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Stops any ongoing transmission.
|
||||
* @note Stopping a transmission also suppresses the transmission callbacks.
|
||||
*
|
||||
* @param[in] uartp pointer to the @p UARTDriver object
|
||||
*
|
||||
* @return The number of data frames not transmitted by the
|
||||
* stopped transmit operation.
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
size_t uart_lld_stop_send(UARTDriver *uartp) {
|
||||
|
||||
dmaStreamDisable(uartp->dmatx);
|
||||
return dmaStreamGetTransactionSize(uartp->dmatx);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Starts a receive operation on the UART peripheral.
|
||||
* @note The buffers are organized as uint8_t arrays for data sizes below
|
||||
* or equal to 8 bits else it is organized as uint16_t arrays.
|
||||
*
|
||||
* @param[in] uartp pointer to the @p UARTDriver object
|
||||
* @param[in] n number of data frames to send
|
||||
* @param[out] rxbuf the pointer to the receive buffer
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void uart_lld_start_receive(UARTDriver *uartp, size_t n, void *rxbuf) {
|
||||
|
||||
/* Stopping previous activity (idle state).*/
|
||||
dmaStreamDisable(uartp->dmarx);
|
||||
|
||||
/* RX DMA channel preparation and start.*/
|
||||
dmaStreamSetMemory0(uartp->dmarx, rxbuf);
|
||||
dmaStreamSetTransactionSize(uartp->dmarx, n);
|
||||
dmaStreamSetMode(uartp->dmarx, uartp->dmamode | STM32_DMA_CR_DIR_P2M |
|
||||
STM32_DMA_CR_MINC | STM32_DMA_CR_TCIE);
|
||||
dmaStreamEnable(uartp->dmarx);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Stops any ongoing receive operation.
|
||||
* @note Stopping a receive operation also suppresses the receive callbacks.
|
||||
*
|
||||
* @param[in] uartp pointer to the @p UARTDriver object
|
||||
*
|
||||
* @return The number of data frames not received by the
|
||||
* stopped receive operation.
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
size_t uart_lld_stop_receive(UARTDriver *uartp) {
|
||||
size_t n;
|
||||
|
||||
dmaStreamDisable(uartp->dmarx);
|
||||
n = dmaStreamGetTransactionSize(uartp->dmarx);
|
||||
set_rx_idle_loop(uartp);
|
||||
return n;
|
||||
}
|
||||
|
||||
#endif /* HAL_USE_UART */
|
||||
|
||||
/** @} */
|
||||
+680
@@ -0,0 +1,680 @@
|
||||
/*
|
||||
ChibiOS/RT - Copyright (C) 2006-2013 Giovanni Di Sirio
|
||||
|
||||
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 STM32/USARTv1/uart_lld.h
|
||||
* @brief STM32 low level UART driver header.
|
||||
*
|
||||
* @addtogroup UART
|
||||
* @{
|
||||
*/
|
||||
|
||||
#ifndef _UART_LLD_H_
|
||||
#define _UART_LLD_H_
|
||||
|
||||
#if HAL_USE_UART || defined(__DOXYGEN__)
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver constants. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver pre-compile time settings. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @name Configuration options
|
||||
* @{
|
||||
*/
|
||||
/**
|
||||
* @brief UART driver on USART1 enable switch.
|
||||
* @details If set to @p TRUE the support for USART1 is included.
|
||||
* @note The default is @p FALSE.
|
||||
*/
|
||||
#if !defined(STM32_UART_USE_USART1) || defined(__DOXYGEN__)
|
||||
#define STM32_UART_USE_USART1 FALSE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief UART driver on USART2 enable switch.
|
||||
* @details If set to @p TRUE the support for USART2 is included.
|
||||
* @note The default is @p FALSE.
|
||||
*/
|
||||
#if !defined(STM32_UART_USE_USART2) || defined(__DOXYGEN__)
|
||||
#define STM32_UART_USE_USART2 FALSE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief UART driver on USART3 enable switch.
|
||||
* @details If set to @p TRUE the support for USART3 is included.
|
||||
* @note The default is @p FALSE.
|
||||
*/
|
||||
#if !defined(STM32_UART_USE_USART3) || defined(__DOXYGEN__)
|
||||
#define STM32_UART_USE_USART3 FALSE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief UART driver on UART4 enable switch.
|
||||
* @details If set to @p TRUE the support for UART4 is included.
|
||||
* @note The default is @p FALSE.
|
||||
*/
|
||||
#if !defined(STM32_UART_USE_UART4) || defined(__DOXYGEN__)
|
||||
#define STM32_UART_USE_UART4 FALSE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief UART driver on UART5 enable switch.
|
||||
* @details If set to @p TRUE the support for UART5 is included.
|
||||
* @note The default is @p FALSE.
|
||||
*/
|
||||
#if !defined(STM32_UART_USE_UART5) || defined(__DOXYGEN__)
|
||||
#define STM32_UART_USE_UART5 FALSE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief UART driver on USART6 enable switch.
|
||||
* @details If set to @p TRUE the support for USART6 is included.
|
||||
* @note The default is @p FALSE.
|
||||
*/
|
||||
#if !defined(STM32_UART_USE_USART6) || defined(__DOXYGEN__)
|
||||
#define STM32_UART_USE_USART6 FALSE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief USART1 interrupt priority level setting.
|
||||
*/
|
||||
#if !defined(STM32_UART_USART1_IRQ_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define STM32_UART_USART1_IRQ_PRIORITY 12
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief USART2 interrupt priority level setting.
|
||||
*/
|
||||
#if !defined(STM32_UART_USART2_IRQ_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define STM32_UART_USART2_IRQ_PRIORITY 12
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief USART3 interrupt priority level setting.
|
||||
*/
|
||||
#if !defined(STM32_UART_USART3_IRQ_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define STM32_UART_USART3_IRQ_PRIORITY 12
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief UART4 interrupt priority level setting.
|
||||
*/
|
||||
#if !defined(STM32_UART_UART4_IRQ_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define STM32_UART_UART4_IRQ_PRIORITY 12
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief UART5 interrupt priority level setting.
|
||||
*/
|
||||
#if !defined(STM32_UART_UART5_IRQ_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define STM32_UART_UART5_IRQ_PRIORITY 12
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief USART6 interrupt priority level setting.
|
||||
*/
|
||||
#if !defined(STM32_UART_USART6_IRQ_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define STM32_UART_USART6_IRQ_PRIORITY 12
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief USART1 DMA priority (0..3|lowest..highest).
|
||||
* @note The priority level is used for both the TX and RX DMA channels but
|
||||
* because of the channels ordering the RX channel has always priority
|
||||
* over the TX channel.
|
||||
*/
|
||||
#if !defined(STM32_UART_USART1_DMA_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define STM32_UART_USART1_DMA_PRIORITY 0
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief USART2 DMA priority (0..3|lowest..highest).
|
||||
* @note The priority level is used for both the TX and RX DMA channels but
|
||||
* because of the channels ordering the RX channel has always priority
|
||||
* over the TX channel.
|
||||
*/
|
||||
#if !defined(STM32_UART_USART2_DMA_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define STM32_UART_USART2_DMA_PRIORITY 0
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief USART3 DMA priority (0..3|lowest..highest).
|
||||
* @note The priority level is used for both the TX and RX DMA channels but
|
||||
* because of the channels ordering the RX channel has always priority
|
||||
* over the TX channel.
|
||||
*/
|
||||
#if !defined(STM32_UART_USART3_DMA_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define STM32_UART_USART3_DMA_PRIORITY 0
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief UART4 DMA priority (0..3|lowest..highest).
|
||||
* @note The priority level is used for both the TX and RX DMA channels but
|
||||
* because of the channels ordering the RX channel has always priority
|
||||
* over the TX channel.
|
||||
*/
|
||||
#if !defined(STM32_UART_UART4_DMA_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define STM32_UART_UART4_DMA_PRIORITY 0
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief UART5 DMA priority (0..3|lowest..highest).
|
||||
* @note The priority level is used for both the TX and RX DMA channels but
|
||||
* because of the channels ordering the RX channel has always priority
|
||||
* over the TX channel.
|
||||
*/
|
||||
#if !defined(STM32_UART_UART5_DMA_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define STM32_UART_UART5_DMA_PRIORITY 0
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief USART6 DMA priority (0..3|lowest..highest).
|
||||
* @note The priority level is used for both the TX and RX DMA channels but
|
||||
* because of the channels ordering the RX channel has always priority
|
||||
* over the TX channel.
|
||||
*/
|
||||
#if !defined(STM32_UART_USART6_DMA_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define STM32_UART_USART6_DMA_PRIORITY 0
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief USART DMA error hook.
|
||||
* @note The default action for DMA errors is a system halt because DMA
|
||||
* error can only happen because programming errors.
|
||||
*/
|
||||
#if !defined(STM32_UART_DMA_ERROR_HOOK) || defined(__DOXYGEN__)
|
||||
#define STM32_UART_DMA_ERROR_HOOK(uartp) chSysHalt()
|
||||
#endif
|
||||
|
||||
#if STM32_ADVANCED_DMA || defined(__DOXYGEN__)
|
||||
|
||||
/**
|
||||
* @brief DMA stream used for USART1 RX operations.
|
||||
* @note This option is only available on platforms with enhanced DMA.
|
||||
*/
|
||||
#if !defined(STM32_UART_USART1_RX_DMA_STREAM) || defined(__DOXYGEN__)
|
||||
#define STM32_UART_USART1_RX_DMA_STREAM STM32_DMA_STREAM_ID(2, 5)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief DMA stream used for USART1 TX operations.
|
||||
* @note This option is only available on platforms with enhanced DMA.
|
||||
*/
|
||||
#if !defined(STM32_UART_USART1_TX_DMA_STREAM) || defined(__DOXYGEN__)
|
||||
#define STM32_UART_USART1_TX_DMA_STREAM STM32_DMA_STREAM_ID(2, 7)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief DMA stream used for USART2 RX operations.
|
||||
* @note This option is only available on platforms with enhanced DMA.
|
||||
*/
|
||||
#if !defined(STM32_UART_USART2_RX_DMA_STREAM) || defined(__DOXYGEN__)
|
||||
#define STM32_UART_USART2_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 5)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief DMA stream used for USART2 TX operations.
|
||||
* @note This option is only available on platforms with enhanced DMA.
|
||||
*/
|
||||
#if !defined(STM32_UART_USART2_TX_DMA_STREAM) || defined(__DOXYGEN__)
|
||||
#define STM32_UART_USART2_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 6)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief DMA stream used for USART3 RX operations.
|
||||
* @note This option is only available on platforms with enhanced DMA.
|
||||
*/
|
||||
#if !defined(STM32_UART_USART3_RX_DMA_STREAM) || defined(__DOXYGEN__)
|
||||
#define STM32_UART_USART3_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 1)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief DMA stream used for USART3 TX operations.
|
||||
* @note This option is only available on platforms with enhanced DMA.
|
||||
*/
|
||||
#if !defined(STM32_UART_USART3_TX_DMA_STREAM) || defined(__DOXYGEN__)
|
||||
#define STM32_UART_USART3_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 3)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief DMA stream used for UART4 RX operations.
|
||||
* @note This option is only available on platforms with enhanced DMA.
|
||||
*/
|
||||
#if !defined(STM32_UART_UART4_RX_DMA_STREAM) || defined(__DOXYGEN__)
|
||||
#define STM32_UART_UART4_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 2)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief DMA stream used for UART4 TX operations.
|
||||
* @note This option is only available on platforms with enhanced DMA.
|
||||
*/
|
||||
#if !defined(STM32_UART_UART4_TX_DMA_STREAM) || defined(__DOXYGEN__)
|
||||
#define STM32_UART_UART4_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 4)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief DMA stream used for UART5 RX operations.
|
||||
* @note This option is only available on platforms with enhanced DMA.
|
||||
*/
|
||||
#if !defined(STM32_UART_UART5_RX_DMA_STREAM) || defined(__DOXYGEN__)
|
||||
#define STM32_UART_UART5_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 0)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief DMA stream used for UART5 TX operations.
|
||||
* @note This option is only available on platforms with enhanced DMA.
|
||||
*/
|
||||
#if !defined(STM32_UART_UART5_TX_DMA_STREAM) || defined(__DOXYGEN__)
|
||||
#define STM32_UART_UART5_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 7)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief DMA stream used for USART6 RX operations.
|
||||
* @note This option is only available on platforms with enhanced DMA.
|
||||
*/
|
||||
#if !defined(STM32_UART_USART6_RX_DMA_STREAM) || defined(__DOXYGEN__)
|
||||
#define STM32_UART_USART6_RX_DMA_STREAM STM32_DMA_STREAM_ID(2, 2)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief DMA stream used for USART6 TX operations.
|
||||
* @note This option is only available on platforms with enhanced DMA.
|
||||
*/
|
||||
#if !defined(STM32_UART_USART6_TX_DMA_STREAM) || defined(__DOXYGEN__)
|
||||
#define STM32_UART_USART6_TX_DMA_STREAM STM32_DMA_STREAM_ID(2, 7)
|
||||
#endif
|
||||
|
||||
#else /* !STM32_ADVANCED_DMA */
|
||||
|
||||
/* Fixed streams for platforms using the old DMA peripheral, the values are
|
||||
valid for both STM32F1xx and STM32L1xx.*/
|
||||
#define STM32_UART_USART1_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 5)
|
||||
#define STM32_UART_USART1_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 4)
|
||||
#define STM32_UART_USART2_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 6)
|
||||
#define STM32_UART_USART2_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 7)
|
||||
#define STM32_UART_USART3_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 3)
|
||||
#define STM32_UART_USART3_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 2)
|
||||
#define STM32_UART_UART4_RX_DMA_STREAM STM32_DMA_STREAM_ID(2, 3)
|
||||
#define STM32_UART_UART4_TX_DMA_STREAM STM32_DMA_STREAM_ID(2, 5)
|
||||
|
||||
#endif /* !STM32_ADVANCED_DMA*/
|
||||
/** @} */
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Derived constants and error checks. */
|
||||
/*===========================================================================*/
|
||||
|
||||
#if STM32_UART_USE_USART1 && !STM32_HAS_USART1
|
||||
#error "USART1 not present in the selected device"
|
||||
#endif
|
||||
|
||||
#if STM32_UART_USE_USART2 && !STM32_HAS_USART2
|
||||
#error "USART2 not present in the selected device"
|
||||
#endif
|
||||
|
||||
#if STM32_UART_USE_USART3 && !STM32_HAS_USART3
|
||||
#error "USART3 not present in the selected device"
|
||||
#endif
|
||||
|
||||
#if STM32_UART_USE_UART4
|
||||
#if !STM32_HAS_UART4
|
||||
#error "UART4 not present in the selected device"
|
||||
#endif
|
||||
|
||||
#if !defined(STM32F2XX) && !defined(STM32F4XX)
|
||||
#error "UART4 DMA access not supported in this platform"
|
||||
#endif
|
||||
#endif /* STM32_UART_USE_UART4 */
|
||||
|
||||
#if STM32_UART_USE_UART5
|
||||
#if !STM32_HAS_UART5
|
||||
#error "UART5 not present in the selected device"
|
||||
#endif
|
||||
|
||||
#if !defined(STM32F2XX) && !defined(STM32F4XX)
|
||||
#error "UART5 DMA access not supported in this platform"
|
||||
#endif
|
||||
#endif /* STM32_UART_USE_UART5 */
|
||||
|
||||
#if STM32_UART_USE_USART6 && !STM32_HAS_USART6
|
||||
#error "USART6 not present in the selected device"
|
||||
#endif
|
||||
|
||||
#if !STM32_UART_USE_USART1 && !STM32_UART_USE_USART2 && \
|
||||
!STM32_UART_USE_USART3 && !STM32_UART_USE_UART4 && \
|
||||
!STM32_UART_USE_UART5 && !STM32_UART_USE_USART6
|
||||
#error "UART driver activated but no USART/UART peripheral assigned"
|
||||
#endif
|
||||
|
||||
#if STM32_UART_USE_USART1 && \
|
||||
!CORTEX_IS_VALID_KERNEL_PRIORITY(STM32_UART_USART1_IRQ_PRIORITY)
|
||||
#error "Invalid IRQ priority assigned to USART1"
|
||||
#endif
|
||||
|
||||
#if STM32_UART_USE_USART2 && \
|
||||
!CORTEX_IS_VALID_KERNEL_PRIORITY(STM32_UART_USART2_IRQ_PRIORITY)
|
||||
#error "Invalid IRQ priority assigned to USART2"
|
||||
#endif
|
||||
|
||||
#if STM32_UART_USE_USART3 && \
|
||||
!CORTEX_IS_VALID_KERNEL_PRIORITY(STM32_UART_USART3_IRQ_PRIORITY)
|
||||
#error "Invalid IRQ priority assigned to USART3"
|
||||
#endif
|
||||
|
||||
#if STM32_UART_USE_UART4 && \
|
||||
!CORTEX_IS_VALID_KERNEL_PRIORITY(STM32_UART_UART4_IRQ_PRIORITY)
|
||||
#error "Invalid IRQ priority assigned to UART4"
|
||||
#endif
|
||||
|
||||
#if STM32_UART_USE_UART5 && \
|
||||
!CORTEX_IS_VALID_KERNEL_PRIORITY(STM32_UART_UART5_IRQ_PRIORITY)
|
||||
#error "Invalid IRQ priority assigned to UART5"
|
||||
#endif
|
||||
|
||||
#if STM32_UART_USE_USART6 && \
|
||||
!CORTEX_IS_VALID_KERNEL_PRIORITY(STM32_UART_USART6_IRQ_PRIORITY)
|
||||
#error "Invalid IRQ priority assigned to USART6"
|
||||
#endif
|
||||
|
||||
#if STM32_UART_USE_USART1 && \
|
||||
!STM32_DMA_IS_VALID_PRIORITY(STM32_UART_USART1_DMA_PRIORITY)
|
||||
#error "Invalid DMA priority assigned to USART1"
|
||||
#endif
|
||||
|
||||
#if STM32_UART_USE_USART2 && \
|
||||
!STM32_DMA_IS_VALID_PRIORITY(STM32_UART_USART2_DMA_PRIORITY)
|
||||
#error "Invalid DMA priority assigned to USART2"
|
||||
#endif
|
||||
|
||||
#if STM32_UART_USE_USART3 && \
|
||||
!STM32_DMA_IS_VALID_PRIORITY(STM32_UART_USART3_DMA_PRIORITY)
|
||||
#error "Invalid DMA priority assigned to USART3"
|
||||
#endif
|
||||
|
||||
#if STM32_UART_USE_UART4 && \
|
||||
!STM32_DMA_IS_VALID_PRIORITY(STM32_UART_UART4_DMA_PRIORITY)
|
||||
#error "Invalid DMA priority assigned to UART4"
|
||||
#endif
|
||||
|
||||
#if STM32_UART_USE_UART5 && \
|
||||
!STM32_DMA_IS_VALID_PRIORITY(STM32_UART_UART5_DMA_PRIORITY)
|
||||
#error "Invalid DMA priority assigned to UART5"
|
||||
#endif
|
||||
|
||||
#if STM32_UART_USE_USART6 && \
|
||||
!STM32_DMA_IS_VALID_PRIORITY(STM32_UART_USART6_DMA_PRIORITY)
|
||||
#error "Invalid DMA priority assigned to USART6"
|
||||
#endif
|
||||
|
||||
#if STM32_UART_USE_USART1 && \
|
||||
!STM32_DMA_IS_VALID_ID(STM32_UART_USART1_RX_DMA_STREAM, \
|
||||
STM32_USART1_RX_DMA_MSK)
|
||||
#error "invalid DMA stream associated to USART1 RX"
|
||||
#endif
|
||||
|
||||
#if STM32_UART_USE_USART1 && \
|
||||
!STM32_DMA_IS_VALID_ID(STM32_UART_USART1_TX_DMA_STREAM, \
|
||||
STM32_USART1_TX_DMA_MSK)
|
||||
#error "invalid DMA stream associated to USART1 TX"
|
||||
#endif
|
||||
|
||||
#if STM32_UART_USE_USART2 && \
|
||||
!STM32_DMA_IS_VALID_ID(STM32_UART_USART2_RX_DMA_STREAM, \
|
||||
STM32_USART2_RX_DMA_MSK)
|
||||
#error "invalid DMA stream associated to USART2 RX"
|
||||
#endif
|
||||
|
||||
#if STM32_UART_USE_USART2 && \
|
||||
!STM32_DMA_IS_VALID_ID(STM32_UART_USART2_TX_DMA_STREAM, \
|
||||
STM32_USART2_TX_DMA_MSK)
|
||||
#error "invalid DMA stream associated to USART2 TX"
|
||||
#endif
|
||||
|
||||
#if STM32_UART_USE_USART3 && \
|
||||
!STM32_DMA_IS_VALID_ID(STM32_UART_USART3_RX_DMA_STREAM, \
|
||||
STM32_USART3_RX_DMA_MSK)
|
||||
#error "invalid DMA stream associated to USART3 RX"
|
||||
#endif
|
||||
|
||||
#if STM32_UART_USE_USART3 && \
|
||||
!STM32_DMA_IS_VALID_ID(STM32_UART_USART3_TX_DMA_STREAM, \
|
||||
STM32_USART3_TX_DMA_MSK)
|
||||
#error "invalid DMA stream associated to USART3 TX"
|
||||
#endif
|
||||
|
||||
#if STM32_UART_USE_UART4 && \
|
||||
!STM32_DMA_IS_VALID_ID(STM32_UART_UART4_RX_DMA_STREAM, \
|
||||
STM32_UART4_RX_DMA_MSK)
|
||||
#error "invalid DMA stream associated to UART4 RX"
|
||||
#endif
|
||||
|
||||
#if STM32_UART_USE_UART4 && \
|
||||
!STM32_DMA_IS_VALID_ID(STM32_UART_UART4_TX_DMA_STREAM, \
|
||||
STM32_UART4_TX_DMA_MSK)
|
||||
#error "invalid DMA stream associated to UART4 TX"
|
||||
#endif
|
||||
|
||||
#if STM32_UART_USE_UART5 && \
|
||||
!STM32_DMA_IS_VALID_ID(STM32_UART_UART5_RX_DMA_STREAM, \
|
||||
STM32_UART5_RX_DMA_MSK)
|
||||
#error "invalid DMA stream associated to UART5 RX"
|
||||
#endif
|
||||
|
||||
#if STM32_UART_USE_UART5 && \
|
||||
!STM32_DMA_IS_VALID_ID(STM32_UART_UART5_TX_DMA_STREAM, \
|
||||
STM32_UART5_TX_DMA_MSK)
|
||||
#error "invalid DMA stream associated to UART5 TX"
|
||||
#endif
|
||||
|
||||
#if STM32_UART_USE_USART6 && \
|
||||
!STM32_DMA_IS_VALID_ID(STM32_UART_USART6_RX_DMA_STREAM, \
|
||||
STM32_USART6_RX_DMA_MSK)
|
||||
#error "invalid DMA stream associated to USART6 RX"
|
||||
#endif
|
||||
|
||||
#if STM32_UART_USE_USART6 && \
|
||||
!STM32_DMA_IS_VALID_ID(STM32_UART_USART6_TX_DMA_STREAM, \
|
||||
STM32_USART6_TX_DMA_MSK)
|
||||
#error "invalid DMA stream associated to USART6 TX"
|
||||
#endif
|
||||
|
||||
#if !defined(STM32_DMA_REQUIRED)
|
||||
#define STM32_DMA_REQUIRED
|
||||
#endif
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver data structures and types. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief UART driver condition flags type.
|
||||
*/
|
||||
typedef uint32_t uartflags_t;
|
||||
|
||||
/**
|
||||
* @brief Structure representing an UART driver.
|
||||
*/
|
||||
typedef struct UARTDriver UARTDriver;
|
||||
|
||||
/**
|
||||
* @brief Generic UART notification callback type.
|
||||
*
|
||||
* @param[in] uartp pointer to the @p UARTDriver object
|
||||
*/
|
||||
typedef void (*uartcb_t)(UARTDriver *uartp);
|
||||
|
||||
/**
|
||||
* @brief Character received UART notification callback type.
|
||||
*
|
||||
* @param[in] uartp pointer to the @p UARTDriver object
|
||||
* @param[in] c received character
|
||||
*/
|
||||
typedef void (*uartccb_t)(UARTDriver *uartp, uint16_t c);
|
||||
|
||||
/**
|
||||
* @brief Receive error UART notification callback type.
|
||||
*
|
||||
* @param[in] uartp pointer to the @p UARTDriver object
|
||||
* @param[in] e receive error mask
|
||||
*/
|
||||
typedef void (*uartecb_t)(UARTDriver *uartp, uartflags_t e);
|
||||
|
||||
/**
|
||||
* @brief Driver configuration structure.
|
||||
* @note It could be empty on some architectures.
|
||||
*/
|
||||
typedef struct {
|
||||
/**
|
||||
* @brief End of transmission buffer callback.
|
||||
*/
|
||||
uartcb_t txend1_cb;
|
||||
/**
|
||||
* @brief Physical end of transmission callback.
|
||||
*/
|
||||
uartcb_t txend2_cb;
|
||||
/**
|
||||
* @brief Receive buffer filled callback.
|
||||
*/
|
||||
uartcb_t rxend_cb;
|
||||
/**
|
||||
* @brief Character received while out if the @p UART_RECEIVE state.
|
||||
*/
|
||||
uartccb_t rxchar_cb;
|
||||
/**
|
||||
* @brief Receive error callback.
|
||||
*/
|
||||
uartecb_t rxerr_cb;
|
||||
/* End of the mandatory fields.*/
|
||||
/**
|
||||
* @brief Bit rate.
|
||||
*/
|
||||
uint32_t speed;
|
||||
/**
|
||||
* @brief Initialization value for the CR1 register.
|
||||
*/
|
||||
uint16_t cr1;
|
||||
/**
|
||||
* @brief Initialization value for the CR2 register.
|
||||
*/
|
||||
uint16_t cr2;
|
||||
/**
|
||||
* @brief Initialization value for the CR3 register.
|
||||
*/
|
||||
uint16_t cr3;
|
||||
} UARTConfig;
|
||||
|
||||
/**
|
||||
* @brief Structure representing an UART driver.
|
||||
*/
|
||||
struct UARTDriver {
|
||||
/**
|
||||
* @brief Driver state.
|
||||
*/
|
||||
uartstate_t state;
|
||||
/**
|
||||
* @brief Transmitter state.
|
||||
*/
|
||||
uarttxstate_t txstate;
|
||||
/**
|
||||
* @brief Receiver state.
|
||||
*/
|
||||
uartrxstate_t rxstate;
|
||||
/**
|
||||
* @brief Current configuration data.
|
||||
*/
|
||||
const UARTConfig *config;
|
||||
#if defined(UART_DRIVER_EXT_FIELDS)
|
||||
UART_DRIVER_EXT_FIELDS
|
||||
#endif
|
||||
/* End of the mandatory fields.*/
|
||||
/**
|
||||
* @brief Pointer to the USART registers block.
|
||||
*/
|
||||
USART_TypeDef *usart;
|
||||
/**
|
||||
* @brief DMA mode bit mask.
|
||||
*/
|
||||
uint32_t dmamode;
|
||||
/**
|
||||
* @brief Receive DMA channel.
|
||||
*/
|
||||
const stm32_dma_stream_t *dmarx;
|
||||
/**
|
||||
* @brief Transmit DMA channel.
|
||||
*/
|
||||
const stm32_dma_stream_t *dmatx;
|
||||
/**
|
||||
* @brief Default receive buffer while into @p UART_RX_IDLE state.
|
||||
*/
|
||||
volatile uint16_t rxbuf;
|
||||
};
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver macros. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* External declarations. */
|
||||
/*===========================================================================*/
|
||||
|
||||
#if STM32_UART_USE_USART1 && !defined(__DOXYGEN__)
|
||||
extern UARTDriver UARTD1;
|
||||
#endif
|
||||
|
||||
#if STM32_UART_USE_USART2 && !defined(__DOXYGEN__)
|
||||
extern UARTDriver UARTD2;
|
||||
#endif
|
||||
|
||||
#if STM32_UART_USE_USART3 && !defined(__DOXYGEN__)
|
||||
extern UARTDriver UARTD3;
|
||||
#endif
|
||||
|
||||
#if STM32_UART_USE_UART4 && !defined(__DOXYGEN__)
|
||||
extern UARTDriver UARTD4;
|
||||
#endif
|
||||
|
||||
#if STM32_UART_USE_UART5 && !defined(__DOXYGEN__)
|
||||
extern UARTDriver UARTD5;
|
||||
#endif
|
||||
|
||||
#if STM32_UART_USE_USART6 && !defined(__DOXYGEN__)
|
||||
extern UARTDriver UARTD6;
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
void uart_lld_init(void);
|
||||
void uart_lld_start(UARTDriver *uartp);
|
||||
void uart_lld_stop(UARTDriver *uartp);
|
||||
void uart_lld_start_send(UARTDriver *uartp, size_t n, const void *txbuf);
|
||||
size_t uart_lld_stop_send(UARTDriver *uartp);
|
||||
void uart_lld_start_receive(UARTDriver *uartp, size_t n, void *rxbuf);
|
||||
size_t uart_lld_stop_receive(UARTDriver *uartp);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* HAL_USE_UART */
|
||||
|
||||
#endif /* _UART_LLD_H_ */
|
||||
|
||||
/** @} */
|
||||
@@ -0,0 +1,534 @@
|
||||
/*
|
||||
ChibiOS/RT - Copyright (C) 2006-2013 Giovanni Di Sirio
|
||||
|
||||
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 STM32/USARTv2/serial_lld.c
|
||||
* @brief STM32 low level serial driver code.
|
||||
*
|
||||
* @addtogroup SERIAL
|
||||
* @{
|
||||
*/
|
||||
|
||||
#include "ch.h"
|
||||
#include "hal.h"
|
||||
|
||||
#if HAL_USE_SERIAL || defined(__DOXYGEN__)
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local definitions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver exported variables. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/** @brief USART1 serial driver identifier.*/
|
||||
#if STM32_SERIAL_USE_USART1 || defined(__DOXYGEN__)
|
||||
SerialDriver SD1;
|
||||
#endif
|
||||
|
||||
/** @brief USART2 serial driver identifier.*/
|
||||
#if STM32_SERIAL_USE_USART2 || defined(__DOXYGEN__)
|
||||
SerialDriver SD2;
|
||||
#endif
|
||||
|
||||
/** @brief USART3 serial driver identifier.*/
|
||||
#if STM32_SERIAL_USE_USART3 || defined(__DOXYGEN__)
|
||||
SerialDriver SD3;
|
||||
#endif
|
||||
|
||||
/** @brief UART4 serial driver identifier.*/
|
||||
#if STM32_SERIAL_USE_UART4 || defined(__DOXYGEN__)
|
||||
SerialDriver SD4;
|
||||
#endif
|
||||
|
||||
/** @brief UART5 serial driver identifier.*/
|
||||
#if STM32_SERIAL_USE_UART5 || defined(__DOXYGEN__)
|
||||
SerialDriver SD5;
|
||||
#endif
|
||||
|
||||
/** @brief USART6 serial driver identifier.*/
|
||||
#if STM32_SERIAL_USE_USART6 || defined(__DOXYGEN__)
|
||||
SerialDriver SD6;
|
||||
#endif
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local variables and types. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/** @brief Driver default configuration.*/
|
||||
static const SerialConfig default_config =
|
||||
{
|
||||
SERIAL_DEFAULT_BITRATE,
|
||||
0,
|
||||
USART_CR2_STOP1_BITS | USART_CR2_LINEN,
|
||||
0
|
||||
};
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief USART initialization.
|
||||
* @details This function must be invoked with interrupts disabled.
|
||||
*
|
||||
* @param[in] sdp pointer to a @p SerialDriver object
|
||||
* @param[in] config the architecture-dependent serial driver configuration
|
||||
*/
|
||||
static void usart_init(SerialDriver *sdp, const SerialConfig *config) {
|
||||
USART_TypeDef *u = sdp->usart;
|
||||
|
||||
/* Baud rate setting.*/
|
||||
u->BRR = (uint16_t)(sdp->clock / config->speed);
|
||||
|
||||
/* Note that some bits are enforced.*/
|
||||
u->CR2 = config->cr2 | USART_CR2_LBDIE;
|
||||
u->CR3 = config->cr3 | USART_CR3_EIE;
|
||||
u->CR1 = config->cr1 | USART_CR1_UE | USART_CR1_PEIE |
|
||||
USART_CR1_RXNEIE | USART_CR1_TE |
|
||||
USART_CR1_RE;
|
||||
u->ICR = 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief USART de-initialization.
|
||||
* @details This function must be invoked with interrupts disabled.
|
||||
*
|
||||
* @param[in] u pointer to an USART I/O block
|
||||
*/
|
||||
static void usart_deinit(USART_TypeDef *u) {
|
||||
|
||||
u->CR1 = 0;
|
||||
u->CR2 = 0;
|
||||
u->CR3 = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Error handling routine.
|
||||
*
|
||||
* @param[in] sdp pointer to a @p SerialDriver object
|
||||
* @param[in] isr USART ISR register value
|
||||
*/
|
||||
static void set_error(SerialDriver *sdp, uint32_t isr) {
|
||||
flagsmask_t sts = 0;
|
||||
|
||||
if (isr & USART_ISR_ORE)
|
||||
sts |= SD_OVERRUN_ERROR;
|
||||
if (isr & USART_ISR_PE)
|
||||
sts |= SD_PARITY_ERROR;
|
||||
if (isr & USART_ISR_FE)
|
||||
sts |= SD_FRAMING_ERROR;
|
||||
if (isr & USART_ISR_NE)
|
||||
sts |= SD_NOISE_ERROR;
|
||||
chSysLockFromIsr();
|
||||
chnAddFlagsI(sdp, sts);
|
||||
chSysUnlockFromIsr();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Common IRQ handler.
|
||||
*
|
||||
* @param[in] sdp communication channel associated to the USART
|
||||
*/
|
||||
static void serve_interrupt(SerialDriver *sdp) {
|
||||
USART_TypeDef *u = sdp->usart;
|
||||
uint32_t cr1 = u->CR1;
|
||||
uint32_t isr;
|
||||
|
||||
/* Reading and clearing status.*/
|
||||
isr = u->ISR;
|
||||
u->ICR = isr;
|
||||
|
||||
/* Error condition detection.*/
|
||||
if (isr & (USART_ISR_ORE | USART_ISR_NE | USART_ISR_FE | USART_ISR_PE))
|
||||
set_error(sdp, isr);
|
||||
|
||||
/* Special case, LIN break detection.*/
|
||||
if (isr & USART_ISR_LBD) {
|
||||
chSysLockFromIsr();
|
||||
chnAddFlagsI(sdp, SD_BREAK_DETECTED);
|
||||
chSysUnlockFromIsr();
|
||||
}
|
||||
|
||||
/* Data available.*/
|
||||
if (isr & USART_ISR_RXNE) {
|
||||
chSysLockFromIsr();
|
||||
sdIncomingDataI(sdp, (uint8_t)u->RDR);
|
||||
chSysUnlockFromIsr();
|
||||
}
|
||||
|
||||
/* Transmission buffer empty.*/
|
||||
if ((cr1 & USART_CR1_TXEIE) && (isr & USART_ISR_TXE)) {
|
||||
msg_t b;
|
||||
chSysLockFromIsr();
|
||||
b = chOQGetI(&sdp->oqueue);
|
||||
if (b < Q_OK) {
|
||||
chnAddFlagsI(sdp, CHN_OUTPUT_EMPTY);
|
||||
u->CR1 = (cr1 & ~USART_CR1_TXEIE) | USART_CR1_TCIE;
|
||||
}
|
||||
else
|
||||
u->TDR = b;
|
||||
chSysUnlockFromIsr();
|
||||
}
|
||||
|
||||
/* Physical transmission end.*/
|
||||
if (isr & USART_ISR_TC) {
|
||||
chSysLockFromIsr();
|
||||
if (chOQIsEmptyI(&sdp->oqueue))
|
||||
chnAddFlagsI(sdp, CHN_TRANSMISSION_END);
|
||||
u->CR1 = cr1 & ~USART_CR1_TCIE;
|
||||
chSysUnlockFromIsr();
|
||||
}
|
||||
}
|
||||
|
||||
#if STM32_SERIAL_USE_USART1 || defined(__DOXYGEN__)
|
||||
static void notify1(GenericQueue *qp) {
|
||||
|
||||
(void)qp;
|
||||
USART1->CR1 |= USART_CR1_TXEIE;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if STM32_SERIAL_USE_USART2 || defined(__DOXYGEN__)
|
||||
static void notify2(GenericQueue *qp) {
|
||||
|
||||
(void)qp;
|
||||
USART2->CR1 |= USART_CR1_TXEIE;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if STM32_SERIAL_USE_USART3 || defined(__DOXYGEN__)
|
||||
static void notify3(GenericQueue *qp) {
|
||||
|
||||
(void)qp;
|
||||
USART3->CR1 |= USART_CR1_TXEIE;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if STM32_SERIAL_USE_UART4 || defined(__DOXYGEN__)
|
||||
static void notify4(GenericQueue *qp) {
|
||||
|
||||
(void)qp;
|
||||
UART4->CR1 |= USART_CR1_TXEIE;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if STM32_SERIAL_USE_UART5 || defined(__DOXYGEN__)
|
||||
static void notify5(GenericQueue *qp) {
|
||||
|
||||
(void)qp;
|
||||
UART5->CR1 |= USART_CR1_TXEIE;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if STM32_SERIAL_USE_USART6 || defined(__DOXYGEN__)
|
||||
static void notify6(GenericQueue *qp) {
|
||||
|
||||
(void)qp;
|
||||
USART6->CR1 |= USART_CR1_TXEIE;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver interrupt handlers. */
|
||||
/*===========================================================================*/
|
||||
|
||||
#if STM32_SERIAL_USE_USART1 || defined(__DOXYGEN__)
|
||||
#if !defined(STM32_USART1_HANDLER)
|
||||
#error "STM32_USART1_HANDLER not defined"
|
||||
#endif
|
||||
/**
|
||||
* @brief USART1 interrupt handler.
|
||||
*
|
||||
* @isr
|
||||
*/
|
||||
CH_IRQ_HANDLER(STM32_USART1_HANDLER) {
|
||||
|
||||
CH_IRQ_PROLOGUE();
|
||||
|
||||
serve_interrupt(&SD1);
|
||||
|
||||
CH_IRQ_EPILOGUE();
|
||||
}
|
||||
#endif
|
||||
|
||||
#if STM32_SERIAL_USE_USART2 || defined(__DOXYGEN__)
|
||||
#if !defined(STM32_USART2_HANDLER)
|
||||
#error "STM32_USART2_HANDLER not defined"
|
||||
#endif
|
||||
/**
|
||||
* @brief USART2 interrupt handler.
|
||||
*
|
||||
* @isr
|
||||
*/
|
||||
CH_IRQ_HANDLER(STM32_USART2_HANDLER) {
|
||||
|
||||
CH_IRQ_PROLOGUE();
|
||||
|
||||
serve_interrupt(&SD2);
|
||||
|
||||
CH_IRQ_EPILOGUE();
|
||||
}
|
||||
#endif
|
||||
|
||||
#if STM32_SERIAL_USE_USART3 || defined(__DOXYGEN__)
|
||||
#if !defined(STM32_USART3_HANDLER)
|
||||
#error "STM32_USART3_HANDLER not defined"
|
||||
#endif
|
||||
/**
|
||||
* @brief USART3 interrupt handler.
|
||||
*
|
||||
* @isr
|
||||
*/
|
||||
CH_IRQ_HANDLER(STM32_USART3_HANDLER) {
|
||||
|
||||
CH_IRQ_PROLOGUE();
|
||||
|
||||
serve_interrupt(&SD3);
|
||||
|
||||
CH_IRQ_EPILOGUE();
|
||||
}
|
||||
#endif
|
||||
|
||||
#if STM32_SERIAL_USE_UART4 || defined(__DOXYGEN__)
|
||||
#if !defined(STM32_UART4_HANDLER)
|
||||
#error "STM32_UART4_HANDLER not defined"
|
||||
#endif
|
||||
/**
|
||||
* @brief UART4 interrupt handler.
|
||||
*
|
||||
* @isr
|
||||
*/
|
||||
CH_IRQ_HANDLER(STM32_UART4_HANDLER) {
|
||||
|
||||
CH_IRQ_PROLOGUE();
|
||||
|
||||
serve_interrupt(&SD4);
|
||||
|
||||
CH_IRQ_EPILOGUE();
|
||||
}
|
||||
#endif
|
||||
|
||||
#if STM32_SERIAL_USE_UART5 || defined(__DOXYGEN__)
|
||||
#if !defined(STM32_UART5_HANDLER)
|
||||
#error "STM32_UART5_HANDLER not defined"
|
||||
#endif
|
||||
/**
|
||||
* @brief UART5 interrupt handler.
|
||||
*
|
||||
* @isr
|
||||
*/
|
||||
CH_IRQ_HANDLER(STM32_UART5_HANDLER) {
|
||||
|
||||
CH_IRQ_PROLOGUE();
|
||||
|
||||
serve_interrupt(&SD5);
|
||||
|
||||
CH_IRQ_EPILOGUE();
|
||||
}
|
||||
#endif
|
||||
|
||||
#if STM32_SERIAL_USE_USART6 || defined(__DOXYGEN__)
|
||||
#if !defined(STM32_USART6_HANDLER)
|
||||
#error "STM32_USART6_HANDLER not defined"
|
||||
#endif
|
||||
/**
|
||||
* @brief USART1 interrupt handler.
|
||||
*
|
||||
* @isr
|
||||
*/
|
||||
CH_IRQ_HANDLER(STM32_USART6_HANDLER) {
|
||||
|
||||
CH_IRQ_PROLOGUE();
|
||||
|
||||
serve_interrupt(&SD6);
|
||||
|
||||
CH_IRQ_EPILOGUE();
|
||||
}
|
||||
#endif
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver exported functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Low level serial driver initialization.
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void sd_lld_init(void) {
|
||||
|
||||
#if STM32_SERIAL_USE_USART1
|
||||
sdObjectInit(&SD1, NULL, notify1);
|
||||
SD1.usart = USART1;
|
||||
SD1.clock = STM32_USART1CLK;
|
||||
#endif
|
||||
|
||||
#if STM32_SERIAL_USE_USART2
|
||||
sdObjectInit(&SD2, NULL, notify2);
|
||||
SD2.usart = USART2;
|
||||
SD2.clock = STM32_USART2CLK;
|
||||
#endif
|
||||
|
||||
#if STM32_SERIAL_USE_USART3
|
||||
sdObjectInit(&SD3, NULL, notify3);
|
||||
SD3.usart = USART3;
|
||||
SD3.clock = STM32_USART3CLK;
|
||||
#endif
|
||||
|
||||
#if STM32_SERIAL_USE_UART4
|
||||
sdObjectInit(&SD4, NULL, notify4);
|
||||
SD4.usart = UART4;
|
||||
SD4.clock = STM32_UART4CLK;
|
||||
#endif
|
||||
|
||||
#if STM32_SERIAL_USE_UART5
|
||||
sdObjectInit(&SD5, NULL, notify5);
|
||||
SD5.usart = UART5;
|
||||
SD5.clock = STM32_UART5CLK;
|
||||
#endif
|
||||
|
||||
#if STM32_SERIAL_USE_USART6
|
||||
sdObjectInit(&SD6, NULL, notify6);
|
||||
SD6.usart = USART6;
|
||||
SD6.clock = STM32_USART6CLK;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @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) {
|
||||
#if STM32_SERIAL_USE_USART1
|
||||
if (&SD1 == sdp) {
|
||||
rccEnableUSART1(FALSE);
|
||||
nvicEnableVector(STM32_USART1_NUMBER,
|
||||
CORTEX_PRIORITY_MASK(STM32_SERIAL_USART1_PRIORITY));
|
||||
}
|
||||
#endif
|
||||
#if STM32_SERIAL_USE_USART2
|
||||
if (&SD2 == sdp) {
|
||||
rccEnableUSART2(FALSE);
|
||||
nvicEnableVector(STM32_USART2_NUMBER,
|
||||
CORTEX_PRIORITY_MASK(STM32_SERIAL_USART2_PRIORITY));
|
||||
}
|
||||
#endif
|
||||
#if STM32_SERIAL_USE_USART3
|
||||
if (&SD3 == sdp) {
|
||||
rccEnableUSART3(FALSE);
|
||||
nvicEnableVector(STM32_USART3_NUMBER,
|
||||
CORTEX_PRIORITY_MASK(STM32_SERIAL_USART3_PRIORITY));
|
||||
}
|
||||
#endif
|
||||
#if STM32_SERIAL_USE_UART4
|
||||
if (&SD4 == sdp) {
|
||||
rccEnableUART4(FALSE);
|
||||
nvicEnableVector(STM32_UART4_NUMBER,
|
||||
CORTEX_PRIORITY_MASK(STM32_SERIAL_UART4_PRIORITY));
|
||||
}
|
||||
#endif
|
||||
#if STM32_SERIAL_USE_UART5
|
||||
if (&SD5 == sdp) {
|
||||
rccEnableUART5(FALSE);
|
||||
nvicEnableVector(STM32_UART5_NUMBER,
|
||||
CORTEX_PRIORITY_MASK(STM32_SERIAL_UART5_PRIORITY));
|
||||
}
|
||||
#endif
|
||||
#if STM32_SERIAL_USE_USART6
|
||||
if (&SD6 == sdp) {
|
||||
rccEnableUSART6(FALSE);
|
||||
nvicEnableVector(STM32_USART6_NUMBER,
|
||||
CORTEX_PRIORITY_MASK(STM32_SERIAL_USART6_PRIORITY));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
usart_init(sdp, config);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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) {
|
||||
usart_deinit(sdp->usart);
|
||||
#if STM32_SERIAL_USE_USART1
|
||||
if (&SD1 == sdp) {
|
||||
rccDisableUSART1(FALSE);
|
||||
nvicDisableVector(STM32_USART1_NUMBER);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
#if STM32_SERIAL_USE_USART2
|
||||
if (&SD2 == sdp) {
|
||||
rccDisableUSART2(FALSE);
|
||||
nvicDisableVector(STM32_USART2_NUMBER);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
#if STM32_SERIAL_USE_USART3
|
||||
if (&SD3 == sdp) {
|
||||
rccDisableUSART3(FALSE);
|
||||
nvicDisableVector(STM32_USART3_NUMBER);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
#if STM32_SERIAL_USE_UART4
|
||||
if (&SD4 == sdp) {
|
||||
rccDisableUART4(FALSE);
|
||||
nvicDisableVector(STM32_UART4_NUMBER);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
#if STM32_SERIAL_USE_UART5
|
||||
if (&SD5 == sdp) {
|
||||
rccDisableUART5(FALSE);
|
||||
nvicDisableVector(STM32_UART5_NUMBER);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
#if STM32_SERIAL_USE_USART6
|
||||
if (&SD6 == sdp) {
|
||||
rccDisableUSART6(FALSE);
|
||||
nvicDisableVector(STM32_USART6_NUMBER);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* HAL_USE_SERIAL */
|
||||
|
||||
/** @} */
|
||||
@@ -0,0 +1,305 @@
|
||||
/*
|
||||
ChibiOS/RT - Copyright (C) 2006-2013 Giovanni Di Sirio
|
||||
|
||||
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 STM32/USARTv2/serial_lld.h
|
||||
* @brief STM32 low level serial driver header.
|
||||
*
|
||||
* @addtogroup SERIAL
|
||||
* @{
|
||||
*/
|
||||
|
||||
#ifndef _SERIAL_LLD_H_
|
||||
#define _SERIAL_LLD_H_
|
||||
|
||||
#if HAL_USE_SERIAL || defined(__DOXYGEN__)
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver constants. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver pre-compile time settings. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @name Configuration options
|
||||
* @{
|
||||
*/
|
||||
/**
|
||||
* @brief USART1 driver enable switch.
|
||||
* @details If set to @p TRUE the support for USART1 is included.
|
||||
* @note The default is @p TRUE.
|
||||
*/
|
||||
#if !defined(STM32_SERIAL_USE_USART1) || defined(__DOXYGEN__)
|
||||
#define STM32_SERIAL_USE_USART1 FALSE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief USART2 driver enable switch.
|
||||
* @details If set to @p TRUE the support for USART2 is included.
|
||||
* @note The default is @p TRUE.
|
||||
*/
|
||||
#if !defined(STM32_SERIAL_USE_USART2) || defined(__DOXYGEN__)
|
||||
#define STM32_SERIAL_USE_USART2 FALSE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief USART3 driver enable switch.
|
||||
* @details If set to @p TRUE the support for USART3 is included.
|
||||
* @note The default is @p TRUE.
|
||||
*/
|
||||
#if !defined(STM32_SERIAL_USE_USART3) || defined(__DOXYGEN__)
|
||||
#define STM32_SERIAL_USE_USART3 FALSE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief UART4 driver enable switch.
|
||||
* @details If set to @p TRUE the support for UART4 is included.
|
||||
* @note The default is @p TRUE.
|
||||
*/
|
||||
#if !defined(STM32_SERIAL_USE_UART4) || defined(__DOXYGEN__)
|
||||
#define STM32_SERIAL_USE_UART4 FALSE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief UART5 driver enable switch.
|
||||
* @details If set to @p TRUE the support for UART5 is included.
|
||||
* @note The default is @p TRUE.
|
||||
*/
|
||||
#if !defined(STM32_SERIAL_USE_UART5) || defined(__DOXYGEN__)
|
||||
#define STM32_SERIAL_USE_UART5 FALSE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief USART6 driver enable switch.
|
||||
* @details If set to @p TRUE the support for USART6 is included.
|
||||
* @note The default is @p TRUE.
|
||||
*/
|
||||
#if !defined(STM32_SERIAL_USE_USART6) || defined(__DOXYGEN__)
|
||||
#define STM32_SERIAL_USE_USART6 FALSE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief USART1 interrupt priority level setting.
|
||||
*/
|
||||
#if !defined(STM32_SERIAL_USART1_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define STM32_SERIAL_USART1_PRIORITY 12
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief USART2 interrupt priority level setting.
|
||||
*/
|
||||
#if !defined(STM32_SERIAL_USART2_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define STM32_SERIAL_USART2_PRIORITY 12
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief USART3 interrupt priority level setting.
|
||||
*/
|
||||
#if !defined(STM32_SERIAL_USART3_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define STM32_SERIAL_USART3_PRIORITY 12
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief UART4 interrupt priority level setting.
|
||||
*/
|
||||
#if !defined(STM32_SERIAL_UART4_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define STM32_SERIAL_UART4_PRIORITY 12
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief UART5 interrupt priority level setting.
|
||||
*/
|
||||
#if !defined(STM32_SERIAL_UART5_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define STM32_SERIAL_UART5_PRIORITY 12
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief USART6 interrupt priority level setting.
|
||||
*/
|
||||
#if !defined(STM32_SERIAL_USART6_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define STM32_SERIAL_USART6_PRIORITY 12
|
||||
#endif
|
||||
/** @} */
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Derived constants and error checks. */
|
||||
/*===========================================================================*/
|
||||
|
||||
#if STM32_SERIAL_USE_USART1 && !STM32_HAS_USART1
|
||||
#error "USART1 not present in the selected device"
|
||||
#endif
|
||||
|
||||
#if STM32_SERIAL_USE_USART2 && !STM32_HAS_USART2
|
||||
#error "USART2 not present in the selected device"
|
||||
#endif
|
||||
|
||||
#if STM32_SERIAL_USE_USART3 && !STM32_HAS_USART3
|
||||
#error "USART3 not present in the selected device"
|
||||
#endif
|
||||
|
||||
#if STM32_SERIAL_USE_UART4 && !STM32_HAS_UART4
|
||||
#error "UART4 not present in the selected device"
|
||||
#endif
|
||||
|
||||
#if STM32_SERIAL_USE_UART5 && !STM32_HAS_UART5
|
||||
#error "UART5 not present in the selected device"
|
||||
#endif
|
||||
|
||||
#if STM32_SERIAL_USE_USART6 && !STM32_HAS_USART6
|
||||
#error "USART6 not present in the selected device"
|
||||
#endif
|
||||
|
||||
#if !STM32_SERIAL_USE_USART1 && !STM32_SERIAL_USE_USART2 && \
|
||||
!STM32_SERIAL_USE_USART3 && !STM32_SERIAL_USE_UART4 && \
|
||||
!STM32_SERIAL_USE_UART5 && !STM32_SERIAL_USE_USART6
|
||||
#error "SERIAL driver activated but no USART/UART peripheral assigned"
|
||||
#endif
|
||||
|
||||
#if STM32_SERIAL_USE_USART1 && \
|
||||
!CORTEX_IS_VALID_KERNEL_PRIORITY(STM32_SERIAL_USART1_PRIORITY)
|
||||
#error "Invalid IRQ priority assigned to USART1"
|
||||
#endif
|
||||
|
||||
#if STM32_SERIAL_USE_USART2 && \
|
||||
!CORTEX_IS_VALID_KERNEL_PRIORITY(STM32_SERIAL_USART2_PRIORITY)
|
||||
#error "Invalid IRQ priority assigned to USART2"
|
||||
#endif
|
||||
|
||||
#if STM32_SERIAL_USE_USART3 && \
|
||||
!CORTEX_IS_VALID_KERNEL_PRIORITY(STM32_SERIAL_USART3_PRIORITY)
|
||||
#error "Invalid IRQ priority assigned to USART3"
|
||||
#endif
|
||||
|
||||
#if STM32_SERIAL_USE_UART4 && \
|
||||
!CORTEX_IS_VALID_KERNEL_PRIORITY(STM32_SERIAL_UART4_PRIORITY)
|
||||
#error "Invalid IRQ priority assigned to UART4"
|
||||
#endif
|
||||
|
||||
#if STM32_SERIAL_USE_UART5 && \
|
||||
!CORTEX_IS_VALID_KERNEL_PRIORITY(STM32_SERIAL_UART5_PRIORITY)
|
||||
#error "Invalid IRQ priority assigned to UART5"
|
||||
#endif
|
||||
|
||||
#if STM32_SERIAL_USE_USART6 && \
|
||||
!CORTEX_IS_VALID_KERNEL_PRIORITY(STM32_SERIAL_USART6_PRIORITY)
|
||||
#error "Invalid IRQ priority assigned to USART6"
|
||||
#endif
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver data structures and types. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief STM32 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 This structure content is architecture dependent, each driver
|
||||
* implementation defines its own version and the custom static
|
||||
* initializers.
|
||||
*/
|
||||
typedef struct {
|
||||
/**
|
||||
* @brief Bit rate.
|
||||
*/
|
||||
uint32_t speed;
|
||||
/* End of the mandatory fields.*/
|
||||
/**
|
||||
* @brief Initialization value for the CR1 register.
|
||||
*/
|
||||
uint32_t cr1;
|
||||
/**
|
||||
* @brief Initialization value for the CR2 register.
|
||||
*/
|
||||
uint32_t cr2;
|
||||
/**
|
||||
* @brief Initialization value for the CR3 register.
|
||||
*/
|
||||
uint32_t cr3;
|
||||
} SerialConfig;
|
||||
|
||||
/**
|
||||
* @brief @p SerialDriver specific data.
|
||||
*/
|
||||
#define _serial_driver_data \
|
||||
_base_asynchronous_channel_data \
|
||||
/* Driver state.*/ \
|
||||
sdstate_t state; \
|
||||
/* Input queue.*/ \
|
||||
InputQueue iqueue; \
|
||||
/* Output queue.*/ \
|
||||
OutputQueue 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 USART registers block.*/ \
|
||||
USART_TypeDef *usart; \
|
||||
/* Clock frequency for the associated USART/UART.*/ \
|
||||
uint32_t clock;
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver macros. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*
|
||||
* Extra USARTs definitions here (missing from the ST header file).
|
||||
*/
|
||||
#define USART_CR2_STOP1_BITS (0 << 12) /**< @brief CR2 1 stop bit value.*/
|
||||
#define USART_CR2_STOP0P5_BITS (1 << 12) /**< @brief CR2 0.5 stop bit value.*/
|
||||
#define USART_CR2_STOP2_BITS (2 << 12) /**< @brief CR2 2 stop bit value.*/
|
||||
#define USART_CR2_STOP1P5_BITS (3 << 12) /**< @brief CR2 1.5 stop bit value.*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* External declarations. */
|
||||
/*===========================================================================*/
|
||||
|
||||
#if STM32_SERIAL_USE_USART1 && !defined(__DOXYGEN__)
|
||||
extern SerialDriver SD1;
|
||||
#endif
|
||||
#if STM32_SERIAL_USE_USART2 && !defined(__DOXYGEN__)
|
||||
extern SerialDriver SD2;
|
||||
#endif
|
||||
#if STM32_SERIAL_USE_USART3 && !defined(__DOXYGEN__)
|
||||
extern SerialDriver SD3;
|
||||
#endif
|
||||
#if STM32_SERIAL_USE_UART4 && !defined(__DOXYGEN__)
|
||||
extern SerialDriver SD4;
|
||||
#endif
|
||||
#if STM32_SERIAL_USE_UART5 && !defined(__DOXYGEN__)
|
||||
extern SerialDriver SD5;
|
||||
#endif
|
||||
#if STM32_SERIAL_USE_USART6 && !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 /* _SERIAL_LLD_H_ */
|
||||
|
||||
/** @} */
|
||||
+604
@@ -0,0 +1,604 @@
|
||||
/*
|
||||
ChibiOS/RT - Copyright (C) 2006-2013 Giovanni Di Sirio
|
||||
|
||||
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 STM32/USARTv2/uart_lld.c
|
||||
* @brief STM32 low level UART driver code.
|
||||
*
|
||||
* @addtogroup UART
|
||||
* @{
|
||||
*/
|
||||
|
||||
#include "ch.h"
|
||||
#include "hal.h"
|
||||
|
||||
#if HAL_USE_UART || defined(__DOXYGEN__)
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local definitions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
#define USART1_RX_DMA_CHANNEL \
|
||||
STM32_DMA_GETCHANNEL(STM32_UART_USART1_RX_DMA_STREAM, \
|
||||
STM32_USART1_RX_DMA_CHN)
|
||||
|
||||
#define USART1_TX_DMA_CHANNEL \
|
||||
STM32_DMA_GETCHANNEL(STM32_UART_USART1_TX_DMA_STREAM, \
|
||||
STM32_USART1_TX_DMA_CHN)
|
||||
|
||||
#define USART2_RX_DMA_CHANNEL \
|
||||
STM32_DMA_GETCHANNEL(STM32_UART_USART2_RX_DMA_STREAM, \
|
||||
STM32_USART2_RX_DMA_CHN)
|
||||
|
||||
#define USART2_TX_DMA_CHANNEL \
|
||||
STM32_DMA_GETCHANNEL(STM32_UART_USART2_TX_DMA_STREAM, \
|
||||
STM32_USART2_TX_DMA_CHN)
|
||||
|
||||
#define USART3_RX_DMA_CHANNEL \
|
||||
STM32_DMA_GETCHANNEL(STM32_UART_USART3_RX_DMA_STREAM, \
|
||||
STM32_USART3_RX_DMA_CHN)
|
||||
|
||||
#define USART3_TX_DMA_CHANNEL \
|
||||
STM32_DMA_GETCHANNEL(STM32_UART_USART3_TX_DMA_STREAM, \
|
||||
STM32_USART3_TX_DMA_CHN)
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver exported variables. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/** @brief USART1 UART driver identifier.*/
|
||||
#if STM32_UART_USE_USART1 || defined(__DOXYGEN__)
|
||||
UARTDriver UARTD1;
|
||||
#endif
|
||||
|
||||
/** @brief USART2 UART driver identifier.*/
|
||||
#if STM32_UART_USE_USART2 || defined(__DOXYGEN__)
|
||||
UARTDriver UARTD2;
|
||||
#endif
|
||||
|
||||
/** @brief USART3 UART driver identifier.*/
|
||||
#if STM32_UART_USE_USART3 || defined(__DOXYGEN__)
|
||||
UARTDriver UARTD3;
|
||||
#endif
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local variables and types. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Status bits translation.
|
||||
*
|
||||
* @param[in] sr USART SR register value
|
||||
*
|
||||
* @return The error flags.
|
||||
*/
|
||||
static uartflags_t translate_errors(uint32_t isr) {
|
||||
uartflags_t sts = 0;
|
||||
|
||||
if (isr & USART_ISR_ORE)
|
||||
sts |= UART_OVERRUN_ERROR;
|
||||
if (isr & USART_ISR_PE)
|
||||
sts |= UART_PARITY_ERROR;
|
||||
if (isr & USART_ISR_FE)
|
||||
sts |= UART_FRAMING_ERROR;
|
||||
if (isr & USART_ISR_NE)
|
||||
sts |= UART_NOISE_ERROR;
|
||||
if (isr & USART_ISR_LBD)
|
||||
sts |= UART_BREAK_DETECTED;
|
||||
return sts;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Puts the receiver in the UART_RX_IDLE state.
|
||||
*
|
||||
* @param[in] uartp pointer to the @p UARTDriver object
|
||||
*/
|
||||
static void set_rx_idle_loop(UARTDriver *uartp) {
|
||||
uint32_t mode;
|
||||
|
||||
/* RX DMA channel preparation, if the char callback is defined then the
|
||||
TCIE interrupt is enabled too.*/
|
||||
if (uartp->config->rxchar_cb == NULL)
|
||||
mode = STM32_DMA_CR_DIR_P2M | STM32_DMA_CR_CIRC;
|
||||
else
|
||||
mode = STM32_DMA_CR_DIR_P2M | STM32_DMA_CR_CIRC | STM32_DMA_CR_TCIE;
|
||||
dmaStreamSetMemory0(uartp->dmarx, &uartp->rxbuf);
|
||||
dmaStreamSetTransactionSize(uartp->dmarx, 1);
|
||||
dmaStreamSetMode(uartp->dmarx, uartp->dmamode | mode);
|
||||
dmaStreamEnable(uartp->dmarx);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief USART de-initialization.
|
||||
* @details This function must be invoked with interrupts disabled.
|
||||
*
|
||||
* @param[in] uartp pointer to the @p UARTDriver object
|
||||
*/
|
||||
static void usart_stop(UARTDriver *uartp) {
|
||||
|
||||
/* Stops RX and TX DMA channels.*/
|
||||
dmaStreamDisable(uartp->dmarx);
|
||||
dmaStreamDisable(uartp->dmatx);
|
||||
|
||||
/* Stops USART operations.*/
|
||||
uartp->usart->CR1 = 0;
|
||||
uartp->usart->CR2 = 0;
|
||||
uartp->usart->CR3 = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief USART initialization.
|
||||
* @details This function must be invoked with interrupts disabled.
|
||||
*
|
||||
* @param[in] uartp pointer to the @p UARTDriver object
|
||||
*/
|
||||
static void usart_start(UARTDriver *uartp) {
|
||||
uint32_t cr1;
|
||||
USART_TypeDef *u = uartp->usart;
|
||||
|
||||
/* Defensive programming, starting from a clean state.*/
|
||||
usart_stop(uartp);
|
||||
|
||||
/* Baud rate setting.*/
|
||||
#if defined(STM32F0XX)
|
||||
if (uartp->usart == USART1)
|
||||
u->BRR = STM32_USART1CLK / uartp->config->speed;
|
||||
else
|
||||
u->BRR = STM32_PCLK / uartp->config->speed;
|
||||
#else /* !defined(STM32F0XX) */
|
||||
if (uartp->usart == USART1)
|
||||
u->BRR = STM32_PCLK2 / uartp->config->speed;
|
||||
else
|
||||
u->BRR = STM32_PCLK1 / uartp->config->speed;
|
||||
#endif /* !defined(STM32F0XX) */
|
||||
|
||||
/* Resetting eventual pending status flags.*/
|
||||
u->ICR = 0xFFFFFFFF;
|
||||
|
||||
/* Note that some bits are enforced because required for correct driver
|
||||
operations.*/
|
||||
u->CR2 = uartp->config->cr2 | USART_CR2_LBDIE;
|
||||
u->CR3 = uartp->config->cr3 | USART_CR3_DMAT | USART_CR3_DMAR |
|
||||
USART_CR3_EIE;
|
||||
|
||||
/* Mustn't ever set TCIE here - if done, it causes an immediate
|
||||
interrupt.*/
|
||||
cr1 = USART_CR1_UE | USART_CR1_PEIE | USART_CR1_TE | USART_CR1_RE;
|
||||
u->CR1 = uartp->config->cr1 | cr1;
|
||||
|
||||
/* Starting the receiver idle loop.*/
|
||||
set_rx_idle_loop(uartp);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief RX DMA common service routine.
|
||||
*
|
||||
* @param[in] uartp pointer to the @p UARTDriver object
|
||||
* @param[in] flags pre-shifted content of the ISR register
|
||||
*/
|
||||
static void uart_lld_serve_rx_end_irq(UARTDriver *uartp, uint32_t flags) {
|
||||
|
||||
/* DMA errors handling.*/
|
||||
#if defined(STM32_UART_DMA_ERROR_HOOK)
|
||||
if ((flags & (STM32_DMA_ISR_TEIF | STM32_DMA_ISR_DMEIF)) != 0) {
|
||||
STM32_UART_DMA_ERROR_HOOK(uartp);
|
||||
}
|
||||
#else
|
||||
(void)flags;
|
||||
#endif
|
||||
|
||||
if (uartp->rxstate == UART_RX_IDLE) {
|
||||
/* Receiver in idle state, a callback is generated, if enabled, for each
|
||||
received character and then the driver stays in the same state.*/
|
||||
if (uartp->config->rxchar_cb != NULL)
|
||||
uartp->config->rxchar_cb(uartp, uartp->rxbuf);
|
||||
}
|
||||
else {
|
||||
/* Receiver in active state, a callback is generated, if enabled, after
|
||||
a completed transfer.*/
|
||||
dmaStreamDisable(uartp->dmarx);
|
||||
uartp->rxstate = UART_RX_COMPLETE;
|
||||
if (uartp->config->rxend_cb != NULL)
|
||||
uartp->config->rxend_cb(uartp);
|
||||
|
||||
/* If the callback didn't explicitly change state then the receiver
|
||||
automatically returns to the idle state.*/
|
||||
if (uartp->rxstate == UART_RX_COMPLETE) {
|
||||
uartp->rxstate = UART_RX_IDLE;
|
||||
set_rx_idle_loop(uartp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief TX DMA common service routine.
|
||||
*
|
||||
* @param[in] uartp pointer to the @p UARTDriver object
|
||||
* @param[in] flags pre-shifted content of the ISR register
|
||||
*/
|
||||
static void uart_lld_serve_tx_end_irq(UARTDriver *uartp, uint32_t flags) {
|
||||
|
||||
/* DMA errors handling.*/
|
||||
#if defined(STM32_UART_DMA_ERROR_HOOK)
|
||||
if ((flags & (STM32_DMA_ISR_TEIF | STM32_DMA_ISR_DMEIF)) != 0) {
|
||||
STM32_UART_DMA_ERROR_HOOK(uartp);
|
||||
}
|
||||
#else
|
||||
(void)flags;
|
||||
#endif
|
||||
|
||||
dmaStreamDisable(uartp->dmatx);
|
||||
|
||||
/* Only enable TC interrupt if there's a callback attached to it.
|
||||
We have to do it here, rather than earlier, because TC flag is set
|
||||
until transmission starts.*/
|
||||
if (uartp->config->txend2_cb != NULL)
|
||||
uartp->usart->CR1 |= USART_CR1_TCIE;
|
||||
|
||||
/* A callback is generated, if enabled, after a completed transfer.*/
|
||||
uartp->txstate = UART_TX_COMPLETE;
|
||||
if (uartp->config->txend1_cb != NULL)
|
||||
uartp->config->txend1_cb(uartp);
|
||||
|
||||
/* If the callback didn't explicitly change state then the transmitter
|
||||
automatically returns to the idle state.*/
|
||||
if (uartp->txstate == UART_TX_COMPLETE)
|
||||
uartp->txstate = UART_TX_IDLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief USART common service routine.
|
||||
*
|
||||
* @param[in] uartp pointer to the @p UARTDriver object
|
||||
*/
|
||||
static void serve_usart_irq(UARTDriver *uartp) {
|
||||
uint32_t isr;
|
||||
USART_TypeDef *u = uartp->usart;
|
||||
uint32_t cr1 = u->CR1;
|
||||
|
||||
/* Reading and clearing status.*/
|
||||
isr = u->ISR;
|
||||
u->ICR = isr;
|
||||
|
||||
if (isr & (USART_ISR_LBD | USART_ISR_ORE | USART_ISR_NE |
|
||||
USART_ISR_FE | USART_ISR_PE)) {
|
||||
if (uartp->config->rxerr_cb != NULL)
|
||||
uartp->config->rxerr_cb(uartp, translate_errors(isr));
|
||||
}
|
||||
|
||||
if ((isr & USART_ISR_TC) && (cr1 & USART_CR1_TCIE)) {
|
||||
/* TC interrupt disabled.*/
|
||||
u->CR1 = cr1 & ~USART_CR1_TCIE;
|
||||
|
||||
/* End of transmission, a callback is generated.*/
|
||||
if (uartp->config->txend2_cb != NULL)
|
||||
uartp->config->txend2_cb(uartp);
|
||||
}
|
||||
}
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver interrupt handlers. */
|
||||
/*===========================================================================*/
|
||||
|
||||
#if STM32_UART_USE_USART1 || defined(__DOXYGEN__)
|
||||
#if !defined(STM32_USART1_HANDLER)
|
||||
#error "STM32_USART1_HANDLER not defined"
|
||||
#endif
|
||||
/**
|
||||
* @brief USART1 IRQ handler.
|
||||
*
|
||||
* @isr
|
||||
*/
|
||||
CH_IRQ_HANDLER(STM32_USART1_HANDLER) {
|
||||
|
||||
CH_IRQ_PROLOGUE();
|
||||
|
||||
serve_usart_irq(&UARTD1);
|
||||
|
||||
CH_IRQ_EPILOGUE();
|
||||
}
|
||||
#endif /* STM32_UART_USE_USART1 */
|
||||
|
||||
#if STM32_UART_USE_USART2 || defined(__DOXYGEN__)
|
||||
#if !defined(STM32_USART2_HANDLER)
|
||||
#error "STM32_USART2_HANDLER not defined"
|
||||
#endif
|
||||
/**
|
||||
* @brief USART2 IRQ handler.
|
||||
*
|
||||
* @isr
|
||||
*/
|
||||
CH_IRQ_HANDLER(STM32_USART2_HANDLER) {
|
||||
|
||||
CH_IRQ_PROLOGUE();
|
||||
|
||||
serve_usart_irq(&UARTD2);
|
||||
|
||||
CH_IRQ_EPILOGUE();
|
||||
}
|
||||
#endif /* STM32_UART_USE_USART2 */
|
||||
|
||||
#if STM32_UART_USE_USART3 || defined(__DOXYGEN__)
|
||||
#if !defined(STM32_USART3_HANDLER)
|
||||
#error "STM32_USART3_HANDLER not defined"
|
||||
#endif
|
||||
/**
|
||||
* @brief USART3 IRQ handler.
|
||||
*
|
||||
* @isr
|
||||
*/
|
||||
CH_IRQ_HANDLER(STM32_USART3_HANDLER) {
|
||||
|
||||
CH_IRQ_PROLOGUE();
|
||||
|
||||
serve_usart_irq(&UARTD3);
|
||||
|
||||
CH_IRQ_EPILOGUE();
|
||||
}
|
||||
#endif /* STM32_UART_USE_USART3 */
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver exported functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Low level UART driver initialization.
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void uart_lld_init(void) {
|
||||
|
||||
#if STM32_UART_USE_USART1
|
||||
uartObjectInit(&UARTD1);
|
||||
UARTD1.usart = USART1;
|
||||
UARTD1.dmamode = STM32_DMA_CR_DMEIE | STM32_DMA_CR_TEIE;
|
||||
UARTD1.dmarx = STM32_DMA_STREAM(STM32_UART_USART1_RX_DMA_STREAM);
|
||||
UARTD1.dmatx = STM32_DMA_STREAM(STM32_UART_USART1_TX_DMA_STREAM);
|
||||
#endif
|
||||
|
||||
#if STM32_UART_USE_USART2
|
||||
uartObjectInit(&UARTD2);
|
||||
UARTD2.usart = USART2;
|
||||
UARTD2.dmamode = STM32_DMA_CR_DMEIE | STM32_DMA_CR_TEIE;
|
||||
UARTD2.dmarx = STM32_DMA_STREAM(STM32_UART_USART2_RX_DMA_STREAM);
|
||||
UARTD2.dmatx = STM32_DMA_STREAM(STM32_UART_USART2_TX_DMA_STREAM);
|
||||
#endif
|
||||
|
||||
#if STM32_UART_USE_USART3
|
||||
uartObjectInit(&UARTD3);
|
||||
UARTD3.usart = USART3;
|
||||
UARTD3.dmamode = STM32_DMA_CR_DMEIE | STM32_DMA_CR_TEIE;
|
||||
UARTD3.dmarx = STM32_DMA_STREAM(STM32_UART_USART3_RX_DMA_STREAM);
|
||||
UARTD3.dmatx = STM32_DMA_STREAM(STM32_UART_USART3_TX_DMA_STREAM);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Configures and activates the UART peripheral.
|
||||
*
|
||||
* @param[in] uartp pointer to the @p UARTDriver object
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void uart_lld_start(UARTDriver *uartp) {
|
||||
|
||||
if (uartp->state == UART_STOP) {
|
||||
#if STM32_UART_USE_USART1
|
||||
if (&UARTD1 == uartp) {
|
||||
bool_t b;
|
||||
b = dmaStreamAllocate(uartp->dmarx,
|
||||
STM32_UART_USART1_IRQ_PRIORITY,
|
||||
(stm32_dmaisr_t)uart_lld_serve_rx_end_irq,
|
||||
(void *)uartp);
|
||||
chDbgAssert(!b, "uart_lld_start(), #1", "stream already allocated");
|
||||
b = dmaStreamAllocate(uartp->dmatx,
|
||||
STM32_UART_USART1_IRQ_PRIORITY,
|
||||
(stm32_dmaisr_t)uart_lld_serve_tx_end_irq,
|
||||
(void *)uartp);
|
||||
chDbgAssert(!b, "uart_lld_start(), #2", "stream already allocated");
|
||||
rccEnableUSART1(FALSE);
|
||||
nvicEnableVector(STM32_USART1_NUMBER,
|
||||
CORTEX_PRIORITY_MASK(STM32_UART_USART1_IRQ_PRIORITY));
|
||||
uartp->dmamode |= STM32_DMA_CR_CHSEL(USART1_RX_DMA_CHANNEL) |
|
||||
STM32_DMA_CR_PL(STM32_UART_USART1_DMA_PRIORITY);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if STM32_UART_USE_USART2
|
||||
if (&UARTD2 == uartp) {
|
||||
bool_t b;
|
||||
b = dmaStreamAllocate(uartp->dmarx,
|
||||
STM32_UART_USART2_IRQ_PRIORITY,
|
||||
(stm32_dmaisr_t)uart_lld_serve_rx_end_irq,
|
||||
(void *)uartp);
|
||||
chDbgAssert(!b, "uart_lld_start(), #3", "stream already allocated");
|
||||
b = dmaStreamAllocate(uartp->dmatx,
|
||||
STM32_UART_USART2_IRQ_PRIORITY,
|
||||
(stm32_dmaisr_t)uart_lld_serve_tx_end_irq,
|
||||
(void *)uartp);
|
||||
chDbgAssert(!b, "uart_lld_start(), #4", "stream already allocated");
|
||||
rccEnableUSART2(FALSE);
|
||||
nvicEnableVector(STM32_USART2_NUMBER,
|
||||
CORTEX_PRIORITY_MASK(STM32_UART_USART2_IRQ_PRIORITY));
|
||||
uartp->dmamode |= STM32_DMA_CR_CHSEL(USART2_RX_DMA_CHANNEL) |
|
||||
STM32_DMA_CR_PL(STM32_UART_USART2_DMA_PRIORITY);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if STM32_UART_USE_USART3
|
||||
if (&UARTD3 == uartp) {
|
||||
bool_t b;
|
||||
b = dmaStreamAllocate(uartp->dmarx,
|
||||
STM32_UART_USART3_IRQ_PRIORITY,
|
||||
(stm32_dmaisr_t)uart_lld_serve_rx_end_irq,
|
||||
(void *)uartp);
|
||||
chDbgAssert(!b, "uart_lld_start(), #5", "stream already allocated");
|
||||
b = dmaStreamAllocate(uartp->dmatx,
|
||||
STM32_UART_USART3_IRQ_PRIORITY,
|
||||
(stm32_dmaisr_t)uart_lld_serve_tx_end_irq,
|
||||
(void *)uartp);
|
||||
chDbgAssert(!b, "uart_lld_start(), #6", "stream already allocated");
|
||||
rccEnableUSART3(FALSE);
|
||||
nvicEnableVector(STM32_USART3_NUMBER,
|
||||
CORTEX_PRIORITY_MASK(STM32_UART_USART3_IRQ_PRIORITY));
|
||||
uartp->dmamode |= STM32_DMA_CR_CHSEL(USART3_RX_DMA_CHANNEL) |
|
||||
STM32_DMA_CR_PL(STM32_UART_USART3_DMA_PRIORITY);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Static DMA setup, the transfer size depends on the USART settings,
|
||||
it is 16 bits if M=1 and PCE=0 else it is 8 bits.*/
|
||||
if ((uartp->config->cr1 & (USART_CR1_M | USART_CR1_PCE)) == USART_CR1_M)
|
||||
uartp->dmamode |= STM32_DMA_CR_PSIZE_HWORD | STM32_DMA_CR_MSIZE_HWORD;
|
||||
dmaStreamSetPeripheral(uartp->dmarx, &uartp->usart->RDR);
|
||||
dmaStreamSetPeripheral(uartp->dmatx, &uartp->usart->TDR);
|
||||
uartp->rxbuf = 0;
|
||||
}
|
||||
|
||||
uartp->rxstate = UART_RX_IDLE;
|
||||
uartp->txstate = UART_TX_IDLE;
|
||||
usart_start(uartp);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Deactivates the UART peripheral.
|
||||
*
|
||||
* @param[in] uartp pointer to the @p UARTDriver object
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void uart_lld_stop(UARTDriver *uartp) {
|
||||
|
||||
if (uartp->state == UART_READY) {
|
||||
usart_stop(uartp);
|
||||
dmaStreamRelease(uartp->dmarx);
|
||||
dmaStreamRelease(uartp->dmatx);
|
||||
|
||||
#if STM32_UART_USE_USART1
|
||||
if (&UARTD1 == uartp) {
|
||||
nvicDisableVector(STM32_USART1_NUMBER);
|
||||
rccDisableUSART1(FALSE);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if STM32_UART_USE_USART2
|
||||
if (&UARTD2 == uartp) {
|
||||
nvicDisableVector(STM32_USART2_NUMBER);
|
||||
rccDisableUSART2(FALSE);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if STM32_UART_USE_USART3
|
||||
if (&UARTD3 == uartp) {
|
||||
nvicDisableVector(STM32_USART3_NUMBER);
|
||||
rccDisableUSART3(FALSE);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Starts a transmission on the UART peripheral.
|
||||
* @note The buffers are organized as uint8_t arrays for data sizes below
|
||||
* or equal to 8 bits else it is organized as uint16_t arrays.
|
||||
*
|
||||
* @param[in] uartp pointer to the @p UARTDriver object
|
||||
* @param[in] n number of data frames to send
|
||||
* @param[in] txbuf the pointer to the transmit buffer
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void uart_lld_start_send(UARTDriver *uartp, size_t n, const void *txbuf) {
|
||||
|
||||
/* TX DMA channel preparation and start.*/
|
||||
dmaStreamSetMemory0(uartp->dmatx, txbuf);
|
||||
dmaStreamSetTransactionSize(uartp->dmatx, n);
|
||||
dmaStreamSetMode(uartp->dmatx, uartp->dmamode | STM32_DMA_CR_DIR_M2P |
|
||||
STM32_DMA_CR_MINC | STM32_DMA_CR_TCIE);
|
||||
dmaStreamEnable(uartp->dmatx);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Stops any ongoing transmission.
|
||||
* @note Stopping a transmission also suppresses the transmission callbacks.
|
||||
*
|
||||
* @param[in] uartp pointer to the @p UARTDriver object
|
||||
*
|
||||
* @return The number of data frames not transmitted by the
|
||||
* stopped transmit operation.
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
size_t uart_lld_stop_send(UARTDriver *uartp) {
|
||||
|
||||
dmaStreamDisable(uartp->dmatx);
|
||||
return dmaStreamGetTransactionSize(uartp->dmatx);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Starts a receive operation on the UART peripheral.
|
||||
* @note The buffers are organized as uint8_t arrays for data sizes below
|
||||
* or equal to 8 bits else it is organized as uint16_t arrays.
|
||||
*
|
||||
* @param[in] uartp pointer to the @p UARTDriver object
|
||||
* @param[in] n number of data frames to send
|
||||
* @param[out] rxbuf the pointer to the receive buffer
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void uart_lld_start_receive(UARTDriver *uartp, size_t n, void *rxbuf) {
|
||||
|
||||
/* Stopping previous activity (idle state).*/
|
||||
dmaStreamDisable(uartp->dmarx);
|
||||
|
||||
/* RX DMA channel preparation and start.*/
|
||||
dmaStreamSetMemory0(uartp->dmarx, rxbuf);
|
||||
dmaStreamSetTransactionSize(uartp->dmarx, n);
|
||||
dmaStreamSetMode(uartp->dmarx, uartp->dmamode | STM32_DMA_CR_DIR_P2M |
|
||||
STM32_DMA_CR_MINC | STM32_DMA_CR_TCIE);
|
||||
dmaStreamEnable(uartp->dmarx);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Stops any ongoing receive operation.
|
||||
* @note Stopping a receive operation also suppresses the receive callbacks.
|
||||
*
|
||||
* @param[in] uartp pointer to the @p UARTDriver object
|
||||
*
|
||||
* @return The number of data frames not received by the
|
||||
* stopped receive operation.
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
size_t uart_lld_stop_receive(UARTDriver *uartp) {
|
||||
size_t n;
|
||||
|
||||
dmaStreamDisable(uartp->dmarx);
|
||||
n = dmaStreamGetTransactionSize(uartp->dmarx);
|
||||
set_rx_idle_loop(uartp);
|
||||
return n;
|
||||
}
|
||||
|
||||
#endif /* HAL_USE_UART */
|
||||
|
||||
/** @} */
|
||||
+458
@@ -0,0 +1,458 @@
|
||||
/*
|
||||
ChibiOS/RT - Copyright (C) 2006-2013 Giovanni Di Sirio
|
||||
|
||||
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 STM32/USARTv2/uart_lld.h
|
||||
* @brief STM32 low level UART driver header.
|
||||
*
|
||||
* @addtogroup UART
|
||||
* @{
|
||||
*/
|
||||
|
||||
#ifndef _UART_LLD_H_
|
||||
#define _UART_LLD_H_
|
||||
|
||||
#if HAL_USE_UART || defined(__DOXYGEN__)
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver constants. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver pre-compile time settings. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @name Configuration options
|
||||
* @{
|
||||
*/
|
||||
/**
|
||||
* @brief UART driver on USART1 enable switch.
|
||||
* @details If set to @p TRUE the support for USART1 is included.
|
||||
* @note The default is @p FALSE.
|
||||
*/
|
||||
#if !defined(STM32_UART_USE_USART1) || defined(__DOXYGEN__)
|
||||
#define STM32_UART_USE_USART1 FALSE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief UART driver on USART2 enable switch.
|
||||
* @details If set to @p TRUE the support for USART2 is included.
|
||||
* @note The default is @p FALSE.
|
||||
*/
|
||||
#if !defined(STM32_UART_USE_USART2) || defined(__DOXYGEN__)
|
||||
#define STM32_UART_USE_USART2 FALSE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief UART driver on USART3 enable switch.
|
||||
* @details If set to @p TRUE the support for USART3 is included.
|
||||
* @note The default is @p FALSE.
|
||||
*/
|
||||
#if !defined(STM32_UART_USE_USART3) || defined(__DOXYGEN__)
|
||||
#define STM32_UART_USE_USART3 FALSE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief USART1 interrupt priority level setting.
|
||||
*/
|
||||
#if !defined(STM32_UART_USART1_IRQ_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define STM32_UART_USART1_IRQ_PRIORITY 12
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief USART2 interrupt priority level setting.
|
||||
*/
|
||||
#if !defined(STM32_UART_USART2_IRQ_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define STM32_UART_USART2_IRQ_PRIORITY 12
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief USART3 interrupt priority level setting.
|
||||
*/
|
||||
#if !defined(STM32_UART_USART3_IRQ_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define STM32_UART_USART3_IRQ_PRIORITY 12
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief USART1 DMA priority (0..3|lowest..highest).
|
||||
* @note The priority level is used for both the TX and RX DMA channels but
|
||||
* because of the channels ordering the RX channel has always priority
|
||||
* over the TX channel.
|
||||
*/
|
||||
#if !defined(STM32_UART_USART1_DMA_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define STM32_UART_USART1_DMA_PRIORITY 0
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief USART2 DMA priority (0..3|lowest..highest).
|
||||
* @note The priority level is used for both the TX and RX DMA channels but
|
||||
* because of the channels ordering the RX channel has always priority
|
||||
* over the TX channel.
|
||||
*/
|
||||
#if !defined(STM32_UART_USART2_DMA_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define STM32_UART_USART2_DMA_PRIORITY 0
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief USART3 DMA priority (0..3|lowest..highest).
|
||||
* @note The priority level is used for both the TX and RX DMA channels but
|
||||
* because of the channels ordering the RX channel has always priority
|
||||
* over the TX channel.
|
||||
*/
|
||||
#if !defined(STM32_UART_USART3_DMA_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define STM32_UART_USART3_DMA_PRIORITY 0
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief USART1 DMA error hook.
|
||||
* @note The default action for DMA errors is a system halt because DMA
|
||||
* error can only happen because programming errors.
|
||||
*/
|
||||
#if !defined(STM32_UART_DMA_ERROR_HOOK) || defined(__DOXYGEN__)
|
||||
#define STM32_UART_DMA_ERROR_HOOK(uartp) chSysHalt()
|
||||
#endif
|
||||
|
||||
#if STM32_ADVANCED_DMA || defined(__DOXYGEN__)
|
||||
|
||||
/**
|
||||
* @brief DMA stream used for USART1 RX operations.
|
||||
* @note This option is only available on platforms with enhanced DMA.
|
||||
*/
|
||||
#if !defined(STM32_UART_USART1_RX_DMA_STREAM) || defined(__DOXYGEN__)
|
||||
#define STM32_UART_USART1_RX_DMA_STREAM STM32_DMA_STREAM_ID(2, 5)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief DMA stream used for USART1 TX operations.
|
||||
* @note This option is only available on platforms with enhanced DMA.
|
||||
*/
|
||||
#if !defined(STM32_UART_USART1_TX_DMA_STREAM) || defined(__DOXYGEN__)
|
||||
#define STM32_UART_USART1_TX_DMA_STREAM STM32_DMA_STREAM_ID(2, 7)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief DMA stream used for USART2 RX operations.
|
||||
* @note This option is only available on platforms with enhanced DMA.
|
||||
*/
|
||||
#if !defined(STM32_UART_USART2_RX_DMA_STREAM) || defined(__DOXYGEN__)
|
||||
#define STM32_UART_USART2_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 5)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief DMA stream used for USART2 TX operations.
|
||||
* @note This option is only available on platforms with enhanced DMA.
|
||||
*/
|
||||
#if !defined(STM32_UART_USART2_TX_DMA_STREAM) || defined(__DOXYGEN__)
|
||||
#define STM32_UART_USART2_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 6)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief DMA stream used for USART3 RX operations.
|
||||
* @note This option is only available on platforms with enhanced DMA.
|
||||
*/
|
||||
#if !defined(STM32_UART_USART3_RX_DMA_STREAM) || defined(__DOXYGEN__)
|
||||
#define STM32_UART_USART3_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 1)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief DMA stream used for USART3 TX operations.
|
||||
* @note This option is only available on platforms with enhanced DMA.
|
||||
*/
|
||||
#if !defined(STM32_UART_USART3_TX_DMA_STREAM) || defined(__DOXYGEN__)
|
||||
#define STM32_UART_USART3_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 3)
|
||||
#endif
|
||||
|
||||
#else /* !STM32_ADVANCED_DMA*/
|
||||
|
||||
#if defined(STM32F0XX)
|
||||
/* Fixed values for STM32F0xx devices.*/
|
||||
#define STM32_UART_USART1_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 3)
|
||||
#define STM32_UART_USART1_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 2)
|
||||
#define STM32_UART_USART2_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 5)
|
||||
#define STM32_UART_USART2_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 4)
|
||||
#endif /* defined(STM32F0XX) */
|
||||
|
||||
#if defined(STM32F30X)|| defined(STM32F37X)
|
||||
/* Fixed values for STM32F3xx devices.*/
|
||||
#define STM32_UART_USART1_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 5)
|
||||
#define STM32_UART_USART1_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 4)
|
||||
#define STM32_UART_USART2_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 6)
|
||||
#define STM32_UART_USART2_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 7)
|
||||
#define STM32_UART_USART3_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 3)
|
||||
#define STM32_UART_USART3_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 2)
|
||||
#endif /* defined(STM32F30X) */
|
||||
|
||||
#endif /* !STM32_ADVANCED_DMA*/
|
||||
/** @} */
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Derived constants and error checks. */
|
||||
/*===========================================================================*/
|
||||
|
||||
#if STM32_UART_USE_USART1 && !STM32_HAS_USART1
|
||||
#error "USART1 not present in the selected device"
|
||||
#endif
|
||||
|
||||
#if STM32_UART_USE_USART2 && !STM32_HAS_USART2
|
||||
#error "USART2 not present in the selected device"
|
||||
#endif
|
||||
|
||||
#if STM32_UART_USE_USART3 && !STM32_HAS_USART3
|
||||
#error "USART3 not present in the selected device"
|
||||
#endif
|
||||
|
||||
#if !STM32_UART_USE_USART1 && !STM32_UART_USE_USART2 && \
|
||||
!STM32_UART_USE_USART3
|
||||
#error "UART driver activated but no USART/UART peripheral assigned"
|
||||
#endif
|
||||
|
||||
#if STM32_UART_USE_USART1 && \
|
||||
!CORTEX_IS_VALID_KERNEL_PRIORITY(STM32_UART_USART1_IRQ_PRIORITY)
|
||||
#error "Invalid IRQ priority assigned to USART1"
|
||||
#endif
|
||||
|
||||
#if STM32_UART_USE_USART2 && \
|
||||
!CORTEX_IS_VALID_KERNEL_PRIORITY(STM32_UART_USART2_IRQ_PRIORITY)
|
||||
#error "Invalid IRQ priority assigned to USART2"
|
||||
#endif
|
||||
|
||||
#if STM32_UART_USE_USART3 && \
|
||||
!CORTEX_IS_VALID_KERNEL_PRIORITY(STM32_UART_USART3_IRQ_PRIORITY)
|
||||
#error "Invalid IRQ priority assigned to USART3"
|
||||
#endif
|
||||
|
||||
#if STM32_UART_USE_USART1 && \
|
||||
!STM32_DMA_IS_VALID_PRIORITY(STM32_UART_USART1_DMA_PRIORITY)
|
||||
#error "Invalid DMA priority assigned to USART1"
|
||||
#endif
|
||||
|
||||
#if STM32_UART_USE_USART2 && \
|
||||
!STM32_DMA_IS_VALID_PRIORITY(STM32_UART_USART2_DMA_PRIORITY)
|
||||
#error "Invalid DMA priority assigned to USART2"
|
||||
#endif
|
||||
|
||||
#if STM32_UART_USE_USART3 && \
|
||||
!STM32_DMA_IS_VALID_PRIORITY(STM32_UART_USART3_DMA_PRIORITY)
|
||||
#error "Invalid DMA priority assigned to USART3"
|
||||
#endif
|
||||
|
||||
#if STM32_UART_USE_USART1 && \
|
||||
!STM32_DMA_IS_VALID_ID(STM32_UART_USART1_RX_DMA_STREAM, \
|
||||
STM32_USART1_RX_DMA_MSK)
|
||||
#error "invalid DMA stream associated to USART1 RX"
|
||||
#endif
|
||||
|
||||
#if STM32_UART_USE_USART1 && \
|
||||
!STM32_DMA_IS_VALID_ID(STM32_UART_USART1_TX_DMA_STREAM, \
|
||||
STM32_USART1_TX_DMA_MSK)
|
||||
#error "invalid DMA stream associated to USART1 TX"
|
||||
#endif
|
||||
|
||||
#if STM32_UART_USE_USART2 && \
|
||||
!STM32_DMA_IS_VALID_ID(STM32_UART_USART2_RX_DMA_STREAM, \
|
||||
STM32_USART2_RX_DMA_MSK)
|
||||
#error "invalid DMA stream associated to USART2 RX"
|
||||
#endif
|
||||
|
||||
#if STM32_UART_USE_USART2 && \
|
||||
!STM32_DMA_IS_VALID_ID(STM32_UART_USART2_TX_DMA_STREAM, \
|
||||
STM32_USART2_TX_DMA_MSK)
|
||||
#error "invalid DMA stream associated to USART2 TX"
|
||||
#endif
|
||||
|
||||
#if STM32_UART_USE_USART3 && \
|
||||
!STM32_DMA_IS_VALID_ID(STM32_UART_USART3_RX_DMA_STREAM, \
|
||||
STM32_USART3_RX_DMA_MSK)
|
||||
#error "invalid DMA stream associated to USART3 RX"
|
||||
#endif
|
||||
|
||||
#if STM32_UART_USE_USART3 && \
|
||||
!STM32_DMA_IS_VALID_ID(STM32_UART_USART3_TX_DMA_STREAM, \
|
||||
STM32_USART3_TX_DMA_MSK)
|
||||
#error "invalid DMA stream associated to USART3 TX"
|
||||
#endif
|
||||
|
||||
#if !defined(STM32_DMA_REQUIRED)
|
||||
#define STM32_DMA_REQUIRED
|
||||
#endif
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver data structures and types. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief UART driver condition flags type.
|
||||
*/
|
||||
typedef uint32_t uartflags_t;
|
||||
|
||||
/**
|
||||
* @brief Structure representing an UART driver.
|
||||
*/
|
||||
typedef struct UARTDriver UARTDriver;
|
||||
|
||||
/**
|
||||
* @brief Generic UART notification callback type.
|
||||
*
|
||||
* @param[in] uartp pointer to the @p UARTDriver object
|
||||
*/
|
||||
typedef void (*uartcb_t)(UARTDriver *uartp);
|
||||
|
||||
/**
|
||||
* @brief Character received UART notification callback type.
|
||||
*
|
||||
* @param[in] uartp pointer to the @p UARTDriver object
|
||||
* @param[in] c received character
|
||||
*/
|
||||
typedef void (*uartccb_t)(UARTDriver *uartp, uint16_t c);
|
||||
|
||||
/**
|
||||
* @brief Receive error UART notification callback type.
|
||||
*
|
||||
* @param[in] uartp pointer to the @p UARTDriver object
|
||||
* @param[in] e receive error mask
|
||||
*/
|
||||
typedef void (*uartecb_t)(UARTDriver *uartp, uartflags_t e);
|
||||
|
||||
/**
|
||||
* @brief Driver configuration structure.
|
||||
* @note It could be empty on some architectures.
|
||||
*/
|
||||
typedef struct {
|
||||
/**
|
||||
* @brief End of transmission buffer callback.
|
||||
*/
|
||||
uartcb_t txend1_cb;
|
||||
/**
|
||||
* @brief Physical end of transmission callback.
|
||||
*/
|
||||
uartcb_t txend2_cb;
|
||||
/**
|
||||
* @brief Receive buffer filled callback.
|
||||
*/
|
||||
uartcb_t rxend_cb;
|
||||
/**
|
||||
* @brief Character received while out if the @p UART_RECEIVE state.
|
||||
*/
|
||||
uartccb_t rxchar_cb;
|
||||
/**
|
||||
* @brief Receive error callback.
|
||||
*/
|
||||
uartecb_t rxerr_cb;
|
||||
/* End of the mandatory fields.*/
|
||||
/**
|
||||
* @brief Bit rate.
|
||||
*/
|
||||
uint32_t speed;
|
||||
/**
|
||||
* @brief Initialization value for the CR1 register.
|
||||
*/
|
||||
uint32_t cr1;
|
||||
/**
|
||||
* @brief Initialization value for the CR2 register.
|
||||
*/
|
||||
uint32_t cr2;
|
||||
/**
|
||||
* @brief Initialization value for the CR3 register.
|
||||
*/
|
||||
uint32_t cr3;
|
||||
} UARTConfig;
|
||||
|
||||
/**
|
||||
* @brief Structure representing an UART driver.
|
||||
*/
|
||||
struct UARTDriver {
|
||||
/**
|
||||
* @brief Driver state.
|
||||
*/
|
||||
uartstate_t state;
|
||||
/**
|
||||
* @brief Transmitter state.
|
||||
*/
|
||||
uarttxstate_t txstate;
|
||||
/**
|
||||
* @brief Receiver state.
|
||||
*/
|
||||
uartrxstate_t rxstate;
|
||||
/**
|
||||
* @brief Current configuration data.
|
||||
*/
|
||||
const UARTConfig *config;
|
||||
#if defined(UART_DRIVER_EXT_FIELDS)
|
||||
UART_DRIVER_EXT_FIELDS
|
||||
#endif
|
||||
/* End of the mandatory fields.*/
|
||||
/**
|
||||
* @brief Pointer to the USART registers block.
|
||||
*/
|
||||
USART_TypeDef *usart;
|
||||
/**
|
||||
* @brief DMA mode bit mask.
|
||||
*/
|
||||
uint32_t dmamode;
|
||||
/**
|
||||
* @brief Receive DMA channel.
|
||||
*/
|
||||
const stm32_dma_stream_t *dmarx;
|
||||
/**
|
||||
* @brief Transmit DMA channel.
|
||||
*/
|
||||
const stm32_dma_stream_t *dmatx;
|
||||
/**
|
||||
* @brief Default receive buffer while into @p UART_RX_IDLE state.
|
||||
*/
|
||||
volatile uint16_t rxbuf;
|
||||
};
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver macros. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* External declarations. */
|
||||
/*===========================================================================*/
|
||||
|
||||
#if STM32_UART_USE_USART1 && !defined(__DOXYGEN__)
|
||||
extern UARTDriver UARTD1;
|
||||
#endif
|
||||
|
||||
#if STM32_UART_USE_USART2 && !defined(__DOXYGEN__)
|
||||
extern UARTDriver UARTD2;
|
||||
#endif
|
||||
|
||||
#if STM32_UART_USE_USART3 && !defined(__DOXYGEN__)
|
||||
extern UARTDriver UARTD3;
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
void uart_lld_init(void);
|
||||
void uart_lld_start(UARTDriver *uartp);
|
||||
void uart_lld_stop(UARTDriver *uartp);
|
||||
void uart_lld_start_send(UARTDriver *uartp, size_t n, const void *txbuf);
|
||||
size_t uart_lld_stop_send(UARTDriver *uartp);
|
||||
void uart_lld_start_receive(UARTDriver *uartp, size_t n, void *rxbuf);
|
||||
size_t uart_lld_stop_receive(UARTDriver *uartp);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* HAL_USE_UART */
|
||||
|
||||
#endif /* _UART_LLD_H_ */
|
||||
|
||||
/** @} */
|
||||
+248
@@ -0,0 +1,248 @@
|
||||
/*
|
||||
ChibiOS/RT - Copyright (C) 2006-2013 Giovanni Di Sirio
|
||||
|
||||
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 stm32_usb.h
|
||||
* @brief STM32 USB registers layout header.
|
||||
* @note This file requires definitions from the ST STM32 header files
|
||||
* stm32f10x.h or stm32l1xx.h.
|
||||
*
|
||||
* @addtogroup USB
|
||||
* @{
|
||||
*/
|
||||
|
||||
#ifndef _STM32_USB_H_
|
||||
#define _STM32_USB_H_
|
||||
|
||||
/**
|
||||
* @brief Number of the available endpoints.
|
||||
* @details This value does not include the endpoint 0 which is always present.
|
||||
*/
|
||||
#define USB_ENDOPOINTS_NUMBER 7
|
||||
|
||||
/**
|
||||
* @brief USB registers block.
|
||||
*/
|
||||
typedef struct {
|
||||
/**
|
||||
* @brief Endpoint registers.
|
||||
*/
|
||||
volatile uint32_t EPR[USB_ENDOPOINTS_NUMBER + 1];
|
||||
/*
|
||||
* @brief Reserved space.
|
||||
*/
|
||||
volatile uint32_t _r20[8];
|
||||
/*
|
||||
* @brief Control Register.
|
||||
*/
|
||||
volatile uint32_t CNTR;
|
||||
/*
|
||||
* @brief Interrupt Status Register.
|
||||
*/
|
||||
volatile uint32_t ISTR;
|
||||
/*
|
||||
* @brief Frame Number Register.
|
||||
*/
|
||||
volatile uint32_t FNR;
|
||||
/*
|
||||
* @brief Device Address Register.
|
||||
*/
|
||||
volatile uint32_t DADDR;
|
||||
/*
|
||||
* @brief Buffer Table Address.
|
||||
*/
|
||||
volatile uint32_t BTABLE;
|
||||
} stm32_usb_t;
|
||||
|
||||
/**
|
||||
* @brief USB descriptor registers block.
|
||||
*/
|
||||
typedef struct {
|
||||
/**
|
||||
* @brief TX buffer offset register.
|
||||
*/
|
||||
volatile uint32_t TXADDR0;
|
||||
/**
|
||||
* @brief TX counter register 0.
|
||||
*/
|
||||
volatile uint16_t TXCOUNT0;
|
||||
/**
|
||||
* @brief TX counter register 1.
|
||||
*/
|
||||
volatile uint16_t TXCOUNT1;
|
||||
/**
|
||||
* @brief RX buffer offset register.
|
||||
*/
|
||||
volatile uint32_t RXADDR0;
|
||||
/**
|
||||
* @brief RX counter register 0.
|
||||
*/
|
||||
volatile uint16_t RXCOUNT0;
|
||||
/**
|
||||
* @brief RX counter register 1.
|
||||
*/
|
||||
volatile uint16_t RXCOUNT1;
|
||||
} stm32_usb_descriptor_t;
|
||||
|
||||
/**
|
||||
* @name Register aliases
|
||||
* @{
|
||||
*/
|
||||
#define RXADDR1 TXADDR0
|
||||
#define TXADDR1 RXADDR0
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @brief USB registers block numeric address.
|
||||
*/
|
||||
#define STM32_USB_BASE (APB1PERIPH_BASE + 0x5C00)
|
||||
|
||||
/**
|
||||
* @brief USB RAM numeric address.
|
||||
*/
|
||||
#define STM32_USBRAM_BASE (APB1PERIPH_BASE + 0x6000)
|
||||
|
||||
/**
|
||||
* @brief Pointer to the USB registers block.
|
||||
*/
|
||||
#define STM32_USB ((stm32_usb_t *)STM32_USB_BASE)
|
||||
|
||||
/**
|
||||
* @brief Pointer to the USB RAM.
|
||||
*/
|
||||
#define STM32_USBRAM ((uint32_t *)STM32_USBRAM_BASE)
|
||||
|
||||
/**
|
||||
* @brief Size of the dedicated packet memory.
|
||||
*/
|
||||
#define USB_PMA_SIZE 512
|
||||
|
||||
/**
|
||||
* @brief Mask of all the toggling bits in the EPR register.
|
||||
*/
|
||||
#define EPR_TOGGLE_MASK (EPR_STAT_TX_MASK | EPR_DTOG_TX | \
|
||||
EPR_STAT_RX_MASK | EPR_DTOG_RX | \
|
||||
EPR_SETUP)
|
||||
|
||||
#define EPR_EA_MASK 0x000F
|
||||
#define EPR_STAT_TX_MASK 0x0030
|
||||
#define EPR_STAT_TX_DIS 0x0000
|
||||
#define EPR_STAT_TX_STALL 0x0010
|
||||
#define EPR_STAT_TX_NAK 0x0020
|
||||
#define EPR_STAT_TX_VALID 0x0030
|
||||
#define EPR_DTOG_TX 0x0040
|
||||
#define EPR_SWBUF_RX EPR_DTOG_TX
|
||||
#define EPR_CTR_TX 0x0080
|
||||
#define EPR_EP_KIND 0x0100
|
||||
#define EPR_EP_DBL_BUF EPR_EP_KIND
|
||||
#define EPR_EP_STATUS_OUT EPR_EP_KIND
|
||||
#define EPR_EP_TYPE_MASK 0x0600
|
||||
#define EPR_EP_TYPE_BULK 0x0000
|
||||
#define EPR_EP_TYPE_CONTROL 0x0200
|
||||
#define EPR_EP_TYPE_ISO 0x0400
|
||||
#define EPR_EP_TYPE_INTERRUPT 0x0600
|
||||
#define EPR_SETUP 0x0800
|
||||
#define EPR_STAT_RX_MASK 0x3000
|
||||
#define EPR_STAT_RX_DIS 0x0000
|
||||
#define EPR_STAT_RX_STALL 0x1000
|
||||
#define EPR_STAT_RX_NAK 0x2000
|
||||
#define EPR_STAT_RX_VALID 0x3000
|
||||
#define EPR_DTOG_RX 0x4000
|
||||
#define EPR_SWBUF_TX EPR_DTOG_RX
|
||||
#define EPR_CTR_RX 0x8000
|
||||
|
||||
#define CNTR_FRES 0x0001
|
||||
#define CNTR_PDWN 0x0002
|
||||
#define CNTR_LP_MODE 0x0004
|
||||
#define CNTR_FSUSP 0x0008
|
||||
#define CNTR_RESUME 0x0010
|
||||
#define CNTR_ESOFM 0x0100
|
||||
#define CNTR_SOFM 0x0200
|
||||
#define CNTR_RESETM 0x0400
|
||||
#define CNTR_SUSPM 0x0800
|
||||
#define CNTR_WKUPM 0x1000
|
||||
#define CNTR_ERRM 0x2000
|
||||
#define CNTR_PMAOVRM 0x4000
|
||||
#define CNTR_CTRM 0x8000
|
||||
|
||||
#define ISTR_EP_ID_MASK 0x000F
|
||||
#define ISTR_DIR 0x0010
|
||||
#define ISTR_ESOF 0x0100
|
||||
#define ISTR_SOF 0x0200
|
||||
#define ISTR_RESET 0x0400
|
||||
#define ISTR_SUSP 0x0800
|
||||
#define ISTR_WKUP 0x1000
|
||||
#define ISTR_ERR 0x2000
|
||||
#define ISTR_PMAOVR 0x4000
|
||||
#define ISTR_CTR 0x8000
|
||||
|
||||
#define FNR_FN_MASK 0x07FF
|
||||
#define FNR_LSOF 0x1800
|
||||
#define FNR_LCK 0x2000
|
||||
#define FNR_RXDM 0x4000
|
||||
#define FNR_RXDP 0x8000
|
||||
|
||||
#define DADDR_ADD_MASK 0x007F
|
||||
#define DADDR_EF 0x0080
|
||||
|
||||
#define RXCOUNT_COUNT_MASK 0x03FF
|
||||
#define TXCOUNT_COUNT_MASK 0x03FF
|
||||
|
||||
#define EPR_CTR_MASK (EPR_CTR_TX | EPR_CTR_RX)
|
||||
|
||||
#define EPR_SET(ep, epr) \
|
||||
STM32_USB->EPR[ep] = ((epr) & ~EPR_TOGGLE_MASK) | EPR_CTR_MASK
|
||||
|
||||
#define EPR_TOGGLE(ep, epr) \
|
||||
STM32_USB->EPR[ep] = (STM32_USB->EPR[ep] ^ ((epr) & EPR_TOGGLE_MASK)) \
|
||||
| EPR_CTR_MASK
|
||||
|
||||
#define EPR_SET_STAT_RX(ep, epr) \
|
||||
STM32_USB->EPR[ep] = ((STM32_USB->EPR[ep] & \
|
||||
~(EPR_TOGGLE_MASK & ~EPR_STAT_RX_MASK)) ^ \
|
||||
(epr)) | EPR_CTR_MASK
|
||||
|
||||
#define EPR_SET_STAT_TX(ep, epr) \
|
||||
STM32_USB->EPR[ep] = ((STM32_USB->EPR[ep] & \
|
||||
~(EPR_TOGGLE_MASK & ~EPR_STAT_TX_MASK)) ^ \
|
||||
(epr)) | EPR_CTR_MASK
|
||||
|
||||
#define EPR_CLEAR_CTR_RX(ep) \
|
||||
STM32_USB->EPR[ep] = (STM32_USB->EPR[ep] & ~EPR_CTR_RX & ~EPR_TOGGLE_MASK)\
|
||||
| EPR_CTR_TX
|
||||
|
||||
#define EPR_CLEAR_CTR_TX(ep) \
|
||||
STM32_USB->EPR[ep] = (STM32_USB->EPR[ep] & ~EPR_CTR_TX & ~EPR_TOGGLE_MASK)\
|
||||
| EPR_CTR_RX
|
||||
|
||||
/**
|
||||
* @brief Returns an endpoint descriptor pointer.
|
||||
*/
|
||||
#define USB_GET_DESCRIPTOR(ep) \
|
||||
((stm32_usb_descriptor_t *)((uint32_t)STM32_USBRAM_BASE + \
|
||||
(uint32_t)STM32_USB->BTABLE * 2 + \
|
||||
(uint32_t)(ep) * \
|
||||
sizeof(stm32_usb_descriptor_t)))
|
||||
|
||||
/**
|
||||
* @brief Converts from a PMA address to a physical address.
|
||||
*/
|
||||
#define USB_ADDR2PTR(addr) \
|
||||
((uint32_t *)((addr) * 2 + STM32_USBRAM_BASE))
|
||||
|
||||
#endif /* _STM32_USB_H_ */
|
||||
|
||||
/** @} */
|
||||
+830
@@ -0,0 +1,830 @@
|
||||
/*
|
||||
ChibiOS/RT - Copyright (C) 2006-2013 Giovanni Di Sirio
|
||||
|
||||
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 STM32/USBv1/usb_lld.c
|
||||
* @brief STM32 USB subsystem low level driver source.
|
||||
*
|
||||
* @addtogroup USB
|
||||
* @{
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "ch.h"
|
||||
#include "hal.h"
|
||||
|
||||
#if HAL_USE_USB || defined(__DOXYGEN__)
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local definitions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
#define BTABLE_ADDR 0x0000
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver exported variables. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/** @brief USB1 driver identifier.*/
|
||||
#if STM32_USB_USE_USB1 || defined(__DOXYGEN__)
|
||||
USBDriver USBD1;
|
||||
#endif
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local variables and types. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief EP0 state.
|
||||
* @note It is an union because IN and OUT endpoints are never used at the
|
||||
* same time for EP0.
|
||||
*/
|
||||
static union {
|
||||
/**
|
||||
* @brief IN EP0 state.
|
||||
*/
|
||||
USBInEndpointState in;
|
||||
/**
|
||||
* @brief OUT EP0 state.
|
||||
*/
|
||||
USBOutEndpointState out;
|
||||
} ep0_state;
|
||||
|
||||
/**
|
||||
* @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,
|
||||
0x40,
|
||||
0x40,
|
||||
&ep0_state.in,
|
||||
&ep0_state.out,
|
||||
1,
|
||||
ep0setup_buffer
|
||||
};
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Resets the packet memory allocator.
|
||||
*
|
||||
* @param[in] usbp pointer to the @p USBDriver object
|
||||
*/
|
||||
static void usb_pm_reset(USBDriver *usbp) {
|
||||
|
||||
/* The first 64 bytes are reserved for the descriptors table. The effective
|
||||
available RAM for endpoint buffers is just 448 bytes.*/
|
||||
usbp->pmnext = 64;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Resets the packet memory allocator.
|
||||
*
|
||||
* @param[in] usbp pointer to the @p USBDriver object
|
||||
* @param[in] size size of the packet buffer to allocate
|
||||
*/
|
||||
static uint32_t usb_pm_alloc(USBDriver *usbp, size_t size) {
|
||||
uint32_t next;
|
||||
|
||||
next = usbp->pmnext;
|
||||
usbp->pmnext += size;
|
||||
chDbgAssert(usbp->pmnext <= USB_PMA_SIZE, "usb_pm_alloc(), #1", "PMA overflow");
|
||||
return next;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Reads from a dedicated packet buffer.
|
||||
*
|
||||
* @param[in] udp pointer to a @p stm32_usb_descriptor_t
|
||||
* @param[out] buf buffer where to copy the packet data
|
||||
* @param[in] n maximum number of bytes to copy. This value must
|
||||
* not exceed the maximum packet size for this endpoint.
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
static void usb_packet_read_to_buffer(stm32_usb_descriptor_t *udp,
|
||||
uint8_t *buf, size_t n) {
|
||||
uint32_t *pmap= USB_ADDR2PTR(udp->RXADDR0);
|
||||
|
||||
n = (n + 1) / 2;
|
||||
while (n > 0) {
|
||||
/* Note, this line relies on the Cortex-M3/M4 ability to perform
|
||||
unaligned word accesses.*/
|
||||
*(uint16_t *)buf = (uint16_t)*pmap++;
|
||||
buf += 2;
|
||||
n--;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Reads from a dedicated packet buffer.
|
||||
*
|
||||
* @param[in] udp pointer to a @p stm32_usb_descriptor_t
|
||||
* @param[in] iqp pointer to an @p InputQueue object
|
||||
* @param[in] n maximum number of bytes to copy. This value must
|
||||
* not exceed the maximum packet size for this endpoint.
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
static void usb_packet_read_to_queue(stm32_usb_descriptor_t *udp,
|
||||
InputQueue *iqp, size_t n) {
|
||||
size_t nhw;
|
||||
uint32_t *pmap= USB_ADDR2PTR(udp->RXADDR0);
|
||||
|
||||
nhw = n / 2;
|
||||
while (nhw > 0) {
|
||||
uint32_t w;
|
||||
|
||||
w = *pmap++;
|
||||
*iqp->q_wrptr++ = (uint8_t)w;
|
||||
if (iqp->q_wrptr >= iqp->q_top)
|
||||
iqp->q_wrptr = iqp->q_buffer;
|
||||
*iqp->q_wrptr++ = (uint8_t)(w >> 8);
|
||||
if (iqp->q_wrptr >= iqp->q_top)
|
||||
iqp->q_wrptr = iqp->q_buffer;
|
||||
nhw--;
|
||||
}
|
||||
/* Last byte for odd numbers.*/
|
||||
if ((n & 1) != 0) {
|
||||
*iqp->q_wrptr++ = (uint8_t)*pmap;
|
||||
if (iqp->q_wrptr >= iqp->q_top)
|
||||
iqp->q_wrptr = iqp->q_buffer;
|
||||
}
|
||||
|
||||
/* Updating queue.*/
|
||||
chSysLockFromIsr();
|
||||
iqp->q_counter += n;
|
||||
while (notempty(&iqp->q_waiting))
|
||||
chSchReadyI(fifo_remove(&iqp->q_waiting))->p_u.rdymsg = Q_OK;
|
||||
chSysUnlockFromIsr();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Writes to a dedicated packet buffer.
|
||||
*
|
||||
* @param[in] udp pointer to a @p stm32_usb_descriptor_t
|
||||
* @param[in] buf buffer where to fetch the packet data
|
||||
* @param[in] n maximum number of bytes to copy. This value must
|
||||
* not exceed the maximum packet size for this endpoint.
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
static void usb_packet_write_from_buffer(stm32_usb_descriptor_t *udp,
|
||||
const uint8_t *buf,
|
||||
size_t n) {
|
||||
uint32_t *pmap = USB_ADDR2PTR(udp->TXADDR0);
|
||||
|
||||
udp->TXCOUNT0 = (uint16_t)n;
|
||||
n = (n + 1) / 2;
|
||||
while (n > 0) {
|
||||
/* Note, this line relies on the Cortex-M3/M4 ability to perform
|
||||
unaligned word accesses.*/
|
||||
*pmap++ = *(uint16_t *)buf;
|
||||
buf += 2;
|
||||
n--;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Writes to a dedicated packet buffer.
|
||||
*
|
||||
* @param[in] udp pointer to a @p stm32_usb_descriptor_t
|
||||
* @param[in] buf buffer where to fetch the packet data
|
||||
* @param[in] n maximum number of bytes to copy. This value must
|
||||
* not exceed the maximum packet size for this endpoint.
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
static void usb_packet_write_from_queue(stm32_usb_descriptor_t *udp,
|
||||
OutputQueue *oqp, size_t n) {
|
||||
size_t nhw;
|
||||
uint32_t *pmap = USB_ADDR2PTR(udp->TXADDR0);
|
||||
|
||||
udp->TXCOUNT0 = (uint16_t)n;
|
||||
nhw = n / 2;
|
||||
while (nhw > 0) {
|
||||
uint32_t w;
|
||||
|
||||
w = (uint32_t)*oqp->q_rdptr++;
|
||||
if (oqp->q_rdptr >= oqp->q_top)
|
||||
oqp->q_rdptr = oqp->q_buffer;
|
||||
w |= (uint32_t)*oqp->q_rdptr++ << 8;
|
||||
if (oqp->q_rdptr >= oqp->q_top)
|
||||
oqp->q_rdptr = oqp->q_buffer;
|
||||
*pmap++ = w;
|
||||
nhw--;
|
||||
}
|
||||
|
||||
/* Last byte for odd numbers.*/
|
||||
if ((n & 1) != 0) {
|
||||
*pmap = (uint32_t)*oqp->q_rdptr++;
|
||||
if (oqp->q_rdptr >= oqp->q_top)
|
||||
oqp->q_rdptr = oqp->q_buffer;
|
||||
}
|
||||
|
||||
/* Updating queue. Note, the lock is done in this unusual way because this
|
||||
function can be called from both ISR and thread context so the kind
|
||||
of lock function to be invoked cannot be decided beforehand.*/
|
||||
port_lock();
|
||||
dbg_enter_lock();
|
||||
|
||||
oqp->q_counter += n;
|
||||
while (notempty(&oqp->q_waiting))
|
||||
chSchReadyI(fifo_remove(&oqp->q_waiting))->p_u.rdymsg = Q_OK;
|
||||
|
||||
dbg_leave_lock();
|
||||
port_unlock();
|
||||
}
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver interrupt handlers. */
|
||||
/*===========================================================================*/
|
||||
|
||||
#if STM32_USB_USE_USB1 || defined(__DOXYGEN__)
|
||||
#if !defined(STM32_USB1_HP_HANDLER)
|
||||
#error "STM32_USB1_HP_HANDLER not defined"
|
||||
#endif
|
||||
/**
|
||||
* @brief USB high priority interrupt handler.
|
||||
*
|
||||
* @isr
|
||||
*/
|
||||
CH_IRQ_HANDLER(STM32_USB1_HP_HANDLER) {
|
||||
|
||||
CH_IRQ_PROLOGUE();
|
||||
|
||||
CH_IRQ_EPILOGUE();
|
||||
}
|
||||
|
||||
#if !defined(STM32_USB1_LP_HANDLER)
|
||||
#error "STM32_USB1_LP_HANDLER not defined"
|
||||
#endif
|
||||
/**
|
||||
* @brief USB low priority interrupt handler.
|
||||
*
|
||||
* @isr
|
||||
*/
|
||||
CH_IRQ_HANDLER(STM32_USB1_LP_HANDLER) {
|
||||
uint32_t istr;
|
||||
USBDriver *usbp = &USBD1;
|
||||
|
||||
CH_IRQ_PROLOGUE();
|
||||
|
||||
istr = STM32_USB->ISTR;
|
||||
|
||||
/* USB bus reset condition handling.*/
|
||||
if (istr & ISTR_RESET) {
|
||||
_usb_reset(usbp);
|
||||
_usb_isr_invoke_event_cb(usbp, USB_EVENT_RESET);
|
||||
STM32_USB->ISTR = ~ISTR_RESET;
|
||||
}
|
||||
|
||||
/* USB bus SUSPEND condition handling.*/
|
||||
if (istr & ISTR_SUSP) {
|
||||
STM32_USB->CNTR |= CNTR_FSUSP;
|
||||
_usb_isr_invoke_event_cb(usbp, USB_EVENT_SUSPEND);
|
||||
#if STM32_USB_LOW_POWER_ON_SUSPEND
|
||||
STM32_USB->CNTR |= CNTR_LP_MODE;
|
||||
#endif
|
||||
STM32_USB->ISTR = ~ISTR_SUSP;
|
||||
}
|
||||
|
||||
/* USB bus WAKEUP condition handling.*/
|
||||
if (istr & ISTR_WKUP) {
|
||||
uint32_t fnr = STM32_USB->FNR;
|
||||
if (!(fnr & FNR_RXDP)) {
|
||||
STM32_USB->CNTR &= ~CNTR_FSUSP;
|
||||
_usb_isr_invoke_event_cb(usbp, USB_EVENT_WAKEUP);
|
||||
}
|
||||
#if STM32_USB_LOW_POWER_ON_SUSPEND
|
||||
else {
|
||||
/* Just noise, going back in SUSPEND mode, reference manual 22.4.5,
|
||||
table 169.*/
|
||||
STM32_USB->CNTR |= CNTR_LP_MODE;
|
||||
}
|
||||
#endif
|
||||
STM32_USB->ISTR = ~ISTR_WKUP;
|
||||
}
|
||||
|
||||
/* SOF handling.*/
|
||||
if (istr & ISTR_SOF) {
|
||||
_usb_isr_invoke_sof_cb(usbp);
|
||||
STM32_USB->ISTR = ~ISTR_SOF;
|
||||
}
|
||||
|
||||
/* Endpoint events handling.*/
|
||||
while (istr & ISTR_CTR) {
|
||||
size_t n;
|
||||
uint32_t ep;
|
||||
uint32_t epr = STM32_USB->EPR[ep = istr & ISTR_EP_ID_MASK];
|
||||
const USBEndpointConfig *epcp = usbp->epc[ep];
|
||||
|
||||
if (epr & EPR_CTR_TX) {
|
||||
size_t transmitted;
|
||||
/* IN endpoint, transmission.*/
|
||||
EPR_CLEAR_CTR_TX(ep);
|
||||
|
||||
transmitted = (size_t)USB_GET_DESCRIPTOR(ep)->TXCOUNT0;
|
||||
epcp->in_state->txcnt += transmitted;
|
||||
n = epcp->in_state->txsize - epcp->in_state->txcnt;
|
||||
if (n > 0) {
|
||||
/* Transfer not completed, there are more packets to send.*/
|
||||
if (n > epcp->in_maxsize)
|
||||
n = epcp->in_maxsize;
|
||||
|
||||
if (epcp->in_state->txqueued)
|
||||
usb_packet_write_from_queue(USB_GET_DESCRIPTOR(ep),
|
||||
epcp->in_state->mode.queue.txqueue,
|
||||
n);
|
||||
else {
|
||||
epcp->in_state->mode.linear.txbuf += transmitted;
|
||||
usb_packet_write_from_buffer(USB_GET_DESCRIPTOR(ep),
|
||||
epcp->in_state->mode.linear.txbuf,
|
||||
n);
|
||||
}
|
||||
chSysLockFromIsr();
|
||||
usb_lld_start_in(usbp, ep);
|
||||
chSysUnlockFromIsr();
|
||||
}
|
||||
else {
|
||||
/* Transfer completed, invokes the callback.*/
|
||||
_usb_isr_invoke_in_cb(usbp, ep);
|
||||
}
|
||||
}
|
||||
if (epr & EPR_CTR_RX) {
|
||||
EPR_CLEAR_CTR_RX(ep);
|
||||
/* OUT endpoint, receive.*/
|
||||
if (epr & EPR_SETUP) {
|
||||
/* Setup packets handling, setup packets are handled using a
|
||||
specific callback.*/
|
||||
_usb_isr_invoke_setup_cb(usbp, ep);
|
||||
}
|
||||
else {
|
||||
stm32_usb_descriptor_t *udp = USB_GET_DESCRIPTOR(ep);
|
||||
n = (size_t)udp->RXCOUNT0 & RXCOUNT_COUNT_MASK;
|
||||
|
||||
/* Reads the packet into the defined buffer.*/
|
||||
if (epcp->out_state->rxqueued)
|
||||
usb_packet_read_to_queue(udp,
|
||||
epcp->out_state->mode.queue.rxqueue,
|
||||
n);
|
||||
else {
|
||||
usb_packet_read_to_buffer(udp,
|
||||
epcp->out_state->mode.linear.rxbuf,
|
||||
n);
|
||||
epcp->out_state->mode.linear.rxbuf += n;
|
||||
}
|
||||
/* Transaction data updated.*/
|
||||
epcp->out_state->rxcnt += n;
|
||||
epcp->out_state->rxsize -= n;
|
||||
epcp->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 ((n < epcp->out_maxsize) || (epcp->out_state->rxpkts == 0)) {
|
||||
/* Transfer complete, invokes the callback.*/
|
||||
_usb_isr_invoke_out_cb(usbp, ep);
|
||||
}
|
||||
else {
|
||||
/* Transfer not complete, there are more packets to receive.*/
|
||||
EPR_SET_STAT_RX(ep, EPR_STAT_RX_VALID);
|
||||
}
|
||||
}
|
||||
}
|
||||
istr = STM32_USB->ISTR;
|
||||
}
|
||||
|
||||
CH_IRQ_EPILOGUE();
|
||||
}
|
||||
#endif
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver exported functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Low level USB driver initialization.
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void usb_lld_init(void) {
|
||||
|
||||
/* Driver initialization.*/
|
||||
usbObjectInit(&USBD1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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) {
|
||||
/* Clock activation.*/
|
||||
#if STM32_USB_USE_USB1
|
||||
if (&USBD1 == usbp) {
|
||||
/* USB clock enabled.*/
|
||||
rccEnableUSB(FALSE);
|
||||
/* Powers up the transceiver while holding the USB in reset state.*/
|
||||
STM32_USB->CNTR = CNTR_FRES;
|
||||
/* Enabling the USB IRQ vectors, this also gives enough time to allow
|
||||
the transceiver power up (1uS).*/
|
||||
nvicEnableVector(STM32_USB1_HP_NUMBER,
|
||||
CORTEX_PRIORITY_MASK(STM32_USB_USB1_HP_IRQ_PRIORITY));
|
||||
nvicEnableVector(STM32_USB1_LP_NUMBER,
|
||||
CORTEX_PRIORITY_MASK(STM32_USB_USB1_LP_IRQ_PRIORITY));
|
||||
/* Releases the USB reset.*/
|
||||
STM32_USB->CNTR = 0;
|
||||
}
|
||||
#endif
|
||||
/* Reset procedure enforced on driver start.*/
|
||||
_usb_reset(usbp);
|
||||
}
|
||||
/* Configuration.*/
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Deactivates the USB peripheral.
|
||||
*
|
||||
* @param[in] usbp pointer to the @p USBDriver object
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void usb_lld_stop(USBDriver *usbp) {
|
||||
|
||||
/* If in ready state then disables the USB clock.*/
|
||||
if (usbp->state == USB_STOP) {
|
||||
#if STM32_USB_USE_USB1
|
||||
if (&USBD1 == usbp) {
|
||||
nvicDisableVector(STM32_USB1_HP_NUMBER);
|
||||
nvicDisableVector(STM32_USB1_LP_NUMBER);
|
||||
STM32_USB->CNTR = CNTR_PDWN | CNTR_FRES;
|
||||
rccDisableUSB(FALSE);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief USB low level reset routine.
|
||||
*
|
||||
* @param[in] usbp pointer to the @p USBDriver object
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void usb_lld_reset(USBDriver *usbp) {
|
||||
uint32_t cntr;
|
||||
|
||||
/* Post reset initialization.*/
|
||||
STM32_USB->BTABLE = 0;
|
||||
STM32_USB->ISTR = 0;
|
||||
STM32_USB->DADDR = DADDR_EF;
|
||||
cntr = /*CNTR_ESOFM | */ CNTR_RESETM | CNTR_SUSPM |
|
||||
CNTR_WKUPM | /*CNTR_ERRM | CNTR_PMAOVRM |*/ CNTR_CTRM;
|
||||
/* The SOF interrupt is only enabled if a callback is defined for
|
||||
this service because it is an high rate source.*/
|
||||
if (usbp->config->sof_cb != NULL)
|
||||
cntr |= CNTR_SOFM;
|
||||
STM32_USB->CNTR = cntr;
|
||||
|
||||
/* Resets the packet memory allocator.*/
|
||||
usb_pm_reset(usbp);
|
||||
|
||||
/* EP0 initialization.*/
|
||||
usbp->epc[0] = &ep0config;
|
||||
usb_lld_init_endpoint(usbp, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the USB address.
|
||||
*
|
||||
* @param[in] usbp pointer to the @p USBDriver object
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void usb_lld_set_address(USBDriver *usbp) {
|
||||
|
||||
STM32_USB->DADDR = (uint32_t)(usbp->address) | DADDR_EF;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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) {
|
||||
uint16_t nblocks, epr;
|
||||
stm32_usb_descriptor_t *dp;
|
||||
const USBEndpointConfig *epcp = usbp->epc[ep];
|
||||
|
||||
/* Setting the endpoint type.*/
|
||||
switch (epcp->ep_mode & USB_EP_MODE_TYPE) {
|
||||
case USB_EP_MODE_TYPE_ISOC:
|
||||
epr = EPR_EP_TYPE_ISO;
|
||||
break;
|
||||
case USB_EP_MODE_TYPE_BULK:
|
||||
epr = EPR_EP_TYPE_BULK;
|
||||
break;
|
||||
case USB_EP_MODE_TYPE_INTR:
|
||||
epr = EPR_EP_TYPE_INTERRUPT;
|
||||
break;
|
||||
default:
|
||||
epr = EPR_EP_TYPE_CONTROL;
|
||||
}
|
||||
|
||||
/* IN endpoint initially in NAK mode.*/
|
||||
if (epcp->in_cb != NULL)
|
||||
epr |= EPR_STAT_TX_NAK;
|
||||
|
||||
/* OUT endpoint initially in NAK mode.*/
|
||||
if (epcp->out_cb != NULL)
|
||||
epr |= EPR_STAT_RX_NAK;
|
||||
|
||||
/* EPxR register setup.*/
|
||||
EPR_SET(ep, epr | ep);
|
||||
EPR_TOGGLE(ep, epr);
|
||||
|
||||
/* Endpoint size and address initialization.*/
|
||||
if (epcp->out_maxsize > 62)
|
||||
nblocks = (((((epcp->out_maxsize - 1) | 0x1f) + 1) / 32) << 10) |
|
||||
0x8000;
|
||||
else
|
||||
nblocks = ((((epcp->out_maxsize - 1) | 1) + 1) / 2) << 10;
|
||||
dp = USB_GET_DESCRIPTOR(ep);
|
||||
dp->TXCOUNT0 = 0;
|
||||
dp->RXCOUNT0 = nblocks;
|
||||
dp->TXADDR0 = usb_pm_alloc(usbp, epcp->in_maxsize);
|
||||
dp->RXADDR0 = usb_pm_alloc(usbp, epcp->out_maxsize);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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) {
|
||||
unsigned i;
|
||||
|
||||
/* Resets the packet memory allocator.*/
|
||||
usb_pm_reset(usbp);
|
||||
|
||||
/* Disabling all endpoints.*/
|
||||
for (i = 1; i <= USB_ENDOPOINTS_NUMBER; i++) {
|
||||
EPR_TOGGLE(i, 0);
|
||||
EPR_SET(i, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @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;
|
||||
switch (STM32_USB->EPR[ep] & EPR_STAT_RX_MASK) {
|
||||
case EPR_STAT_RX_DIS:
|
||||
return EP_STATUS_DISABLED;
|
||||
case EPR_STAT_RX_STALL:
|
||||
return EP_STATUS_STALLED;
|
||||
default:
|
||||
return EP_STATUS_ACTIVE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @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;
|
||||
switch (STM32_USB->EPR[ep] & EPR_STAT_TX_MASK) {
|
||||
case EPR_STAT_TX_DIS:
|
||||
return EP_STATUS_DISABLED;
|
||||
case EPR_STAT_TX_STALL:
|
||||
return EP_STATUS_STALLED;
|
||||
default:
|
||||
return EP_STATUS_ACTIVE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @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) {
|
||||
uint32_t *pmap;
|
||||
stm32_usb_descriptor_t *udp;
|
||||
uint32_t n;
|
||||
|
||||
(void)usbp;
|
||||
udp = USB_GET_DESCRIPTOR(ep);
|
||||
pmap = USB_ADDR2PTR(udp->RXADDR0);
|
||||
for (n = 0; n < 4; n++) {
|
||||
*(uint16_t *)buf = (uint16_t)*pmap++;
|
||||
buf += 2;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Prepares for a receive operation.
|
||||
*
|
||||
* @param[in] usbp pointer to the @p USBDriver object
|
||||
* @param[in] ep endpoint number
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void usb_lld_prepare_receive(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 Prepares for a transmit operation.
|
||||
*
|
||||
* @param[in] usbp pointer to the @p USBDriver object
|
||||
* @param[in] ep endpoint number
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void usb_lld_prepare_transmit(USBDriver *usbp, usbep_t ep) {
|
||||
size_t n;
|
||||
USBInEndpointState *isp = usbp->epc[ep]->in_state;
|
||||
|
||||
/* Transfer initialization.*/
|
||||
n = isp->txsize;
|
||||
if (n > (size_t)usbp->epc[ep]->in_maxsize)
|
||||
n = (size_t)usbp->epc[ep]->in_maxsize;
|
||||
|
||||
if (isp->txqueued)
|
||||
usb_packet_write_from_queue(USB_GET_DESCRIPTOR(ep),
|
||||
isp->mode.queue.txqueue, n);
|
||||
else
|
||||
usb_packet_write_from_buffer(USB_GET_DESCRIPTOR(ep),
|
||||
isp->mode.linear.txbuf, n);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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) {
|
||||
|
||||
(void)usbp;
|
||||
|
||||
EPR_SET_STAT_RX(ep, EPR_STAT_RX_VALID);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Starts a transmit operation on an IN endpoint.
|
||||
*
|
||||
* @param[in] usbp pointer to the @p USBDriver object
|
||||
* @param[in] ep endpoint number
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void usb_lld_start_in(USBDriver *usbp, usbep_t ep) {
|
||||
|
||||
(void)usbp;
|
||||
|
||||
EPR_SET_STAT_TX(ep, EPR_STAT_TX_VALID);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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;
|
||||
|
||||
EPR_SET_STAT_RX(ep, EPR_STAT_RX_STALL);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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;
|
||||
|
||||
EPR_SET_STAT_TX(ep, EPR_STAT_TX_STALL);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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;
|
||||
|
||||
/* Makes sure to not put to NAK an endpoint that is already
|
||||
transferring.*/
|
||||
if ((STM32_USB->EPR[ep] & EPR_STAT_RX_MASK) != EPR_STAT_RX_VALID)
|
||||
EPR_SET_STAT_TX(ep, EPR_STAT_RX_NAK);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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;
|
||||
|
||||
/* Makes sure to not put to NAK an endpoint that is already
|
||||
transferring.*/
|
||||
if ((STM32_USB->EPR[ep] & EPR_STAT_TX_MASK) != EPR_STAT_TX_VALID)
|
||||
EPR_SET_STAT_TX(ep, EPR_STAT_TX_NAK);
|
||||
}
|
||||
|
||||
#endif /* HAL_USE_USB */
|
||||
|
||||
/** @} */
|
||||
+447
@@ -0,0 +1,447 @@
|
||||
/*
|
||||
ChibiOS/RT - Copyright (C) 2006-2013 Giovanni Di Sirio
|
||||
|
||||
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 STM32/USBv1/usb_lld.h
|
||||
* @brief STM32 USB subsystem low level driver header.
|
||||
*
|
||||
* @addtogroup USB
|
||||
* @{
|
||||
*/
|
||||
|
||||
#ifndef _USB_LLD_H_
|
||||
#define _USB_LLD_H_
|
||||
|
||||
#if HAL_USE_USB || defined(__DOXYGEN__)
|
||||
|
||||
#include "stm32_usb.h"
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver constants. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Maximum endpoint address.
|
||||
*/
|
||||
#define USB_MAX_ENDPOINTS USB_ENDOPOINTS_NUMBER
|
||||
|
||||
/**
|
||||
* @brief Status stage handling method.
|
||||
*/
|
||||
#define USB_EP0_STATUS_STAGE USB_EP0_STATUS_STAGE_SW
|
||||
|
||||
/**
|
||||
* @brief This device requires the address change after the status packet.
|
||||
*/
|
||||
#define USB_SET_ADDRESS_MODE USB_LATE_SET_ADDRESS
|
||||
|
||||
/**
|
||||
* @brief Method for set address acknowledge.
|
||||
*/
|
||||
#define USB_SET_ADDRESS_ACK_HANDLING USB_SET_ADDRESS_ACK_SW
|
||||
|
||||
/*===========================================================================*/
|
||||
/* 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(STM32_USB_USE_USB1) || defined(__DOXYGEN__)
|
||||
#define STM32_USB_USE_USB1 FALSE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Enables the USB device low power mode on suspend.
|
||||
*/
|
||||
#if !defined(STM32_USB_LOW_POWER_ON_SUSPEND) || defined(__DOXYGEN__)
|
||||
#define STM32_USB_LOW_POWER_ON_SUSPEND FALSE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief USB1 interrupt priority level setting.
|
||||
*/
|
||||
#if !defined(STM32_USB_USB1_HP_IRQ_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define STM32_USB_USB1_HP_IRQ_PRIORITY 13
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief USB1 interrupt priority level setting.
|
||||
*/
|
||||
#if !defined(STM32_USB_USB1_LP_IRQ_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define STM32_USB_USB1_LP_IRQ_PRIORITY 14
|
||||
#endif
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Derived constants and error checks. */
|
||||
/*===========================================================================*/
|
||||
|
||||
#if STM32_USB_USE_USB1 && !STM32_HAS_USB
|
||||
#error "USB not present in the selected device"
|
||||
#endif
|
||||
|
||||
#if !STM32_USB_USE_USB1
|
||||
#error "USB driver activated but no USB peripheral assigned"
|
||||
#endif
|
||||
|
||||
#if STM32_USB_USE_USB1 && \
|
||||
!CORTEX_IS_VALID_KERNEL_PRIORITY(STM32_USB_USB1_HP_IRQ_PRIORITY)
|
||||
#error "Invalid IRQ priority assigned to USB HP"
|
||||
#endif
|
||||
|
||||
#if STM32_USB_USE_USB1 && \
|
||||
!CORTEX_IS_VALID_KERNEL_PRIORITY(STM32_USB_USB1_LP_IRQ_PRIORITY)
|
||||
#error "Invalid IRQ priority assigned to USB LP"
|
||||
#endif
|
||||
|
||||
#if STM32_USBCLK != 48000000
|
||||
#error "the USB driver requires a 48MHz clock"
|
||||
#endif
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver data structures and types. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Type of an IN endpoint state structure.
|
||||
*/
|
||||
typedef struct {
|
||||
/**
|
||||
* @brief Buffer mode, queue or linear.
|
||||
*/
|
||||
bool_t txqueued;
|
||||
/**
|
||||
* @brief Requested transmit transfer size.
|
||||
*/
|
||||
size_t txsize;
|
||||
/**
|
||||
* @brief Transmitted bytes so far.
|
||||
*/
|
||||
size_t txcnt;
|
||||
union {
|
||||
struct {
|
||||
/**
|
||||
* @brief Pointer to the transmission linear buffer.
|
||||
*/
|
||||
const uint8_t *txbuf;
|
||||
} linear;
|
||||
struct {
|
||||
/**
|
||||
* @brief Pointer to the output queue.
|
||||
*/
|
||||
OutputQueue *txqueue;
|
||||
} queue;
|
||||
/* End of the mandatory fields.*/
|
||||
} mode;
|
||||
} USBInEndpointState;
|
||||
|
||||
/**
|
||||
* @brief Type of an OUT endpoint state structure.
|
||||
*/
|
||||
typedef struct {
|
||||
/**
|
||||
* @brief Buffer mode, queue or linear.
|
||||
*/
|
||||
bool_t rxqueued;
|
||||
/**
|
||||
* @brief Requested receive transfer size.
|
||||
*/
|
||||
size_t rxsize;
|
||||
/**
|
||||
* @brief Received bytes so far.
|
||||
*/
|
||||
size_t rxcnt;
|
||||
union {
|
||||
struct {
|
||||
/**
|
||||
* @brief Pointer to the receive linear buffer.
|
||||
*/
|
||||
uint8_t *rxbuf;
|
||||
} linear;
|
||||
struct {
|
||||
/**
|
||||
* @brief Pointer to the input queue.
|
||||
*/
|
||||
InputQueue *rxqueue;
|
||||
} queue;
|
||||
} mode;
|
||||
/* End of the mandatory fields.*/
|
||||
/**
|
||||
* @brief Number of packets to receive.
|
||||
*/
|
||||
uint16_t rxpkts;
|
||||
} 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 the IN endpoint is not
|
||||
* used.
|
||||
*/
|
||||
usbepcallback_t in_cb;
|
||||
/**
|
||||
* @brief OUT endpoint notification callback.
|
||||
* @details This field must be set to @p NULL if the OUT endpoint is not
|
||||
* used.
|
||||
*/
|
||||
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 structure maintains the state of the IN endpoint.
|
||||
*/
|
||||
USBInEndpointState *in_state;
|
||||
/**
|
||||
* @brief @p USBEndpointState associated to the OUT endpoint.
|
||||
* @details This structure maintains the state of the OUT endpoint.
|
||||
*/
|
||||
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;
|
||||
#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) (STM32_USB->FNR & FNR_FN_MASK)
|
||||
|
||||
/**
|
||||
* @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 Returns the exact size of a received packet.
|
||||
* @pre The OUT endpoint must have been configured in packet 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_packet_size(usbp, ep) \
|
||||
((size_t)USB_GET_DESCRIPTOR(ep)->RXCOUNT & RXCOUNT_COUNT_MASK)
|
||||
|
||||
/*===========================================================================*/
|
||||
/* External declarations. */
|
||||
/*===========================================================================*/
|
||||
|
||||
#if STM32_USB_USE_USB1 && !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_prepare_receive(USBDriver *usbp, usbep_t ep);
|
||||
void usb_lld_prepare_transmit(USBDriver *usbp, usbep_t ep);
|
||||
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 /* _USB_LLD_H_ */
|
||||
|
||||
/** @} */
|
||||
+725
@@ -0,0 +1,725 @@
|
||||
/*
|
||||
ChibiOS/RT - Copyright (C) 2006-2013 Giovanni Di Sirio
|
||||
|
||||
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 STM32/can_lld.c
|
||||
* @brief STM32 CAN subsystem low level driver source.
|
||||
*
|
||||
* @addtogroup CAN
|
||||
* @{
|
||||
*/
|
||||
|
||||
#include "ch.h"
|
||||
#include "hal.h"
|
||||
|
||||
#if HAL_USE_CAN || defined(__DOXYGEN__)
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local definitions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver exported variables. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/** @brief CAN1 driver identifier.*/
|
||||
#if STM32_CAN_USE_CAN1 || defined(__DOXYGEN__)
|
||||
CANDriver CAND1;
|
||||
#endif
|
||||
|
||||
/** @brief CAN2 driver identifier.*/
|
||||
#if STM32_CAN_USE_CAN2 || defined(__DOXYGEN__)
|
||||
CANDriver CAND2;
|
||||
#endif
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local variables and types. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Programs the filters.
|
||||
*
|
||||
* @param[in] can2sb number of the first filter assigned to CAN2
|
||||
* @param[in] num number of entries in the filters array, if zero then
|
||||
* a default filter is programmed
|
||||
* @param[in] cfp pointer to the filters array, can be @p NULL if
|
||||
* (num == 0)
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
static void can_lld_set_filters(uint32_t can2sb,
|
||||
uint32_t num,
|
||||
const CANFilter *cfp) {
|
||||
|
||||
/* Temporarily enabling CAN1 clock.*/
|
||||
rccEnableCAN1(FALSE);
|
||||
|
||||
/* Filters initialization.*/
|
||||
CAN1->FMR = (CAN1->FMR & 0xFFFF0000) | (can2sb << 8) | CAN_FMR_FINIT;
|
||||
if (num > 0) {
|
||||
uint32_t i, fmask;
|
||||
|
||||
/* All filters cleared.*/
|
||||
CAN1->FA1R = 0;
|
||||
CAN1->FM1R = 0;
|
||||
CAN1->FS1R = 0;
|
||||
CAN1->FFA1R = 0;
|
||||
for (i = 0; i < STM32_CAN_MAX_FILTERS; i++) {
|
||||
CAN1->sFilterRegister[i].FR1 = 0;
|
||||
CAN1->sFilterRegister[i].FR2 = 0;
|
||||
}
|
||||
|
||||
/* Scanning the filters array.*/
|
||||
for (i = 0; i < num; i++) {
|
||||
fmask = 1 << cfp->filter;
|
||||
if (cfp->mode)
|
||||
CAN1->FM1R |= fmask;
|
||||
if (cfp->scale)
|
||||
CAN1->FS1R |= fmask;
|
||||
if (cfp->assignment)
|
||||
CAN1->FFA1R |= fmask;
|
||||
CAN1->sFilterRegister[cfp->filter].FR1 = cfp->register1;
|
||||
CAN1->sFilterRegister[cfp->filter].FR2 = cfp->register2;
|
||||
CAN1->FA1R |= fmask;
|
||||
cfp++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Setting up a single default filter that enables everything for both
|
||||
CANs.*/
|
||||
CAN1->sFilterRegister[0].FR1 = 0;
|
||||
CAN1->sFilterRegister[0].FR2 = 0;
|
||||
#if STM32_HAS_CAN2
|
||||
CAN1->sFilterRegister[can2sb].FR1 = 0;
|
||||
CAN1->sFilterRegister[can2sb].FR2 = 0;
|
||||
#endif
|
||||
CAN1->FM1R = 0;
|
||||
CAN1->FFA1R = 0;
|
||||
#if STM32_HAS_CAN2
|
||||
CAN1->FS1R = 1 | (1 << can2sb);
|
||||
CAN1->FA1R = 1 | (1 << can2sb);
|
||||
#else
|
||||
CAN1->FS1R = 1;
|
||||
CAN1->FA1R = 1;
|
||||
#endif
|
||||
}
|
||||
CAN1->FMR &= ~CAN_FMR_FINIT;
|
||||
|
||||
/* Clock disabled, it will be enabled again in can_lld_start().*/
|
||||
rccDisableCAN1(FALSE);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Common TX ISR handler.
|
||||
*
|
||||
* @param[in] canp pointer to the @p CANDriver object
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
static void can_lld_tx_handler(CANDriver *canp) {
|
||||
|
||||
/* No more events until a message is transmitted.*/
|
||||
canp->can->TSR = CAN_TSR_RQCP0 | CAN_TSR_RQCP1 | CAN_TSR_RQCP2;
|
||||
chSysLockFromIsr();
|
||||
while (chSemGetCounterI(&canp->txsem) < 0)
|
||||
chSemSignalI(&canp->txsem);
|
||||
chEvtBroadcastFlagsI(&canp->txempty_event, CAN_MAILBOX_TO_MASK(1));
|
||||
chSysUnlockFromIsr();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Common RX0 ISR handler.
|
||||
*
|
||||
* @param[in] canp pointer to the @p CANDriver object
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
static void can_lld_rx0_handler(CANDriver *canp) {
|
||||
uint32_t rf0r;
|
||||
|
||||
rf0r = canp->can->RF0R;
|
||||
if ((rf0r & CAN_RF0R_FMP0) > 0) {
|
||||
/* No more receive events until the queue 0 has been emptied.*/
|
||||
canp->can->IER &= ~CAN_IER_FMPIE0;
|
||||
chSysLockFromIsr();
|
||||
while (chSemGetCounterI(&canp->rxsem) < 0)
|
||||
chSemSignalI(&canp->rxsem);
|
||||
chEvtBroadcastFlagsI(&canp->rxfull_event, CAN_MAILBOX_TO_MASK(1));
|
||||
chSysUnlockFromIsr();
|
||||
}
|
||||
if ((rf0r & CAN_RF0R_FOVR0) > 0) {
|
||||
/* Overflow events handling.*/
|
||||
canp->can->RF0R = CAN_RF0R_FOVR0;
|
||||
chSysLockFromIsr();
|
||||
chEvtBroadcastFlagsI(&canp->error_event, CAN_OVERFLOW_ERROR);
|
||||
chSysUnlockFromIsr();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Common RX1 ISR handler.
|
||||
*
|
||||
* @param[in] canp pointer to the @p CANDriver object
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
static void can_lld_rx1_handler(CANDriver *canp) {
|
||||
uint32_t rf1r;
|
||||
|
||||
rf1r = canp->can->RF1R;
|
||||
if ((rf1r & CAN_RF1R_FMP1) > 0) {
|
||||
/* No more receive events until the queue 0 has been emptied.*/
|
||||
canp->can->IER &= ~CAN_IER_FMPIE1;
|
||||
chSysLockFromIsr();
|
||||
while (chSemGetCounterI(&canp->rxsem) < 0)
|
||||
chSemSignalI(&canp->rxsem);
|
||||
chEvtBroadcastFlagsI(&canp->rxfull_event, CAN_MAILBOX_TO_MASK(2));
|
||||
chSysUnlockFromIsr();
|
||||
}
|
||||
if ((rf1r & CAN_RF1R_FOVR1) > 0) {
|
||||
/* Overflow events handling.*/
|
||||
canp->can->RF1R = CAN_RF1R_FOVR1;
|
||||
chSysLockFromIsr();
|
||||
chEvtBroadcastFlagsI(&canp->error_event, CAN_OVERFLOW_ERROR);
|
||||
chSysUnlockFromIsr();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Common SCE ISR handler.
|
||||
*
|
||||
* @param[in] canp pointer to the @p CANDriver object
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
static void can_lld_sce_handler(CANDriver *canp) {
|
||||
uint32_t msr;
|
||||
|
||||
msr = canp->can->MSR;
|
||||
canp->can->MSR = CAN_MSR_ERRI | CAN_MSR_WKUI | CAN_MSR_SLAKI;
|
||||
/* Wakeup event.*/
|
||||
#if CAN_USE_SLEEP_MODE
|
||||
if (msr & CAN_MSR_WKUI) {
|
||||
canp->state = CAN_READY;
|
||||
canp->can->MCR &= ~CAN_MCR_SLEEP;
|
||||
chSysLockFromIsr();
|
||||
chEvtBroadcastI(&canp->wakeup_event);
|
||||
chSysUnlockFromIsr();
|
||||
}
|
||||
#endif /* CAN_USE_SLEEP_MODE */
|
||||
/* Error event.*/
|
||||
if (msr & CAN_MSR_ERRI) {
|
||||
flagsmask_t flags;
|
||||
uint32_t esr = canp->can->ESR;
|
||||
|
||||
canp->can->ESR &= ~CAN_ESR_LEC;
|
||||
flags = (flagsmask_t)(esr & 7);
|
||||
if ((esr & CAN_ESR_LEC) > 0)
|
||||
flags |= CAN_FRAMING_ERROR;
|
||||
|
||||
chSysLockFromIsr();
|
||||
/* The content of the ESR register is copied unchanged in the upper
|
||||
half word of the listener flags mask.*/
|
||||
chEvtBroadcastFlagsI(&canp->error_event, flags | (flagsmask_t)(esr << 16));
|
||||
chSysUnlockFromIsr();
|
||||
}
|
||||
}
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver interrupt handlers. */
|
||||
/*===========================================================================*/
|
||||
|
||||
#if STM32_CAN_USE_CAN1 || defined(__DOXYGEN__)
|
||||
/**
|
||||
* @brief CAN1 TX interrupt handler.
|
||||
*
|
||||
* @isr
|
||||
*/
|
||||
CH_IRQ_HANDLER(STM32_CAN1_TX_HANDLER) {
|
||||
|
||||
CH_IRQ_PROLOGUE();
|
||||
|
||||
can_lld_tx_handler(&CAND1);
|
||||
|
||||
CH_IRQ_EPILOGUE();
|
||||
}
|
||||
|
||||
/*
|
||||
* @brief CAN1 RX0 interrupt handler.
|
||||
*
|
||||
* @isr
|
||||
*/
|
||||
CH_IRQ_HANDLER(STM32_CAN1_RX0_HANDLER) {
|
||||
|
||||
CH_IRQ_PROLOGUE();
|
||||
|
||||
can_lld_rx0_handler(&CAND1);
|
||||
|
||||
CH_IRQ_EPILOGUE();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief CAN1 RX1 interrupt handler.
|
||||
*
|
||||
* @isr
|
||||
*/
|
||||
CH_IRQ_HANDLER(STM32_CAN1_RX1_HANDLER) {
|
||||
|
||||
CH_IRQ_PROLOGUE();
|
||||
|
||||
can_lld_rx1_handler(&CAND1);
|
||||
|
||||
CH_IRQ_EPILOGUE();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief CAN1 SCE interrupt handler.
|
||||
*
|
||||
* @isr
|
||||
*/
|
||||
CH_IRQ_HANDLER(STM32_CAN1_SCE_HANDLER) {
|
||||
|
||||
CH_IRQ_PROLOGUE();
|
||||
|
||||
can_lld_sce_handler(&CAND1);
|
||||
|
||||
CH_IRQ_EPILOGUE();
|
||||
}
|
||||
#endif /* STM32_CAN_USE_CAN1 */
|
||||
|
||||
#if STM32_CAN_USE_CAN2 || defined(__DOXYGEN__)
|
||||
/**
|
||||
* @brief CAN2 TX interrupt handler.
|
||||
*
|
||||
* @isr
|
||||
*/
|
||||
CH_IRQ_HANDLER(STM32_CAN2_TX_HANDLER) {
|
||||
|
||||
CH_IRQ_PROLOGUE();
|
||||
|
||||
can_lld_tx_handler(&CAND2);
|
||||
|
||||
CH_IRQ_EPILOGUE();
|
||||
}
|
||||
|
||||
/*
|
||||
* @brief CAN2 RX0 interrupt handler.
|
||||
*
|
||||
* @isr
|
||||
*/
|
||||
CH_IRQ_HANDLER(STM32_CAN2_RX0_HANDLER) {
|
||||
|
||||
CH_IRQ_PROLOGUE();
|
||||
|
||||
can_lld_rx0_handler(&CAND2);
|
||||
|
||||
CH_IRQ_EPILOGUE();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief CAN2 RX1 interrupt handler.
|
||||
*
|
||||
* @isr
|
||||
*/
|
||||
CH_IRQ_HANDLER(STM32_CAN2_RX1_HANDLER) {
|
||||
|
||||
CH_IRQ_PROLOGUE();
|
||||
|
||||
can_lld_rx1_handler(&CAND2);
|
||||
|
||||
CH_IRQ_EPILOGUE();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief CAN2 SCE interrupt handler.
|
||||
*
|
||||
* @isr
|
||||
*/
|
||||
CH_IRQ_HANDLER(STM32_CAN2_SCE_HANDLER) {
|
||||
|
||||
CH_IRQ_PROLOGUE();
|
||||
|
||||
can_lld_sce_handler(&CAND2);
|
||||
|
||||
CH_IRQ_EPILOGUE();
|
||||
}
|
||||
#endif /* STM32_CAN_USE_CAN2 */
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver exported functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Low level CAN driver initialization.
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void can_lld_init(void) {
|
||||
|
||||
#if STM32_CAN_USE_CAN1
|
||||
/* Driver initialization.*/
|
||||
canObjectInit(&CAND1);
|
||||
CAND1.can = CAN1;
|
||||
#endif
|
||||
#if STM32_CAN_USE_CAN2
|
||||
/* Driver initialization.*/
|
||||
canObjectInit(&CAND2);
|
||||
CAND2.can = CAN2;
|
||||
#endif
|
||||
|
||||
/* Filters initialization.*/
|
||||
#if STM32_HAS_CAN2
|
||||
can_lld_set_filters(STM32_CAN_MAX_FILTERS / 2, 0, NULL);
|
||||
#else
|
||||
can_lld_set_filters(STM32_CAN_MAX_FILTERS, 0, NULL);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Configures and activates the CAN peripheral.
|
||||
*
|
||||
* @param[in] canp pointer to the @p CANDriver object
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void can_lld_start(CANDriver *canp) {
|
||||
|
||||
/* Clock activation.*/
|
||||
#if STM32_CAN_USE_CAN1
|
||||
if (&CAND1 == canp) {
|
||||
nvicEnableVector(STM32_CAN1_TX_NUMBER,
|
||||
CORTEX_PRIORITY_MASK(STM32_CAN_CAN1_IRQ_PRIORITY));
|
||||
nvicEnableVector(STM32_CAN1_RX0_NUMBER,
|
||||
CORTEX_PRIORITY_MASK(STM32_CAN_CAN1_IRQ_PRIORITY));
|
||||
nvicEnableVector(STM32_CAN1_RX1_NUMBER,
|
||||
CORTEX_PRIORITY_MASK(STM32_CAN_CAN1_IRQ_PRIORITY));
|
||||
nvicEnableVector(STM32_CAN1_SCE_NUMBER,
|
||||
CORTEX_PRIORITY_MASK(STM32_CAN_CAN1_IRQ_PRIORITY));
|
||||
rccEnableCAN1(FALSE);
|
||||
}
|
||||
#endif
|
||||
#if STM32_CAN_USE_CAN2
|
||||
if (&CAND2 == canp) {
|
||||
|
||||
chDbgAssert(CAND1.state != CAN_STOP,
|
||||
"can_lld_start(), #1", "CAN1 must be started");
|
||||
|
||||
nvicEnableVector(STM32_CAN2_TX_NUMBER,
|
||||
CORTEX_PRIORITY_MASK(STM32_CAN_CAN2_IRQ_PRIORITY));
|
||||
nvicEnableVector(STM32_CAN2_RX0_NUMBER,
|
||||
CORTEX_PRIORITY_MASK(STM32_CAN_CAN2_IRQ_PRIORITY));
|
||||
nvicEnableVector(STM32_CAN2_RX1_NUMBER,
|
||||
CORTEX_PRIORITY_MASK(STM32_CAN_CAN2_IRQ_PRIORITY));
|
||||
nvicEnableVector(STM32_CAN2_SCE_NUMBER,
|
||||
CORTEX_PRIORITY_MASK(STM32_CAN_CAN2_IRQ_PRIORITY));
|
||||
rccEnableCAN2(FALSE);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Entering initialization mode. */
|
||||
canp->state = CAN_STARTING;
|
||||
canp->can->MCR = CAN_MCR_INRQ;
|
||||
while ((canp->can->MSR & CAN_MSR_INAK) == 0)
|
||||
chThdSleepS(1);
|
||||
/* BTR initialization.*/
|
||||
canp->can->BTR = canp->config->btr;
|
||||
/* MCR initialization.*/
|
||||
canp->can->MCR = canp->config->mcr;
|
||||
|
||||
/* Interrupt sources initialization.*/
|
||||
canp->can->IER = CAN_IER_TMEIE | CAN_IER_FMPIE0 | CAN_IER_FMPIE1 |
|
||||
CAN_IER_WKUIE | CAN_IER_ERRIE | CAN_IER_LECIE |
|
||||
CAN_IER_BOFIE | CAN_IER_EPVIE | CAN_IER_EWGIE |
|
||||
CAN_IER_FOVIE0 | CAN_IER_FOVIE1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Deactivates the CAN peripheral.
|
||||
*
|
||||
* @param[in] canp pointer to the @p CANDriver object
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void can_lld_stop(CANDriver *canp) {
|
||||
|
||||
/* If in ready state then disables the CAN peripheral.*/
|
||||
if (canp->state == CAN_READY) {
|
||||
#if STM32_CAN_USE_CAN1
|
||||
if (&CAND1 == canp) {
|
||||
|
||||
#if STM32_CAN_USE_CAN2
|
||||
chDbgAssert(CAND2.state == CAN_STOP,
|
||||
"can_lld_stop(), #1", "CAN2 must be stopped");
|
||||
#endif
|
||||
|
||||
CAN1->MCR = 0x00010002; /* Register reset value. */
|
||||
CAN1->IER = 0x00000000; /* All sources disabled. */
|
||||
nvicDisableVector(STM32_CAN1_TX_NUMBER);
|
||||
nvicDisableVector(STM32_CAN1_RX0_NUMBER);
|
||||
nvicDisableVector(STM32_CAN1_RX1_NUMBER);
|
||||
nvicDisableVector(STM32_CAN1_SCE_NUMBER);
|
||||
rccDisableCAN1(FALSE);
|
||||
}
|
||||
#endif
|
||||
#if STM32_CAN_USE_CAN2
|
||||
if (&CAND2 == canp) {
|
||||
CAN2->MCR = 0x00010002; /* Register reset value. */
|
||||
CAN2->IER = 0x00000000; /* All sources disabled. */
|
||||
nvicDisableVector(STM32_CAN2_TX_NUMBER);
|
||||
nvicDisableVector(STM32_CAN2_RX0_NUMBER);
|
||||
nvicDisableVector(STM32_CAN2_RX1_NUMBER);
|
||||
nvicDisableVector(STM32_CAN2_SCE_NUMBER);
|
||||
rccDisableCAN2(FALSE);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Determines whether a frame can be transmitted.
|
||||
*
|
||||
* @param[in] canp pointer to the @p CANDriver object
|
||||
* @param[in] mailbox mailbox number, @p CAN_ANY_MAILBOX for any mailbox
|
||||
*
|
||||
* @return The queue space availability.
|
||||
* @retval FALSE no space in the transmit queue.
|
||||
* @retval TRUE transmit slot available.
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
bool_t can_lld_is_tx_empty(CANDriver *canp, canmbx_t mailbox) {
|
||||
|
||||
switch (mailbox) {
|
||||
case CAN_ANY_MAILBOX:
|
||||
return (canp->can->TSR & CAN_TSR_TME) != 0;
|
||||
case 1:
|
||||
return (canp->can->TSR & CAN_TSR_TME0) != 0;
|
||||
case 2:
|
||||
return (canp->can->TSR & CAN_TSR_TME1) != 0;
|
||||
case 3:
|
||||
return (canp->can->TSR & CAN_TSR_TME2) != 0;
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Inserts a frame into the transmit queue.
|
||||
*
|
||||
* @param[in] canp pointer to the @p CANDriver object
|
||||
* @param[in] ctfp pointer to the CAN frame to be transmitted
|
||||
* @param[in] mailbox mailbox number, @p CAN_ANY_MAILBOX for any mailbox
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void can_lld_transmit(CANDriver *canp,
|
||||
canmbx_t mailbox,
|
||||
const CANTxFrame *ctfp) {
|
||||
uint32_t tir;
|
||||
CAN_TxMailBox_TypeDef *tmbp;
|
||||
|
||||
/* Pointer to a free transmission mailbox.*/
|
||||
switch (mailbox) {
|
||||
case CAN_ANY_MAILBOX:
|
||||
tmbp = &canp->can->sTxMailBox[(canp->can->TSR & CAN_TSR_CODE) >> 24];
|
||||
break;
|
||||
case 1:
|
||||
tmbp = &canp->can->sTxMailBox[0];
|
||||
break;
|
||||
case 2:
|
||||
tmbp = &canp->can->sTxMailBox[1];
|
||||
break;
|
||||
case 3:
|
||||
tmbp = &canp->can->sTxMailBox[2];
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
/* Preparing the message.*/
|
||||
if (ctfp->IDE)
|
||||
tir = ((uint32_t)ctfp->EID << 3) | ((uint32_t)ctfp->RTR << 1) |
|
||||
CAN_TI0R_IDE;
|
||||
else
|
||||
tir = ((uint32_t)ctfp->SID << 21) | ((uint32_t)ctfp->RTR << 1);
|
||||
tmbp->TDTR = ctfp->DLC;
|
||||
tmbp->TDLR = ctfp->data32[0];
|
||||
tmbp->TDHR = ctfp->data32[1];
|
||||
tmbp->TIR = tir | CAN_TI0R_TXRQ;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Determines whether a frame has been received.
|
||||
*
|
||||
* @param[in] canp pointer to the @p CANDriver object
|
||||
* @param[in] mailbox mailbox number, @p CAN_ANY_MAILBOX for any mailbox
|
||||
*
|
||||
* @return The queue space availability.
|
||||
* @retval FALSE no space in the transmit queue.
|
||||
* @retval TRUE transmit slot available.
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
bool_t can_lld_is_rx_nonempty(CANDriver *canp, canmbx_t mailbox) {
|
||||
|
||||
switch (mailbox) {
|
||||
case CAN_ANY_MAILBOX:
|
||||
return ((canp->can->RF0R & CAN_RF0R_FMP0) != 0 ||
|
||||
(canp->can->RF1R & CAN_RF1R_FMP1) != 0);
|
||||
case 1:
|
||||
return (canp->can->RF0R & CAN_RF0R_FMP0) != 0;
|
||||
case 2:
|
||||
return (canp->can->RF1R & CAN_RF1R_FMP1) != 0;
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Receives a frame from the input queue.
|
||||
*
|
||||
* @param[in] canp pointer to the @p CANDriver object
|
||||
* @param[in] mailbox mailbox number, @p CAN_ANY_MAILBOX for any mailbox
|
||||
* @param[out] crfp pointer to the buffer where the CAN frame is copied
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void can_lld_receive(CANDriver *canp,
|
||||
canmbx_t mailbox,
|
||||
CANRxFrame *crfp) {
|
||||
uint32_t rir, rdtr;
|
||||
|
||||
if (mailbox == CAN_ANY_MAILBOX) {
|
||||
if ((canp->can->RF0R & CAN_RF0R_FMP0) != 0)
|
||||
mailbox = 1;
|
||||
else if ((canp->can->RF1R & CAN_RF1R_FMP1) != 0)
|
||||
mailbox = 2;
|
||||
else {
|
||||
/* Should not happen, do nothing.*/
|
||||
return;
|
||||
}
|
||||
}
|
||||
switch (mailbox) {
|
||||
case 1:
|
||||
/* Fetches the message.*/
|
||||
rir = canp->can->sFIFOMailBox[0].RIR;
|
||||
rdtr = canp->can->sFIFOMailBox[0].RDTR;
|
||||
crfp->data32[0] = canp->can->sFIFOMailBox[0].RDLR;
|
||||
crfp->data32[1] = canp->can->sFIFOMailBox[0].RDHR;
|
||||
|
||||
/* Releases the mailbox.*/
|
||||
canp->can->RF0R = CAN_RF0R_RFOM0;
|
||||
|
||||
/* If the queue is empty re-enables the interrupt in order to generate
|
||||
events again.*/
|
||||
if ((canp->can->RF0R & CAN_RF0R_FMP0) == 0)
|
||||
canp->can->IER |= CAN_IER_FMPIE0;
|
||||
break;
|
||||
case 2:
|
||||
/* Fetches the message.*/
|
||||
rir = canp->can->sFIFOMailBox[1].RIR;
|
||||
rdtr = canp->can->sFIFOMailBox[1].RDTR;
|
||||
crfp->data32[0] = canp->can->sFIFOMailBox[1].RDLR;
|
||||
crfp->data32[1] = canp->can->sFIFOMailBox[1].RDHR;
|
||||
|
||||
/* Releases the mailbox.*/
|
||||
canp->can->RF1R = CAN_RF1R_RFOM1;
|
||||
|
||||
/* If the queue is empty re-enables the interrupt in order to generate
|
||||
events again.*/
|
||||
if ((canp->can->RF1R & CAN_RF1R_FMP1) == 0)
|
||||
canp->can->IER |= CAN_IER_FMPIE1;
|
||||
break;
|
||||
default:
|
||||
/* Should not happen, do nothing.*/
|
||||
return;
|
||||
}
|
||||
|
||||
/* Decodes the various fields in the RX frame.*/
|
||||
crfp->RTR = (rir & CAN_RI0R_RTR) >> 1;
|
||||
crfp->IDE = (rir & CAN_RI0R_IDE) >> 2;
|
||||
if (crfp->IDE)
|
||||
crfp->EID = rir >> 3;
|
||||
else
|
||||
crfp->SID = rir >> 21;
|
||||
crfp->DLC = rdtr & CAN_RDT0R_DLC;
|
||||
crfp->FMI = (uint8_t)(rdtr >> 8);
|
||||
crfp->TIME = (uint16_t)(rdtr >> 16);
|
||||
}
|
||||
|
||||
#if CAN_USE_SLEEP_MODE || defined(__DOXYGEN__)
|
||||
/**
|
||||
* @brief Enters the sleep mode.
|
||||
*
|
||||
* @param[in] canp pointer to the @p CANDriver object
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void can_lld_sleep(CANDriver *canp) {
|
||||
|
||||
canp->can->MCR |= CAN_MCR_SLEEP;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enforces leaving the sleep mode.
|
||||
*
|
||||
* @param[in] canp pointer to the @p CANDriver object
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void can_lld_wakeup(CANDriver *canp) {
|
||||
|
||||
canp->can->MCR &= ~CAN_MCR_SLEEP;
|
||||
}
|
||||
#endif /* CAN_USE_SLEEP_MODE */
|
||||
|
||||
/**
|
||||
* @brief Programs the filters.
|
||||
* @note This is an STM32-specific API.
|
||||
*
|
||||
* @param[in] can2sb number of the first filter assigned to CAN2
|
||||
* @param[in] num number of entries in the filters array, if zero then
|
||||
* a default filter is programmed
|
||||
* @param[in] cfp pointer to the filters array, can be @p NULL if
|
||||
* (num == 0)
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
void canSTM32SetFilters(uint32_t can2sb, uint32_t num, const CANFilter *cfp) {
|
||||
|
||||
chDbgCheck((can2sb >= 1) && (can2sb < STM32_CAN_MAX_FILTERS) &&
|
||||
(num <= STM32_CAN_MAX_FILTERS),
|
||||
"canSTM32SetFilters");
|
||||
|
||||
#if STM32_CAN_USE_CAN1
|
||||
chDbgAssert(CAND1.state == CAN_STOP,
|
||||
"canSTM32SetFilters(), #1", "invalid state");
|
||||
#endif
|
||||
#if STM32_CAN_USE_CAN2
|
||||
chDbgAssert(CAND2.state == CAN_STOP,
|
||||
"canSTM32SetFilters(), #2", "invalid state");
|
||||
#endif
|
||||
|
||||
can_lld_set_filters(can2sb, num, cfp);
|
||||
}
|
||||
|
||||
#endif /* HAL_USE_CAN */
|
||||
|
||||
/** @} */
|
||||
+367
@@ -0,0 +1,367 @@
|
||||
/*
|
||||
ChibiOS/RT - Copyright (C) 2006-2013 Giovanni Di Sirio
|
||||
|
||||
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 STM32/can_lld.h
|
||||
* @brief STM32 CAN subsystem low level driver header.
|
||||
*
|
||||
* @addtogroup CAN
|
||||
* @{
|
||||
*/
|
||||
|
||||
#ifndef _CAN_LLD_H_
|
||||
#define _CAN_LLD_H_
|
||||
|
||||
#if HAL_USE_CAN || defined(__DOXYGEN__)
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver constants. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*
|
||||
* The following macros from the ST header file are replaced with better
|
||||
* equivalents.
|
||||
*/
|
||||
#undef CAN_BTR_BRP
|
||||
#undef CAN_BTR_TS1
|
||||
#undef CAN_BTR_TS2
|
||||
#undef CAN_BTR_SJW
|
||||
|
||||
/**
|
||||
* @brief This switch defines whether the driver implementation supports
|
||||
* a low power switch mode with automatic an wakeup feature.
|
||||
*/
|
||||
#define CAN_SUPPORTS_SLEEP TRUE
|
||||
|
||||
/**
|
||||
* @brief This implementation supports three transmit mailboxes.
|
||||
*/
|
||||
#define CAN_TX_MAILBOXES 3
|
||||
|
||||
/**
|
||||
* @brief This implementation supports two receive mailboxes.
|
||||
*/
|
||||
#define CAN_RX_MAILBOXES 2
|
||||
|
||||
/**
|
||||
* @name CAN registers helper macros
|
||||
* @{
|
||||
*/
|
||||
#define CAN_BTR_BRP(n) (n) /**< @brief BRP field macro.*/
|
||||
#define CAN_BTR_TS1(n) ((n) << 16) /**< @brief TS1 field macro.*/
|
||||
#define CAN_BTR_TS2(n) ((n) << 20) /**< @brief TS2 field macro.*/
|
||||
#define CAN_BTR_SJW(n) ((n) << 24) /**< @brief SJW field macro.*/
|
||||
|
||||
#define CAN_IDE_STD 0 /**< @brief Standard id. */
|
||||
#define CAN_IDE_EXT 1 /**< @brief Extended id. */
|
||||
|
||||
#define CAN_RTR_DATA 0 /**< @brief Data frame. */
|
||||
#define CAN_RTR_REMOTE 1 /**< @brief Remote frame. */
|
||||
/** @} */
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver pre-compile time settings. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @name Configuration options
|
||||
* @{
|
||||
*/
|
||||
/**
|
||||
* @brief CAN1 driver enable switch.
|
||||
* @details If set to @p TRUE the support for CAN1 is included.
|
||||
*/
|
||||
#if !defined(STM32_CAN_USE_CAN1) || defined(__DOXYGEN__)
|
||||
#define STM32_CAN_USE_CAN1 FALSE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief CAN2 driver enable switch.
|
||||
* @details If set to @p TRUE the support for CAN2 is included.
|
||||
*/
|
||||
#if !defined(STM32_CAN_USE_CAN2) || defined(__DOXYGEN__)
|
||||
#define STM32_CAN_USE_CAN2 FALSE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief CAN1 interrupt priority level setting.
|
||||
*/
|
||||
#if !defined(STM32_CAN_CAN1_IRQ_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define STM32_CAN_CAN1_IRQ_PRIORITY 11
|
||||
#endif
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @brief CAN2 interrupt priority level setting.
|
||||
*/
|
||||
#if !defined(STM32_CAN_CAN2_IRQ_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define STM32_CAN_CAN2_IRQ_PRIORITY 11
|
||||
#endif
|
||||
/** @} */
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Derived constants and error checks. */
|
||||
/*===========================================================================*/
|
||||
|
||||
#if STM32_CAN_USE_CAN1 && !STM32_HAS_CAN1
|
||||
#error "CAN1 not present in the selected device"
|
||||
#endif
|
||||
|
||||
#if STM32_CAN_USE_CAN2 && !STM32_HAS_CAN2
|
||||
#error "CAN2 not present in the selected device"
|
||||
#endif
|
||||
|
||||
#if !STM32_CAN_USE_CAN1 && !STM32_CAN_USE_CAN2
|
||||
#error "CAN driver activated but no CAN peripheral assigned"
|
||||
#endif
|
||||
|
||||
#if !STM32_CAN_USE_CAN1 && STM32_CAN_USE_CAN2
|
||||
#error "CAN2 requires CAN1, it cannot operate independently"
|
||||
#endif
|
||||
|
||||
#if CAN_USE_SLEEP_MODE && !CAN_SUPPORTS_SLEEP
|
||||
#error "CAN sleep mode not supported in this architecture"
|
||||
#endif
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver data structures and types. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Type of a transmission mailbox index.
|
||||
*/
|
||||
typedef uint32_t canmbx_t;
|
||||
|
||||
/**
|
||||
* @brief CAN transmission frame.
|
||||
* @note Accessing the frame data as word16 or word32 is not portable because
|
||||
* machine data endianness, it can be still useful for a quick filling.
|
||||
*/
|
||||
typedef struct {
|
||||
struct {
|
||||
uint8_t DLC:4; /**< @brief Data length. */
|
||||
uint8_t RTR:1; /**< @brief Frame type. */
|
||||
uint8_t IDE:1; /**< @brief Identifier type. */
|
||||
};
|
||||
union {
|
||||
struct {
|
||||
uint32_t SID:11; /**< @brief Standard identifier.*/
|
||||
};
|
||||
struct {
|
||||
uint32_t EID:29; /**< @brief Extended identifier.*/
|
||||
};
|
||||
};
|
||||
union {
|
||||
uint8_t data8[8]; /**< @brief Frame data. */
|
||||
uint16_t data16[4]; /**< @brief Frame data. */
|
||||
uint32_t data32[2]; /**< @brief Frame data. */
|
||||
};
|
||||
} CANTxFrame;
|
||||
|
||||
/**
|
||||
* @brief CAN received frame.
|
||||
* @note Accessing the frame data as word16 or word32 is not portable because
|
||||
* machine data endianness, it can be still useful for a quick filling.
|
||||
*/
|
||||
typedef struct {
|
||||
struct {
|
||||
uint8_t FMI; /**< @brief Filter id. */
|
||||
uint16_t TIME; /**< @brief Time stamp. */
|
||||
};
|
||||
struct {
|
||||
uint8_t DLC:4; /**< @brief Data length. */
|
||||
uint8_t RTR:1; /**< @brief Frame type. */
|
||||
uint8_t IDE:1; /**< @brief Identifier type. */
|
||||
};
|
||||
union {
|
||||
struct {
|
||||
uint32_t SID:11; /**< @brief Standard identifier.*/
|
||||
};
|
||||
struct {
|
||||
uint32_t EID:29; /**< @brief Extended identifier.*/
|
||||
};
|
||||
};
|
||||
union {
|
||||
uint8_t data8[8]; /**< @brief Frame data. */
|
||||
uint16_t data16[4]; /**< @brief Frame data. */
|
||||
uint32_t data32[2]; /**< @brief Frame data. */
|
||||
};
|
||||
} CANRxFrame;
|
||||
|
||||
/**
|
||||
* @brief CAN filter.
|
||||
* @note Refer to the STM32 reference manual for info about filters.
|
||||
*/
|
||||
typedef struct {
|
||||
/**
|
||||
* @brief Number of the filter to be programmed.
|
||||
*/
|
||||
uint32_t filter;
|
||||
/**
|
||||
* @brief Filter mode.
|
||||
* @note This bit represent the CAN_FM1R register bit associated to this
|
||||
* filter (0=mask mode, 1=list mode).
|
||||
*/
|
||||
uint32_t mode:1;
|
||||
/**
|
||||
* @brief Filter scale.
|
||||
* @note This bit represent the CAN_FS1R register bit associated to this
|
||||
* filter (0=16 bits mode, 1=32 bits mode).
|
||||
*/
|
||||
uint32_t scale:1;
|
||||
/**
|
||||
* @brief Filter mode.
|
||||
* @note This bit represent the CAN_FFA1R register bit associated to this
|
||||
* filter, must be set to zero in this version of the driver.
|
||||
*/
|
||||
uint32_t assignment:1;
|
||||
/**
|
||||
* @brief Filter register 1 (identifier).
|
||||
*/
|
||||
uint32_t register1;
|
||||
/**
|
||||
* @brief Filter register 2 (mask/identifier depending on mode=0/1).
|
||||
*/
|
||||
uint32_t register2;
|
||||
} CANFilter;
|
||||
|
||||
/**
|
||||
* @brief Driver configuration structure.
|
||||
*/
|
||||
typedef struct {
|
||||
/**
|
||||
* @brief CAN MCR register initialization data.
|
||||
* @note Some bits in this register are enforced by the driver regardless
|
||||
* their status in this field.
|
||||
*/
|
||||
uint32_t mcr;
|
||||
/**
|
||||
* @brief CAN BTR register initialization data.
|
||||
* @note Some bits in this register are enforced by the driver regardless
|
||||
* their status in this field.
|
||||
*/
|
||||
uint32_t btr;
|
||||
} CANConfig;
|
||||
|
||||
/**
|
||||
* @brief Structure representing an CAN driver.
|
||||
*/
|
||||
typedef struct {
|
||||
/**
|
||||
* @brief Driver state.
|
||||
*/
|
||||
canstate_t state;
|
||||
/**
|
||||
* @brief Current configuration data.
|
||||
*/
|
||||
const CANConfig *config;
|
||||
/**
|
||||
* @brief Transmission queue semaphore.
|
||||
*/
|
||||
Semaphore txsem;
|
||||
/**
|
||||
* @brief Receive queue semaphore.
|
||||
*/
|
||||
Semaphore rxsem;
|
||||
/**
|
||||
* @brief One or more frames become available.
|
||||
* @note After broadcasting this event it will not be broadcasted again
|
||||
* until the received frames queue has been completely emptied. It
|
||||
* is <b>not</b> broadcasted for each received frame. It is
|
||||
* responsibility of the application to empty the queue by
|
||||
* repeatedly invoking @p chReceive() when listening to this event.
|
||||
* This behavior minimizes the interrupt served by the system
|
||||
* because CAN traffic.
|
||||
* @note The flags associated to the listeners will indicate which
|
||||
* receive mailboxes become non-empty.
|
||||
*/
|
||||
EventSource rxfull_event;
|
||||
/**
|
||||
* @brief One or more transmission mailbox become available.
|
||||
* @note The flags associated to the listeners will indicate which
|
||||
* transmit mailboxes become empty.
|
||||
*
|
||||
*/
|
||||
EventSource txempty_event;
|
||||
/**
|
||||
* @brief A CAN bus error happened.
|
||||
* @note The flags associated to the listeners will indicate the
|
||||
* error(s) that have occurred.
|
||||
*/
|
||||
EventSource error_event;
|
||||
#if CAN_USE_SLEEP_MODE || defined (__DOXYGEN__)
|
||||
/**
|
||||
* @brief Entering sleep state event.
|
||||
*/
|
||||
EventSource sleep_event;
|
||||
/**
|
||||
* @brief Exiting sleep state event.
|
||||
*/
|
||||
EventSource wakeup_event;
|
||||
#endif /* CAN_USE_SLEEP_MODE */
|
||||
/* End of the mandatory fields.*/
|
||||
/**
|
||||
* @brief Pointer to the CAN registers.
|
||||
*/
|
||||
CAN_TypeDef *can;
|
||||
} CANDriver;
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver macros. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* External declarations. */
|
||||
/*===========================================================================*/
|
||||
|
||||
#if STM32_CAN_USE_CAN1 && !defined(__DOXYGEN__)
|
||||
extern CANDriver CAND1;
|
||||
#endif
|
||||
|
||||
#if STM32_CAN_USE_CAN2 && !defined(__DOXYGEN__)
|
||||
extern CANDriver CAND2;
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
void can_lld_init(void);
|
||||
void can_lld_start(CANDriver *canp);
|
||||
void can_lld_stop(CANDriver *canp);
|
||||
bool_t can_lld_is_tx_empty(CANDriver *canp,
|
||||
canmbx_t mailbox);
|
||||
void can_lld_transmit(CANDriver *canp,
|
||||
canmbx_t mailbox,
|
||||
const CANTxFrame *crfp);
|
||||
bool_t can_lld_is_rx_nonempty(CANDriver *canp,
|
||||
canmbx_t mailbox);
|
||||
void can_lld_receive(CANDriver *canp,
|
||||
canmbx_t mailbox,
|
||||
CANRxFrame *ctfp);
|
||||
#if CAN_USE_SLEEP_MODE
|
||||
void can_lld_sleep(CANDriver *canp);
|
||||
void can_lld_wakeup(CANDriver *canp);
|
||||
#endif /* CAN_USE_SLEEP_MODE */
|
||||
void canSTM32SetFilters(uint32_t can2sb, uint32_t num, const CANFilter *cfp);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* HAL_USE_CAN */
|
||||
|
||||
#endif /* _CAN_LLD_H_ */
|
||||
|
||||
/** @} */
|
||||
+223
@@ -0,0 +1,223 @@
|
||||
/*
|
||||
ChibiOS/RT - Copyright (C) 2006-2013 Giovanni Di Sirio
|
||||
|
||||
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 STM32/ext_lld.c
|
||||
* @brief STM32 EXT subsystem low level driver source.
|
||||
*
|
||||
* @addtogroup EXT
|
||||
* @{
|
||||
*/
|
||||
|
||||
#include "ch.h"
|
||||
#include "hal.h"
|
||||
|
||||
#if HAL_USE_EXT || defined(__DOXYGEN__)
|
||||
|
||||
#include "ext_lld_isr.h"
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local definitions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver exported variables. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief EXTD1 driver identifier.
|
||||
*/
|
||||
EXTDriver EXTD1;
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local variables and types. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver interrupt handlers. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver exported functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Low level EXT driver initialization.
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void ext_lld_init(void) {
|
||||
|
||||
/* Driver initialization.*/
|
||||
extObjectInit(&EXTD1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Configures and activates the EXT peripheral.
|
||||
*
|
||||
* @param[in] extp pointer to the @p EXTDriver object
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void ext_lld_start(EXTDriver *extp) {
|
||||
unsigned i;
|
||||
|
||||
if (extp->state == EXT_STOP)
|
||||
ext_lld_exti_irq_enable();
|
||||
|
||||
/* Configuration of automatic channels.*/
|
||||
for (i = 0; i < EXT_MAX_CHANNELS; i++)
|
||||
if (extp->config->channels[i].mode & EXT_CH_MODE_AUTOSTART)
|
||||
ext_lld_channel_enable(extp, i);
|
||||
else
|
||||
ext_lld_channel_disable(extp, i);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Deactivates the EXT peripheral.
|
||||
*
|
||||
* @param[in] extp pointer to the @p EXTDriver object
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void ext_lld_stop(EXTDriver *extp) {
|
||||
|
||||
if (extp->state == EXT_ACTIVE)
|
||||
ext_lld_exti_irq_disable();
|
||||
|
||||
EXTI->EMR = 0;
|
||||
EXTI->IMR = 0;
|
||||
EXTI->PR = 0xFFFFFFFF;
|
||||
#if STM32_EXTI_NUM_CHANNELS > 32
|
||||
EXTI->PR2 = 0xFFFFFFFF;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enables an EXT channel.
|
||||
*
|
||||
* @param[in] extp pointer to the @p EXTDriver object
|
||||
* @param[in] channel channel to be enabled
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void ext_lld_channel_enable(EXTDriver *extp, expchannel_t channel) {
|
||||
|
||||
/* Setting the associated GPIO for external channels.*/
|
||||
if (channel < 16) {
|
||||
uint32_t n = channel >> 2;
|
||||
uint32_t mask = ~(0xF << ((channel & 3) * 4));
|
||||
uint32_t port = ((extp->config->channels[channel].mode &
|
||||
EXT_MODE_GPIO_MASK) >>
|
||||
EXT_MODE_GPIO_OFF) << ((channel & 3) * 4);
|
||||
|
||||
#if defined(STM32F10X_LD_VL) || defined(STM32F10X_MD_VL) || \
|
||||
defined(STM32F10X_HD_VL) || defined(STM32F10X_LD) || \
|
||||
defined(STM32F10X_MD) || defined(STM32F10X_HD) || \
|
||||
defined(STM32F10X_XL) || defined(STM32F10X_CL)
|
||||
AFIO->EXTICR[n] = (AFIO->EXTICR[n] & mask) | port;
|
||||
#else /* !defined(STM32F1XX) */
|
||||
SYSCFG->EXTICR[n] = (SYSCFG->EXTICR[n] & mask) | port;
|
||||
#endif /* !defined(STM32F1XX) */
|
||||
}
|
||||
|
||||
#if STM32_EXTI_NUM_CHANNELS > 32
|
||||
if (channel < 32) {
|
||||
#endif
|
||||
/* Programming edge registers.*/
|
||||
if (extp->config->channels[channel].mode & EXT_CH_MODE_RISING_EDGE)
|
||||
EXTI->RTSR |= (1 << channel);
|
||||
else
|
||||
EXTI->RTSR &= ~(1 << channel);
|
||||
if (extp->config->channels[channel].mode & EXT_CH_MODE_FALLING_EDGE)
|
||||
EXTI->FTSR |= (1 << channel);
|
||||
else
|
||||
EXTI->FTSR &= ~(1 << channel);
|
||||
|
||||
/* Programming interrupt and event registers.*/
|
||||
if (extp->config->channels[channel].cb != NULL) {
|
||||
EXTI->IMR |= (1 << channel);
|
||||
EXTI->EMR &= ~(1 << channel);
|
||||
}
|
||||
else {
|
||||
EXTI->EMR |= (1 << channel);
|
||||
EXTI->IMR &= ~(1 << channel);
|
||||
}
|
||||
#if STM32_EXTI_NUM_CHANNELS > 32
|
||||
}
|
||||
else {
|
||||
/* Programming edge registers.*/
|
||||
if (extp->config->channels[channel].mode & EXT_CH_MODE_RISING_EDGE)
|
||||
EXTI->RTSR2 |= (1 << (32 - channel));
|
||||
else
|
||||
EXTI->RTSR2 &= ~(1 << (32 - channel));
|
||||
if (extp->config->channels[channel].mode & EXT_CH_MODE_FALLING_EDGE)
|
||||
EXTI->FTSR2 |= (1 << (32 - channel));
|
||||
else
|
||||
EXTI->FTSR2 &= ~(1 << (32 - channel));
|
||||
|
||||
/* Programming interrupt and event registers.*/
|
||||
if (extp->config->channels[channel].cb != NULL) {
|
||||
EXTI->IMR2 |= (1 << (32 - channel));
|
||||
EXTI->EMR2 &= ~(1 << (32 - channel));
|
||||
}
|
||||
else {
|
||||
EXTI->EMR2 |= (1 << (32 - channel));
|
||||
EXTI->IMR2 &= ~(1 << (32 - channel));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Disables an EXT channel.
|
||||
*
|
||||
* @param[in] extp pointer to the @p EXTDriver object
|
||||
* @param[in] channel channel to be disabled
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void ext_lld_channel_disable(EXTDriver *extp, expchannel_t channel) {
|
||||
|
||||
(void)extp;
|
||||
|
||||
#if STM32_EXTI_NUM_CHANNELS > 32
|
||||
if (channel < 32) {
|
||||
#endif
|
||||
EXTI->IMR &= ~(1 << channel);
|
||||
EXTI->EMR &= ~(1 << channel);
|
||||
EXTI->RTSR &= ~(1 << channel);
|
||||
EXTI->FTSR &= ~(1 << channel);
|
||||
EXTI->PR = (1 << channel);
|
||||
#if STM32_EXTI_NUM_CHANNELS > 32
|
||||
}
|
||||
else {
|
||||
EXTI->IMR2 &= ~(1 << (32 - channel));
|
||||
EXTI->EMR2 &= ~(1 << (32 - channel));
|
||||
EXTI->RTSR2 &= ~(1 << (32 - channel));
|
||||
EXTI->FTSR2 &= ~(1 << (32 - channel));
|
||||
EXTI->PR2 = (1 << (32 - channel));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif /* HAL_USE_EXT */
|
||||
|
||||
/** @} */
|
||||
+153
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
ChibiOS/RT - Copyright (C) 2006-2013 Giovanni Di Sirio
|
||||
|
||||
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 STM32/ext_lld.h
|
||||
* @brief STM32 EXT subsystem low level driver header.
|
||||
*
|
||||
* @addtogroup EXT
|
||||
* @{
|
||||
*/
|
||||
|
||||
#ifndef _EXT_LLD_H_
|
||||
#define _EXT_LLD_H_
|
||||
|
||||
#if HAL_USE_EXT || defined(__DOXYGEN__)
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver constants. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Available number of EXT channels.
|
||||
*/
|
||||
#define EXT_MAX_CHANNELS STM32_EXTI_NUM_CHANNELS
|
||||
|
||||
/**
|
||||
* @name STM32-specific EXT channel modes
|
||||
* @{
|
||||
*/
|
||||
#define EXT_MODE_GPIO_MASK 0xF0 /**< @brief Port field mask. */
|
||||
#define EXT_MODE_GPIO_OFF 4 /**< @brief Port field offset. */
|
||||
#define EXT_MODE_GPIOA 0x00 /**< @brief GPIOA identifier. */
|
||||
#define EXT_MODE_GPIOB 0x10 /**< @brief GPIOB identifier. */
|
||||
#define EXT_MODE_GPIOC 0x20 /**< @brief GPIOC identifier. */
|
||||
#define EXT_MODE_GPIOD 0x30 /**< @brief GPIOD identifier. */
|
||||
#define EXT_MODE_GPIOE 0x40 /**< @brief GPIOE identifier. */
|
||||
#define EXT_MODE_GPIOF 0x50 /**< @brief GPIOF identifier. */
|
||||
#define EXT_MODE_GPIOG 0x60 /**< @brief GPIOG identifier. */
|
||||
#define EXT_MODE_GPIOH 0x70 /**< @brief GPIOH identifier. */
|
||||
#define EXT_MODE_GPIOI 0x80 /**< @brief GPIOI identifier. */
|
||||
/** @} */
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver pre-compile time settings. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Derived constants and error checks. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver data structures and types. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief EXT channel identifier.
|
||||
*/
|
||||
typedef uint32_t expchannel_t;
|
||||
|
||||
/**
|
||||
* @brief Type of an EXT generic notification callback.
|
||||
*
|
||||
* @param[in] extp pointer to the @p EXPDriver object triggering the
|
||||
* callback
|
||||
*/
|
||||
typedef void (*extcallback_t)(EXTDriver *extp, expchannel_t channel);
|
||||
|
||||
/**
|
||||
* @brief Channel configuration structure.
|
||||
*/
|
||||
typedef struct {
|
||||
/**
|
||||
* @brief Channel mode.
|
||||
*/
|
||||
uint32_t mode;
|
||||
/**
|
||||
* @brief Channel callback.
|
||||
* @details In the STM32 implementation a @p NULL callback pointer is
|
||||
* valid and configures the channel as an event sources instead
|
||||
* of an interrupt source.
|
||||
*/
|
||||
extcallback_t cb;
|
||||
} EXTChannelConfig;
|
||||
|
||||
/**
|
||||
* @brief Driver configuration structure.
|
||||
* @note It could be empty on some architectures.
|
||||
*/
|
||||
typedef struct {
|
||||
/**
|
||||
* @brief Channel configurations.
|
||||
*/
|
||||
EXTChannelConfig channels[EXT_MAX_CHANNELS];
|
||||
/* End of the mandatory fields.*/
|
||||
} EXTConfig;
|
||||
|
||||
/**
|
||||
* @brief Structure representing an EXT driver.
|
||||
*/
|
||||
struct EXTDriver {
|
||||
/**
|
||||
* @brief Driver state.
|
||||
*/
|
||||
extstate_t state;
|
||||
/**
|
||||
* @brief Current configuration data.
|
||||
*/
|
||||
const EXTConfig *config;
|
||||
/* End of the mandatory fields.*/
|
||||
};
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver macros. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* External declarations. */
|
||||
/*===========================================================================*/
|
||||
|
||||
#if !defined(__DOXYGEN__)
|
||||
extern EXTDriver EXTD1;
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
void ext_lld_init(void);
|
||||
void ext_lld_start(EXTDriver *extp);
|
||||
void ext_lld_stop(EXTDriver *extp);
|
||||
void ext_lld_channel_enable(EXTDriver *extp, expchannel_t channel);
|
||||
void ext_lld_channel_disable(EXTDriver *extp, expchannel_t channel);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* HAL_USE_EXT */
|
||||
|
||||
#endif /* _EXT_LLD_H_ */
|
||||
|
||||
/** @} */
|
||||
+745
@@ -0,0 +1,745 @@
|
||||
/*
|
||||
ChibiOS/RT - Copyright (C) 2006-2013 Giovanni Di Sirio
|
||||
|
||||
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 STM32/mac_lld.c
|
||||
* @brief STM32 low level MAC driver code.
|
||||
*
|
||||
* @addtogroup MAC
|
||||
* @{
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "ch.h"
|
||||
#include "hal.h"
|
||||
#include "mii.h"
|
||||
|
||||
#if HAL_USE_MAC || defined(__DOXYGEN__)
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local definitions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
#define BUFFER_SIZE ((((STM32_MAC_BUFFERS_SIZE - 1) | 3) + 1) / 4)
|
||||
|
||||
/* MII divider optimal value.*/
|
||||
#if (STM32_HCLK >= 150000000)
|
||||
#define MACMIIDR_CR ETH_MACMIIAR_CR_Div102
|
||||
#elif (STM32_HCLK >= 100000000)
|
||||
#define MACMIIDR_CR ETH_MACMIIAR_CR_Div62
|
||||
#elif (STM32_HCLK >= 60000000)
|
||||
#define MACMIIDR_CR ETH_MACMIIAR_CR_Div42
|
||||
#elif (STM32_HCLK >= 35000000)
|
||||
#define MACMIIDR_CR ETH_MACMIIAR_CR_Div26
|
||||
#elif (STM32_HCLK >= 20000000)
|
||||
#define MACMIIDR_CR ETH_MACMIIAR_CR_Div16
|
||||
#else
|
||||
#error "STM32_HCLK below minimum frequency for ETH operations (20MHz)"
|
||||
#endif
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver exported variables. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Ethernet driver 1.
|
||||
*/
|
||||
MACDriver ETHD1;
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local variables and types. */
|
||||
/*===========================================================================*/
|
||||
|
||||
static const uint8_t default_mac_address[] = {0xAA, 0x55, 0x13,
|
||||
0x37, 0x01, 0x10};
|
||||
|
||||
static stm32_eth_rx_descriptor_t rd[STM32_MAC_RECEIVE_BUFFERS];
|
||||
static stm32_eth_tx_descriptor_t td[STM32_MAC_TRANSMIT_BUFFERS];
|
||||
|
||||
static uint32_t rb[STM32_MAC_RECEIVE_BUFFERS][BUFFER_SIZE];
|
||||
static uint32_t tb[STM32_MAC_TRANSMIT_BUFFERS][BUFFER_SIZE];
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Writes a PHY register.
|
||||
*
|
||||
* @param[in] macp pointer to the @p MACDriver object
|
||||
* @param[in] reg register number
|
||||
* @param[in] value new register value
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void mii_write(MACDriver *macp, uint32_t reg, uint32_t value) {
|
||||
|
||||
ETH->MACMIIDR = value;
|
||||
ETH->MACMIIAR = macp->phyaddr | (reg << 6) | MACMIIDR_CR |
|
||||
ETH_MACMIIAR_MW | ETH_MACMIIAR_MB;
|
||||
while ((ETH->MACMIIAR & ETH_MACMIIAR_MB) != 0)
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Reads a PHY register.
|
||||
*
|
||||
* @param[in] macp pointer to the @p MACDriver object
|
||||
* @param[in] reg register number
|
||||
*
|
||||
* @return The PHY register content.
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
uint32_t mii_read(MACDriver *macp, uint32_t reg) {
|
||||
|
||||
ETH->MACMIIAR = macp->phyaddr | (reg << 6) | MACMIIDR_CR | ETH_MACMIIAR_MB;
|
||||
while ((ETH->MACMIIAR & ETH_MACMIIAR_MB) != 0)
|
||||
;
|
||||
return ETH->MACMIIDR;
|
||||
}
|
||||
|
||||
#if !defined(BOARD_PHY_ADDRESS)
|
||||
/**
|
||||
* @brief PHY address detection.
|
||||
*
|
||||
* @param[in] macp pointer to the @p MACDriver object
|
||||
*/
|
||||
static void mii_find_phy(MACDriver *macp) {
|
||||
uint32_t i;
|
||||
|
||||
#if STM32_MAC_PHY_TIMEOUT > 0
|
||||
halrtcnt_t start = halGetCounterValue();
|
||||
halrtcnt_t timeout = start + MS2RTT(STM32_MAC_PHY_TIMEOUT);
|
||||
while (halIsCounterWithin(start, timeout)) {
|
||||
#endif
|
||||
for (i = 0; i < 31; i++) {
|
||||
macp->phyaddr = i << 11;
|
||||
ETH->MACMIIDR = (i << 6) | MACMIIDR_CR;
|
||||
if ((mii_read(macp, MII_PHYSID1) == (BOARD_PHY_ID >> 16)) &&
|
||||
((mii_read(macp, MII_PHYSID2) & 0xFFF0) == (BOARD_PHY_ID & 0xFFF0))) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
#if STM32_MAC_PHY_TIMEOUT > 0
|
||||
}
|
||||
#endif
|
||||
/* Wrong or defective board.*/
|
||||
chSysHalt();
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief MAC address setup.
|
||||
*
|
||||
* @param[in] p pointer to a six bytes buffer containing the MAC
|
||||
* address
|
||||
*/
|
||||
static void mac_lld_set_address(const uint8_t *p) {
|
||||
|
||||
/* MAC address configuration, only a single address comparator is used,
|
||||
hash table not used.*/
|
||||
ETH->MACA0HR = ((uint32_t)p[5] << 8) |
|
||||
((uint32_t)p[4] << 0);
|
||||
ETH->MACA0LR = ((uint32_t)p[3] << 24) |
|
||||
((uint32_t)p[2] << 16) |
|
||||
((uint32_t)p[1] << 8) |
|
||||
((uint32_t)p[0] << 0);
|
||||
ETH->MACA1HR = 0x0000FFFF;
|
||||
ETH->MACA1LR = 0xFFFFFFFF;
|
||||
ETH->MACA2HR = 0x0000FFFF;
|
||||
ETH->MACA2LR = 0xFFFFFFFF;
|
||||
ETH->MACA3HR = 0x0000FFFF;
|
||||
ETH->MACA3LR = 0xFFFFFFFF;
|
||||
ETH->MACHTHR = 0;
|
||||
ETH->MACHTLR = 0;
|
||||
}
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver interrupt handlers. */
|
||||
/*===========================================================================*/
|
||||
|
||||
CH_IRQ_HANDLER(ETH_IRQHandler) {
|
||||
uint32_t dmasr;
|
||||
|
||||
CH_IRQ_PROLOGUE();
|
||||
|
||||
dmasr = ETH->DMASR;
|
||||
ETH->DMASR = dmasr; /* Clear status bits.*/
|
||||
|
||||
if (dmasr & ETH_DMASR_RS) {
|
||||
/* Data Received.*/
|
||||
chSysLockFromIsr();
|
||||
chSemResetI(ÐD1.rdsem, 0);
|
||||
#if MAC_USE_EVENTS
|
||||
chEvtBroadcastI(ÐD1.rdevent);
|
||||
#endif
|
||||
chSysUnlockFromIsr();
|
||||
}
|
||||
|
||||
if (dmasr & ETH_DMASR_TS) {
|
||||
/* Data Transmitted.*/
|
||||
chSysLockFromIsr();
|
||||
chSemResetI(ÐD1.tdsem, 0);
|
||||
chSysUnlockFromIsr();
|
||||
}
|
||||
|
||||
CH_IRQ_EPILOGUE();
|
||||
}
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver exported functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Low level MAC initialization.
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void mac_lld_init(void) {
|
||||
unsigned i;
|
||||
|
||||
macObjectInit(ÐD1);
|
||||
ETHD1.link_up = FALSE;
|
||||
|
||||
/* Descriptor tables are initialized in chained mode, note that the first
|
||||
word is not initialized here but in mac_lld_start().*/
|
||||
for (i = 0; i < STM32_MAC_RECEIVE_BUFFERS; i++) {
|
||||
rd[i].rdes1 = STM32_RDES1_RCH | STM32_MAC_BUFFERS_SIZE;
|
||||
rd[i].rdes2 = (uint32_t)rb[i];
|
||||
rd[i].rdes3 = (uint32_t)&rd[(i + 1) % STM32_MAC_RECEIVE_BUFFERS];
|
||||
}
|
||||
for (i = 0; i < STM32_MAC_TRANSMIT_BUFFERS; i++) {
|
||||
td[i].tdes1 = 0;
|
||||
td[i].tdes2 = (uint32_t)tb[i];
|
||||
td[i].tdes3 = (uint32_t)&td[(i + 1) % STM32_MAC_TRANSMIT_BUFFERS];
|
||||
}
|
||||
|
||||
/* Selection of the RMII or MII mode based on info exported by board.h.*/
|
||||
#if defined(STM32F10X_CL)
|
||||
#if defined(BOARD_PHY_RMII)
|
||||
AFIO->MAPR |= AFIO_MAPR_MII_RMII_SEL;
|
||||
#else
|
||||
AFIO->MAPR &= ~AFIO_MAPR_MII_RMII_SEL;
|
||||
#endif
|
||||
#elif defined(STM32F2XX) || defined(STM32F4XX)
|
||||
#if defined(BOARD_PHY_RMII)
|
||||
SYSCFG->PMC |= SYSCFG_PMC_MII_RMII_SEL;
|
||||
#else
|
||||
SYSCFG->PMC &= ~SYSCFG_PMC_MII_RMII_SEL;
|
||||
#endif
|
||||
#else
|
||||
#error "unsupported STM32 platform for MAC driver"
|
||||
#endif
|
||||
|
||||
/* Reset of the MAC core.*/
|
||||
rccResetETH();
|
||||
|
||||
/* MAC clocks temporary activation.*/
|
||||
rccEnableETH(FALSE);
|
||||
|
||||
/* PHY address setup.*/
|
||||
#if defined(BOARD_PHY_ADDRESS)
|
||||
ETHD1.phyaddr = BOARD_PHY_ADDRESS << 11;
|
||||
#else
|
||||
mii_find_phy(ÐD1);
|
||||
#endif
|
||||
|
||||
#if defined(BOARD_PHY_RESET)
|
||||
/* PHY board-specific reset procedure.*/
|
||||
BOARD_PHY_RESET();
|
||||
#else
|
||||
/* PHY soft reset procedure.*/
|
||||
mii_write(ÐD1, MII_BMCR, BMCR_RESET);
|
||||
#if defined(BOARD_PHY_RESET_DELAY)
|
||||
halPolledDelay(BOARD_PHY_RESET_DELAY);
|
||||
#endif
|
||||
while (mii_read(ÐD1, MII_BMCR) & BMCR_RESET)
|
||||
;
|
||||
#endif
|
||||
|
||||
#if STM32_MAC_ETH1_CHANGE_PHY_STATE
|
||||
/* PHY in power down mode until the driver will be started.*/
|
||||
mii_write(ÐD1, MII_BMCR, mii_read(ÐD1, MII_BMCR) | BMCR_PDOWN);
|
||||
#endif
|
||||
|
||||
/* MAC clocks stopped again.*/
|
||||
rccDisableETH(FALSE);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Configures and activates the MAC peripheral.
|
||||
*
|
||||
* @param[in] macp pointer to the @p MACDriver object
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void mac_lld_start(MACDriver *macp) {
|
||||
unsigned i;
|
||||
|
||||
/* Resets the state of all descriptors.*/
|
||||
for (i = 0; i < STM32_MAC_RECEIVE_BUFFERS; i++)
|
||||
rd[i].rdes0 = STM32_RDES0_OWN;
|
||||
macp->rxptr = (stm32_eth_rx_descriptor_t *)rd;
|
||||
for (i = 0; i < STM32_MAC_TRANSMIT_BUFFERS; i++)
|
||||
td[i].tdes0 = STM32_TDES0_TCH;
|
||||
macp->txptr = (stm32_eth_tx_descriptor_t *)td;
|
||||
|
||||
/* MAC clocks activation and commanded reset procedure.*/
|
||||
rccEnableETH(FALSE);
|
||||
#if defined(STM32_MAC_DMABMR_SR)
|
||||
ETH->DMABMR |= ETH_DMABMR_SR;
|
||||
while(ETH->DMABMR & ETH_DMABMR_SR)
|
||||
;
|
||||
#endif
|
||||
|
||||
/* ISR vector enabled.*/
|
||||
nvicEnableVector(ETH_IRQn,
|
||||
CORTEX_PRIORITY_MASK(STM32_MAC_ETH1_IRQ_PRIORITY));
|
||||
|
||||
#if STM32_MAC_ETH1_CHANGE_PHY_STATE
|
||||
/* PHY in power up mode.*/
|
||||
mii_write(macp, MII_BMCR, mii_read(macp, MII_BMCR) & ~BMCR_PDOWN);
|
||||
#endif
|
||||
|
||||
/* MAC configuration.*/
|
||||
ETH->MACFFR = 0;
|
||||
ETH->MACFCR = 0;
|
||||
ETH->MACVLANTR = 0;
|
||||
|
||||
/* MAC address setup.*/
|
||||
if (macp->config->mac_address == NULL)
|
||||
mac_lld_set_address(default_mac_address);
|
||||
else
|
||||
mac_lld_set_address(macp->config->mac_address);
|
||||
|
||||
/* Transmitter and receiver enabled.
|
||||
Note that the complete setup of the MAC is performed when the link
|
||||
status is detected.*/
|
||||
#if STM32_MAC_IP_CHECKSUM_OFFLOAD
|
||||
ETH->MACCR = ETH_MACCR_IPCO | ETH_MACCR_RE | ETH_MACCR_TE;
|
||||
#else
|
||||
ETH->MACCR = ETH_MACCR_RE | ETH_MACCR_TE;
|
||||
#endif
|
||||
|
||||
/* DMA configuration:
|
||||
Descriptor chains pointers.*/
|
||||
ETH->DMARDLAR = (uint32_t)rd;
|
||||
ETH->DMATDLAR = (uint32_t)td;
|
||||
|
||||
/* Enabling required interrupt sources.*/
|
||||
ETH->DMASR = ETH->DMASR;
|
||||
ETH->DMAIER = ETH_DMAIER_NISE | ETH_DMAIER_RIE | ETH_DMAIER_TIE;
|
||||
|
||||
/* DMA general settings.*/
|
||||
ETH->DMABMR = ETH_DMABMR_AAB | ETH_DMABMR_RDP_1Beat | ETH_DMABMR_PBL_1Beat;
|
||||
|
||||
/* Transmit FIFO flush.*/
|
||||
ETH->DMAOMR = ETH_DMAOMR_FTF;
|
||||
while (ETH->DMAOMR & ETH_DMAOMR_FTF)
|
||||
;
|
||||
|
||||
/* DMA final configuration and start.*/
|
||||
ETH->DMAOMR = ETH_DMAOMR_DTCEFD | ETH_DMAOMR_RSF | ETH_DMAOMR_TSF |
|
||||
ETH_DMAOMR_ST | ETH_DMAOMR_SR;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Deactivates the MAC peripheral.
|
||||
*
|
||||
* @param[in] macp pointer to the @p MACDriver object
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void mac_lld_stop(MACDriver *macp) {
|
||||
|
||||
if (macp->state != MAC_STOP) {
|
||||
#if STM32_MAC_ETH1_CHANGE_PHY_STATE
|
||||
/* PHY in power down mode until the driver will be restarted.*/
|
||||
mii_write(macp, MII_BMCR, mii_read(macp, MII_BMCR) | BMCR_PDOWN);
|
||||
#endif
|
||||
|
||||
/* MAC and DMA stopped.*/
|
||||
ETH->MACCR = 0;
|
||||
ETH->DMAOMR = 0;
|
||||
ETH->DMAIER = 0;
|
||||
ETH->DMASR = ETH->DMASR;
|
||||
|
||||
/* MAC clocks stopped.*/
|
||||
rccDisableETH(FALSE);
|
||||
|
||||
/* ISR vector disabled.*/
|
||||
nvicDisableVector(ETH_IRQn);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a transmission descriptor.
|
||||
* @details One of the available transmission descriptors is locked and
|
||||
* returned.
|
||||
*
|
||||
* @param[in] macp pointer to the @p MACDriver object
|
||||
* @param[out] tdp pointer to a @p MACTransmitDescriptor structure
|
||||
* @return The operation status.
|
||||
* @retval RDY_OK the descriptor has been obtained.
|
||||
* @retval RDY_TIMEOUT descriptor not available.
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
msg_t mac_lld_get_transmit_descriptor(MACDriver *macp,
|
||||
MACTransmitDescriptor *tdp) {
|
||||
stm32_eth_tx_descriptor_t *tdes;
|
||||
|
||||
if (!macp->link_up)
|
||||
return RDY_TIMEOUT;
|
||||
|
||||
chSysLock();
|
||||
|
||||
/* Get Current TX descriptor.*/
|
||||
tdes = macp->txptr;
|
||||
|
||||
/* Ensure that descriptor isn't owned by the Ethernet DMA or locked by
|
||||
another thread.*/
|
||||
if (tdes->tdes0 & (STM32_TDES0_OWN | STM32_TDES0_LOCKED)) {
|
||||
chSysUnlock();
|
||||
return RDY_TIMEOUT;
|
||||
}
|
||||
|
||||
/* Marks the current descriptor as locked using a reserved bit.*/
|
||||
tdes->tdes0 |= STM32_TDES0_LOCKED;
|
||||
|
||||
/* Next TX descriptor to use.*/
|
||||
macp->txptr = (stm32_eth_tx_descriptor_t *)tdes->tdes3;
|
||||
|
||||
chSysUnlock();
|
||||
|
||||
/* Set the buffer size and configuration.*/
|
||||
tdp->offset = 0;
|
||||
tdp->size = STM32_MAC_BUFFERS_SIZE;
|
||||
tdp->physdesc = tdes;
|
||||
|
||||
return RDY_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Releases a transmit descriptor and starts the transmission of the
|
||||
* enqueued data as a single frame.
|
||||
*
|
||||
* @param[in] tdp the pointer to the @p MACTransmitDescriptor structure
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void mac_lld_release_transmit_descriptor(MACTransmitDescriptor *tdp) {
|
||||
|
||||
chDbgAssert(!(tdp->physdesc->tdes0 & STM32_TDES0_OWN),
|
||||
"mac_lld_release_transmit_descriptor(), #1",
|
||||
"attempt to release descriptor already owned by DMA");
|
||||
|
||||
chSysLock();
|
||||
|
||||
/* Unlocks the descriptor and returns it to the DMA engine.*/
|
||||
tdp->physdesc->tdes1 = tdp->offset;
|
||||
tdp->physdesc->tdes0 = STM32_TDES0_CIC(STM32_MAC_IP_CHECKSUM_OFFLOAD) |
|
||||
STM32_TDES0_IC | STM32_TDES0_LS | STM32_TDES0_FS |
|
||||
STM32_TDES0_TCH | STM32_TDES0_OWN;
|
||||
|
||||
/* If the DMA engine is stalled then a restart request is issued.*/
|
||||
if ((ETH->DMASR & ETH_DMASR_TPS) == ETH_DMASR_TPS_Suspended) {
|
||||
ETH->DMASR = ETH_DMASR_TBUS;
|
||||
ETH->DMATPDR = ETH_DMASR_TBUS; /* Any value is OK.*/
|
||||
}
|
||||
|
||||
chSysUnlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a receive descriptor.
|
||||
*
|
||||
* @param[in] macp pointer to the @p MACDriver object
|
||||
* @param[out] rdp pointer to a @p MACReceiveDescriptor structure
|
||||
* @return The operation status.
|
||||
* @retval RDY_OK the descriptor has been obtained.
|
||||
* @retval RDY_TIMEOUT descriptor not available.
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
msg_t mac_lld_get_receive_descriptor(MACDriver *macp,
|
||||
MACReceiveDescriptor *rdp) {
|
||||
stm32_eth_rx_descriptor_t *rdes;
|
||||
|
||||
chSysLock();
|
||||
|
||||
/* Get Current RX descriptor.*/
|
||||
rdes = macp->rxptr;
|
||||
|
||||
/* Iterates through received frames until a valid one is found, invalid
|
||||
frames are discarded.*/
|
||||
while (!(rdes->rdes0 & STM32_RDES0_OWN)) {
|
||||
if (!(rdes->rdes0 & (STM32_RDES0_AFM | STM32_RDES0_ES))
|
||||
#if STM32_MAC_IP_CHECKSUM_OFFLOAD
|
||||
&& (rdes->rdes0 & STM32_RDES0_FT)
|
||||
&& !(rdes->rdes0 & (STM32_RDES0_IPHCE | STM32_RDES0_PCE))
|
||||
#endif
|
||||
&& (rdes->rdes0 & STM32_RDES0_FS) && (rdes->rdes0 & STM32_RDES0_LS)) {
|
||||
/* Found a valid one.*/
|
||||
rdp->offset = 0;
|
||||
rdp->size = ((rdes->rdes0 & STM32_RDES0_FL_MASK) >> 16) - 4;
|
||||
rdp->physdesc = rdes;
|
||||
macp->rxptr = (stm32_eth_rx_descriptor_t *)rdes->rdes3;
|
||||
|
||||
chSysUnlock();
|
||||
return RDY_OK;
|
||||
}
|
||||
/* Invalid frame found, purging.*/
|
||||
rdes->rdes0 = STM32_RDES0_OWN;
|
||||
rdes = (stm32_eth_rx_descriptor_t *)rdes->rdes3;
|
||||
}
|
||||
|
||||
/* Next descriptor to check.*/
|
||||
macp->rxptr = rdes;
|
||||
|
||||
chSysUnlock();
|
||||
return RDY_TIMEOUT;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Releases a receive descriptor.
|
||||
* @details The descriptor and its buffer are made available for more incoming
|
||||
* frames.
|
||||
*
|
||||
* @param[in] rdp the pointer to the @p MACReceiveDescriptor structure
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void mac_lld_release_receive_descriptor(MACReceiveDescriptor *rdp) {
|
||||
|
||||
chDbgAssert(!(rdp->physdesc->rdes0 & STM32_RDES0_OWN),
|
||||
"mac_lld_release_receive_descriptor(), #1",
|
||||
"attempt to release descriptor already owned by DMA");
|
||||
|
||||
chSysLock();
|
||||
|
||||
/* Give buffer back to the Ethernet DMA.*/
|
||||
rdp->physdesc->rdes0 = STM32_RDES0_OWN;
|
||||
|
||||
/* If the DMA engine is stalled then a restart request is issued.*/
|
||||
if ((ETH->DMASR & ETH_DMASR_RPS) == ETH_DMASR_RPS_Suspended) {
|
||||
ETH->DMASR = ETH_DMASR_RBUS;
|
||||
ETH->DMARPDR = ETH_DMASR_RBUS; /* Any value is OK.*/
|
||||
}
|
||||
|
||||
chSysUnlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Updates and returns the link status.
|
||||
*
|
||||
* @param[in] macp pointer to the @p MACDriver object
|
||||
* @return The link status.
|
||||
* @retval TRUE if the link is active.
|
||||
* @retval FALSE if the link is down.
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
bool_t mac_lld_poll_link_status(MACDriver *macp) {
|
||||
uint32_t maccr, bmsr, bmcr;
|
||||
|
||||
maccr = ETH->MACCR;
|
||||
|
||||
/* PHY CR and SR registers read.*/
|
||||
(void)mii_read(macp, MII_BMSR);
|
||||
bmsr = mii_read(macp, MII_BMSR);
|
||||
bmcr = mii_read(macp, MII_BMCR);
|
||||
|
||||
/* Check on auto-negotiation mode.*/
|
||||
if (bmcr & BMCR_ANENABLE) {
|
||||
uint32_t lpa;
|
||||
|
||||
/* Auto-negotiation must be finished without faults and link established.*/
|
||||
if ((bmsr & (BMSR_LSTATUS | BMSR_RFAULT | BMSR_ANEGCOMPLETE)) !=
|
||||
(BMSR_LSTATUS | BMSR_ANEGCOMPLETE))
|
||||
return macp->link_up = FALSE;
|
||||
|
||||
/* Auto-negotiation enabled, checks the LPA register.*/
|
||||
lpa = mii_read(macp, MII_LPA);
|
||||
|
||||
/* Check on link speed.*/
|
||||
if (lpa & (LPA_100HALF | LPA_100FULL | LPA_100BASE4))
|
||||
maccr |= ETH_MACCR_FES;
|
||||
else
|
||||
maccr &= ~ETH_MACCR_FES;
|
||||
|
||||
/* Check on link mode.*/
|
||||
if (lpa & (LPA_10FULL | LPA_100FULL))
|
||||
maccr |= ETH_MACCR_DM;
|
||||
else
|
||||
maccr &= ~ETH_MACCR_DM;
|
||||
}
|
||||
else {
|
||||
/* Link must be established.*/
|
||||
if (!(bmsr & BMSR_LSTATUS))
|
||||
return macp->link_up = FALSE;
|
||||
|
||||
/* Check on link speed.*/
|
||||
if (bmcr & BMCR_SPEED100)
|
||||
maccr |= ETH_MACCR_FES;
|
||||
else
|
||||
maccr &= ~ETH_MACCR_FES;
|
||||
|
||||
/* Check on link mode.*/
|
||||
if (bmcr & BMCR_FULLDPLX)
|
||||
maccr |= ETH_MACCR_DM;
|
||||
else
|
||||
maccr &= ~ETH_MACCR_DM;
|
||||
}
|
||||
|
||||
/* Changes the mode in the MAC.*/
|
||||
ETH->MACCR = maccr;
|
||||
|
||||
/* Returns the link status.*/
|
||||
return macp->link_up = TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Writes to a transmit descriptor's stream.
|
||||
*
|
||||
* @param[in] tdp pointer to a @p MACTransmitDescriptor structure
|
||||
* @param[in] buf pointer to the buffer containing the data to be
|
||||
* written
|
||||
* @param[in] size number of bytes to be written
|
||||
* @return The number of bytes written into the descriptor's
|
||||
* stream, this value can be less than the amount
|
||||
* specified in the parameter @p size if the maximum
|
||||
* frame size is reached.
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
size_t mac_lld_write_transmit_descriptor(MACTransmitDescriptor *tdp,
|
||||
uint8_t *buf,
|
||||
size_t size) {
|
||||
|
||||
chDbgAssert(!(tdp->physdesc->tdes0 & STM32_TDES0_OWN),
|
||||
"mac_lld_write_transmit_descriptor(), #1",
|
||||
"attempt to write descriptor already owned by DMA");
|
||||
|
||||
if (size > tdp->size - tdp->offset)
|
||||
size = tdp->size - tdp->offset;
|
||||
|
||||
if (size > 0) {
|
||||
memcpy((uint8_t *)(tdp->physdesc->tdes2) + tdp->offset, buf, size);
|
||||
tdp->offset += size;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Reads from a receive descriptor's stream.
|
||||
*
|
||||
* @param[in] rdp pointer to a @p MACReceiveDescriptor structure
|
||||
* @param[in] buf pointer to the buffer that will receive the read data
|
||||
* @param[in] size number of bytes to be read
|
||||
* @return The number of bytes read from the descriptor's
|
||||
* stream, this value can be less than the amount
|
||||
* specified in the parameter @p size if there are
|
||||
* no more bytes to read.
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
size_t mac_lld_read_receive_descriptor(MACReceiveDescriptor *rdp,
|
||||
uint8_t *buf,
|
||||
size_t size) {
|
||||
|
||||
chDbgAssert(!(rdp->physdesc->rdes0 & STM32_RDES0_OWN),
|
||||
"mac_lld_read_receive_descriptor(), #1",
|
||||
"attempt to read descriptor already owned by DMA");
|
||||
|
||||
if (size > rdp->size - rdp->offset)
|
||||
size = rdp->size - rdp->offset;
|
||||
|
||||
if (size > 0) {
|
||||
memcpy(buf, (uint8_t *)(rdp->physdesc->rdes2) + rdp->offset, size);
|
||||
rdp->offset += size;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
#if MAC_USE_ZERO_COPY || defined(__DOXYGEN__)
|
||||
/**
|
||||
* @brief Returns a pointer to the next transmit buffer in the descriptor
|
||||
* chain.
|
||||
* @note The API guarantees that enough buffers can be requested to fill
|
||||
* a whole frame.
|
||||
*
|
||||
* @param[in] tdp pointer to a @p MACTransmitDescriptor structure
|
||||
* @param[in] size size of the requested buffer. Specify the frame size
|
||||
* on the first call then scale the value down subtracting
|
||||
* the amount of data already copied into the previous
|
||||
* buffers.
|
||||
* @param[out] sizep pointer to variable receiving the buffer size, it is
|
||||
* zero when the last buffer has already been returned.
|
||||
* Note that a returned size lower than the amount
|
||||
* requested means that more buffers must be requested
|
||||
* in order to fill the frame data entirely.
|
||||
* @return Pointer to the returned buffer.
|
||||
* @retval NULL if the buffer chain has been entirely scanned.
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
uint8_t *mac_lld_get_next_transmit_buffer(MACTransmitDescriptor *tdp,
|
||||
size_t size,
|
||||
size_t *sizep) {
|
||||
|
||||
if (tdp->offset == 0) {
|
||||
*sizep = tdp->size;
|
||||
tdp->offset = size;
|
||||
return (uint8_t *)tdp->physdesc->tdes2;
|
||||
}
|
||||
*sizep = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a pointer to the next receive buffer in the descriptor
|
||||
* chain.
|
||||
* @note The API guarantees that the descriptor chain contains a whole
|
||||
* frame.
|
||||
*
|
||||
* @param[in] rdp pointer to a @p MACReceiveDescriptor structure
|
||||
* @param[out] sizep pointer to variable receiving the buffer size, it is
|
||||
* zero when the last buffer has already been returned.
|
||||
* @return Pointer to the returned buffer.
|
||||
* @retval NULL if the buffer chain has been entirely scanned.
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
const uint8_t *mac_lld_get_next_receive_buffer(MACReceiveDescriptor *rdp,
|
||||
size_t *sizep) {
|
||||
|
||||
if (rdp->size > 0) {
|
||||
*sizep = rdp->size;
|
||||
rdp->offset = rdp->size;
|
||||
rdp->size = 0;
|
||||
return (uint8_t *)rdp->physdesc->rdes2;
|
||||
}
|
||||
*sizep = 0;
|
||||
return NULL;
|
||||
}
|
||||
#endif /* MAC_USE_ZERO_COPY */
|
||||
|
||||
#endif /* HAL_USE_MAC */
|
||||
|
||||
/** @} */
|
||||
+364
@@ -0,0 +1,364 @@
|
||||
/*
|
||||
ChibiOS/RT - Copyright (C) 2006-2013 Giovanni Di Sirio
|
||||
|
||||
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 STM32/mac_lld.h
|
||||
* @brief STM32 low level MAC driver header.
|
||||
*
|
||||
* @addtogroup MAC
|
||||
* @{
|
||||
*/
|
||||
|
||||
#ifndef _MAC_LLD_H_
|
||||
#define _MAC_LLD_H_
|
||||
|
||||
#if HAL_USE_MAC || defined(__DOXYGEN__)
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver constants. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief This implementation supports the zero-copy mode API.
|
||||
*/
|
||||
#define MAC_SUPPORTS_ZERO_COPY TRUE
|
||||
|
||||
/**
|
||||
* @name RDES0 constants
|
||||
* @{
|
||||
*/
|
||||
#define STM32_RDES0_OWN 0x80000000
|
||||
#define STM32_RDES0_AFM 0x40000000
|
||||
#define STM32_RDES0_FL_MASK 0x3FFF0000
|
||||
#define STM32_RDES0_ES 0x00008000
|
||||
#define STM32_RDES0_DESERR 0x00004000
|
||||
#define STM32_RDES0_SAF 0x00002000
|
||||
#define STM32_RDES0_LE 0x00001000
|
||||
#define STM32_RDES0_OE 0x00000800
|
||||
#define STM32_RDES0_VLAN 0x00000400
|
||||
#define STM32_RDES0_FS 0x00000200
|
||||
#define STM32_RDES0_LS 0x00000100
|
||||
#define STM32_RDES0_IPHCE 0x00000080
|
||||
#define STM32_RDES0_LCO 0x00000040
|
||||
#define STM32_RDES0_FT 0x00000020
|
||||
#define STM32_RDES0_RWT 0x00000010
|
||||
#define STM32_RDES0_RE 0x00000008
|
||||
#define STM32_RDES0_DE 0x00000004
|
||||
#define STM32_RDES0_CE 0x00000002
|
||||
#define STM32_RDES0_PCE 0x00000001
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name RDES1 constants
|
||||
* @{
|
||||
*/
|
||||
#define STM32_RDES1_DIC 0x80000000
|
||||
#define STM32_RDES1_RBS2_MASK 0x1FFF0000
|
||||
#define STM32_RDES1_RER 0x00008000
|
||||
#define STM32_RDES1_RCH 0x00004000
|
||||
#define STM32_RDES1_RBS1_MASK 0x00001FFF
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name TDES0 constants
|
||||
* @{
|
||||
*/
|
||||
#define STM32_TDES0_OWN 0x80000000
|
||||
#define STM32_TDES0_IC 0x40000000
|
||||
#define STM32_TDES0_LS 0x20000000
|
||||
#define STM32_TDES0_FS 0x10000000
|
||||
#define STM32_TDES0_DC 0x08000000
|
||||
#define STM32_TDES0_DP 0x04000000
|
||||
#define STM32_TDES0_TTSE 0x02000000
|
||||
#define STM32_TDES0_LOCKED 0x01000000 /* NOTE: Pseudo flag. */
|
||||
#define STM32_TDES0_CIC_MASK 0x00C00000
|
||||
#define STM32_TDES0_CIC(n) ((n) << 22)
|
||||
#define STM32_TDES0_TER 0x00200000
|
||||
#define STM32_TDES0_TCH 0x00100000
|
||||
#define STM32_TDES0_TTSS 0x00020000
|
||||
#define STM32_TDES0_IHE 0x00010000
|
||||
#define STM32_TDES0_ES 0x00008000
|
||||
#define STM32_TDES0_JT 0x00004000
|
||||
#define STM32_TDES0_FF 0x00002000
|
||||
#define STM32_TDES0_IPE 0x00001000
|
||||
#define STM32_TDES0_LCA 0x00000800
|
||||
#define STM32_TDES0_NC 0x00000400
|
||||
#define STM32_TDES0_LCO 0x00000200
|
||||
#define STM32_TDES0_EC 0x00000100
|
||||
#define STM32_TDES0_VF 0x00000080
|
||||
#define STM32_TDES0_CC_MASK 0x00000078
|
||||
#define STM32_TDES0_ED 0x00000004
|
||||
#define STM32_TDES0_UF 0x00000002
|
||||
#define STM32_TDES0_DB 0x00000001
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name TDES1 constants
|
||||
* @{
|
||||
*/
|
||||
#define STM32_TDES1_TBS2_MASK 0x1FFF0000
|
||||
#define STM32_TDES1_TBS1_MASK 0x00001FFF
|
||||
/** @} */
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver pre-compile time settings. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @name Configuration options
|
||||
* @{
|
||||
*/
|
||||
/**
|
||||
* @brief Number of available transmit buffers.
|
||||
*/
|
||||
#if !defined(STM32_MAC_TRANSMIT_BUFFERS) || defined(__DOXYGEN__)
|
||||
#define STM32_MAC_TRANSMIT_BUFFERS 2
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Number of available receive buffers.
|
||||
*/
|
||||
#if !defined(STM32_MAC_RECEIVE_BUFFERS) || defined(__DOXYGEN__)
|
||||
#define STM32_MAC_RECEIVE_BUFFERS 4
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Maximum supported frame size.
|
||||
*/
|
||||
#if !defined(STM32_MAC_BUFFERS_SIZE) || defined(__DOXYGEN__)
|
||||
#define STM32_MAC_BUFFERS_SIZE 1522
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief PHY detection timeout.
|
||||
* @details Timeout, in milliseconds, for PHY address detection, if a PHY
|
||||
* is not detected within the timeout then the driver halts during
|
||||
* initialization. This setting applies only if the PHY address is
|
||||
* not explicitly set in the board header file using
|
||||
* @p BOARD_PHY_ADDRESS. A zero value disables the timeout and a
|
||||
* single search path is performed.
|
||||
*/
|
||||
#if !defined(STM32_MAC_PHY_TIMEOUT) || defined(__DOXYGEN__)
|
||||
#define STM32_MAC_PHY_TIMEOUT 100
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Change the PHY power state inside the driver.
|
||||
*/
|
||||
#if !defined(STM32_MAC_ETH1_CHANGE_PHY_STATE) || defined(__DOXYGEN__)
|
||||
#define STM32_MAC_ETH1_CHANGE_PHY_STATE TRUE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief ETHD1 interrupt priority level setting.
|
||||
*/
|
||||
#if !defined(STM32_MAC_ETH1_IRQ_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define STM32_MAC_ETH1_IRQ_PRIORITY 13
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief IP checksum offload.
|
||||
* @details The following modes are available:
|
||||
* - 0 Function disabled.
|
||||
* - 1 Only IP header checksum calculation and insertion are enabled.
|
||||
* - 2 IP header checksum and payload checksum calculation and
|
||||
* insertion are enabled, but pseudo-header checksum is not
|
||||
* calculated in hardware.
|
||||
* - 3 IP Header checksum and payload checksum calculation and
|
||||
* insertion are enabled, and pseudo-header checksum is
|
||||
* calculated in hardware.
|
||||
* .
|
||||
*/
|
||||
#if !defined(STM32_MAC_IP_CHECKSUM_OFFLOAD) || defined(__DOXYGEN__)
|
||||
#define STM32_MAC_IP_CHECKSUM_OFFLOAD 0
|
||||
#endif
|
||||
/** @} */
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Derived constants and error checks. */
|
||||
/*===========================================================================*/
|
||||
|
||||
#if (STM32_MAC_PHY_TIMEOUT > 0) && !HAL_IMPLEMENTS_COUNTERS
|
||||
#error "STM32_MAC_PHY_TIMEOUT requires the realtime counter service"
|
||||
#endif
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver data structures and types. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Type of an STM32 Ethernet receive descriptor.
|
||||
*/
|
||||
typedef struct {
|
||||
volatile uint32_t rdes0;
|
||||
volatile uint32_t rdes1;
|
||||
volatile uint32_t rdes2;
|
||||
volatile uint32_t rdes3;
|
||||
} stm32_eth_rx_descriptor_t;
|
||||
|
||||
/**
|
||||
* @brief Type of an STM32 Ethernet transmit descriptor.
|
||||
*/
|
||||
typedef struct {
|
||||
volatile uint32_t tdes0;
|
||||
volatile uint32_t tdes1;
|
||||
volatile uint32_t tdes2;
|
||||
volatile uint32_t tdes3;
|
||||
} stm32_eth_tx_descriptor_t;
|
||||
|
||||
/**
|
||||
* @brief Driver configuration structure.
|
||||
*/
|
||||
typedef struct {
|
||||
/**
|
||||
* @brief MAC address.
|
||||
*/
|
||||
uint8_t *mac_address;
|
||||
/* End of the mandatory fields.*/
|
||||
} MACConfig;
|
||||
|
||||
/**
|
||||
* @brief Structure representing a MAC driver.
|
||||
*/
|
||||
struct MACDriver {
|
||||
/**
|
||||
* @brief Driver state.
|
||||
*/
|
||||
macstate_t state;
|
||||
/**
|
||||
* @brief Current configuration data.
|
||||
*/
|
||||
const MACConfig *config;
|
||||
/**
|
||||
* @brief Transmit semaphore.
|
||||
*/
|
||||
Semaphore tdsem;
|
||||
/**
|
||||
* @brief Receive semaphore.
|
||||
*/
|
||||
Semaphore rdsem;
|
||||
#if MAC_USE_EVENTS || defined(__DOXYGEN__)
|
||||
/**
|
||||
* @brief Receive event.
|
||||
*/
|
||||
EventSource rdevent;
|
||||
#endif
|
||||
/* End of the mandatory fields.*/
|
||||
/**
|
||||
* @brief Link status flag.
|
||||
*/
|
||||
bool_t link_up;
|
||||
/**
|
||||
* @brief PHY address (pre shifted).
|
||||
*/
|
||||
uint32_t phyaddr;
|
||||
/**
|
||||
* @brief Receive next frame pointer.
|
||||
*/
|
||||
stm32_eth_rx_descriptor_t *rxptr;
|
||||
/**
|
||||
* @brief Transmit next frame pointer.
|
||||
*/
|
||||
stm32_eth_tx_descriptor_t *txptr;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Structure representing a transmit descriptor.
|
||||
*/
|
||||
typedef struct {
|
||||
/**
|
||||
* @brief Current write offset.
|
||||
*/
|
||||
size_t offset;
|
||||
/**
|
||||
* @brief Available space size.
|
||||
*/
|
||||
size_t size;
|
||||
/* End of the mandatory fields.*/
|
||||
/**
|
||||
* @brief Pointer to the physical descriptor.
|
||||
*/
|
||||
stm32_eth_tx_descriptor_t *physdesc;
|
||||
} MACTransmitDescriptor;
|
||||
|
||||
/**
|
||||
* @brief Structure representing a receive descriptor.
|
||||
*/
|
||||
typedef struct {
|
||||
/**
|
||||
* @brief Current read offset.
|
||||
*/
|
||||
size_t offset;
|
||||
/**
|
||||
* @brief Available data size.
|
||||
*/
|
||||
size_t size;
|
||||
/* End of the mandatory fields.*/
|
||||
/**
|
||||
* @brief Pointer to the physical descriptor.
|
||||
*/
|
||||
stm32_eth_rx_descriptor_t *physdesc;
|
||||
} MACReceiveDescriptor;
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver macros. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* External declarations. */
|
||||
/*===========================================================================*/
|
||||
|
||||
#if !defined(__DOXYGEN__)
|
||||
extern MACDriver ETHD1;
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
void mii_write(MACDriver *macp, uint32_t reg, uint32_t value);
|
||||
uint32_t mii_read(MACDriver *macp, uint32_t reg);
|
||||
void mac_lld_init(void);
|
||||
void mac_lld_start(MACDriver *macp);
|
||||
void mac_lld_stop(MACDriver *macp);
|
||||
msg_t mac_lld_get_transmit_descriptor(MACDriver *macp,
|
||||
MACTransmitDescriptor *tdp);
|
||||
void mac_lld_release_transmit_descriptor(MACTransmitDescriptor *tdp);
|
||||
msg_t mac_lld_get_receive_descriptor(MACDriver *macp,
|
||||
MACReceiveDescriptor *rdp);
|
||||
void mac_lld_release_receive_descriptor(MACReceiveDescriptor *rdp);
|
||||
bool_t mac_lld_poll_link_status(MACDriver *macp);
|
||||
size_t mac_lld_write_transmit_descriptor(MACTransmitDescriptor *tdp,
|
||||
uint8_t *buf,
|
||||
size_t size);
|
||||
size_t mac_lld_read_receive_descriptor(MACReceiveDescriptor *rdp,
|
||||
uint8_t *buf,
|
||||
size_t size);
|
||||
#if MAC_USE_ZERO_COPY
|
||||
uint8_t *mac_lld_get_next_transmit_buffer(MACTransmitDescriptor *tdp,
|
||||
size_t size,
|
||||
size_t *sizep);
|
||||
const uint8_t *mac_lld_get_next_receive_buffer(MACReceiveDescriptor *rdp,
|
||||
size_t *sizep);
|
||||
#endif /* MAC_USE_ZERO_COPY */
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* HAL_USE_MAC */
|
||||
|
||||
#endif /* _MAC_LLD_H_ */
|
||||
|
||||
/** @} */
|
||||
+791
@@ -0,0 +1,791 @@
|
||||
/*
|
||||
ChibiOS/RT - Copyright (C) 2006-2013 Giovanni Di Sirio
|
||||
|
||||
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 STM32/sdc_lld.c
|
||||
* @brief STM32 SDC subsystem low level driver source.
|
||||
*
|
||||
* @addtogroup SDC
|
||||
* @{
|
||||
*/
|
||||
|
||||
/*
|
||||
TODO: Try preerase blocks before writing (ACMD23).
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "ch.h"
|
||||
#include "hal.h"
|
||||
|
||||
#if HAL_USE_SDC || defined(__DOXYGEN__)
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local definitions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
#define DMA_CHANNEL \
|
||||
STM32_DMA_GETCHANNEL(STM32_SDC_SDIO_DMA_STREAM, \
|
||||
STM32_SDC_SDIO_DMA_CHN)
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver exported variables. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/** @brief SDCD1 driver identifier.*/
|
||||
SDCDriver SDCD1;
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local variables and types. */
|
||||
/*===========================================================================*/
|
||||
|
||||
#if STM32_SDC_SDIO_UNALIGNED_SUPPORT
|
||||
/**
|
||||
* @brief Buffer for temporary storage during unaligned transfers.
|
||||
*/
|
||||
static union {
|
||||
uint32_t alignment;
|
||||
uint8_t buf[MMCSD_BLOCK_SIZE];
|
||||
} u;
|
||||
#endif /* STM32_SDC_SDIO_UNALIGNED_SUPPORT */
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Prepares card to handle read transaction.
|
||||
*
|
||||
* @param[in] sdcp pointer to the @p SDCDriver object
|
||||
* @param[in] startblk first block to read
|
||||
* @param[in] n number of blocks to read
|
||||
* @param[in] resp pointer to the response buffer
|
||||
*
|
||||
* @return The operation status.
|
||||
* @retval CH_SUCCESS operation succeeded.
|
||||
* @retval CH_FAILED operation failed.
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
static bool_t sdc_lld_prepare_read(SDCDriver *sdcp, uint32_t startblk,
|
||||
uint32_t n, uint32_t *resp) {
|
||||
|
||||
/* Driver handles data in 512 bytes blocks (just like HC cards). But if we
|
||||
have not HC card than we must convert address from blocks to bytes.*/
|
||||
if (!(sdcp->cardmode & SDC_MODE_HIGH_CAPACITY))
|
||||
startblk *= MMCSD_BLOCK_SIZE;
|
||||
|
||||
if (n > 1) {
|
||||
/* Send read multiple blocks command to card.*/
|
||||
if (sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_READ_MULTIPLE_BLOCK,
|
||||
startblk, resp) || MMCSD_R1_ERROR(resp[0]))
|
||||
return CH_FAILED;
|
||||
}
|
||||
else{
|
||||
/* Send read single block command.*/
|
||||
if (sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_READ_SINGLE_BLOCK,
|
||||
startblk, resp) || MMCSD_R1_ERROR(resp[0]))
|
||||
return CH_FAILED;
|
||||
}
|
||||
|
||||
return CH_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Prepares card to handle write transaction.
|
||||
*
|
||||
* @param[in] sdcp pointer to the @p SDCDriver object
|
||||
* @param[in] startblk first block to read
|
||||
* @param[in] n number of blocks to write
|
||||
* @param[in] resp pointer to the response buffer
|
||||
*
|
||||
* @return The operation status.
|
||||
* @retval CH_SUCCESS operation succeeded.
|
||||
* @retval CH_FAILED operation failed.
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
static bool_t sdc_lld_prepare_write(SDCDriver *sdcp, uint32_t startblk,
|
||||
uint32_t n, uint32_t *resp) {
|
||||
|
||||
/* Driver handles data in 512 bytes blocks (just like HC cards). But if we
|
||||
have not HC card than we must convert address from blocks to bytes.*/
|
||||
if (!(sdcp->cardmode & SDC_MODE_HIGH_CAPACITY))
|
||||
startblk *= MMCSD_BLOCK_SIZE;
|
||||
|
||||
if (n > 1) {
|
||||
/* Write multiple blocks command.*/
|
||||
if (sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_WRITE_MULTIPLE_BLOCK,
|
||||
startblk, resp) || MMCSD_R1_ERROR(resp[0]))
|
||||
return CH_FAILED;
|
||||
}
|
||||
else{
|
||||
/* Write single block command.*/
|
||||
if (sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_WRITE_BLOCK,
|
||||
startblk, resp) || MMCSD_R1_ERROR(resp[0]))
|
||||
return CH_FAILED;
|
||||
}
|
||||
|
||||
return CH_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Wait end of data transaction and performs finalizations.
|
||||
*
|
||||
* @param[in] sdcp pointer to the @p SDCDriver object
|
||||
* @param[in] n number of blocks in transaction
|
||||
* @param[in] resp pointer to the response buffer
|
||||
*
|
||||
* @return The operation status.
|
||||
* @retval CH_SUCCESS operation succeeded.
|
||||
* @retval CH_FAILED operation failed.
|
||||
*/
|
||||
static bool_t sdc_lld_wait_transaction_end(SDCDriver *sdcp, uint32_t n,
|
||||
uint32_t *resp) {
|
||||
|
||||
/* Note the mask is checked before going to sleep because the interrupt
|
||||
may have occurred before reaching the critical zone.*/
|
||||
chSysLock();
|
||||
if (SDIO->MASK != 0) {
|
||||
chDbgAssert(sdcp->thread == NULL,
|
||||
"sdc_lld_start_data_transaction(), #1", "not NULL");
|
||||
sdcp->thread = chThdSelf();
|
||||
chSchGoSleepS(THD_STATE_SUSPENDED);
|
||||
chDbgAssert(sdcp->thread == NULL,
|
||||
"sdc_lld_start_data_transaction(), #2", "not NULL");
|
||||
}
|
||||
if ((SDIO->STA & SDIO_STA_DATAEND) == 0) {
|
||||
chSysUnlock();
|
||||
return CH_FAILED;
|
||||
}
|
||||
|
||||
#if (defined(STM32F4XX) || defined(STM32F2XX))
|
||||
/* Wait until DMA channel enabled to be sure that all data transferred.*/
|
||||
while (sdcp->dma->stream->CR & STM32_DMA_CR_EN)
|
||||
;
|
||||
|
||||
/* DMA event flags must be manually cleared.*/
|
||||
dmaStreamClearInterrupt(sdcp->dma);
|
||||
|
||||
SDIO->ICR = STM32_SDIO_ICR_ALL_FLAGS;
|
||||
SDIO->DCTRL = 0;
|
||||
chSysUnlock();
|
||||
|
||||
/* Wait until interrupt flags to be cleared.*/
|
||||
/*while (((DMA2->LISR) >> (sdcp->dma->ishift)) & STM32_DMA_ISR_TCIF)
|
||||
dmaStreamClearInterrupt(sdcp->dma);*/
|
||||
#else
|
||||
/* Waits for transfer completion at DMA level, the the stream is
|
||||
disabled and cleared.*/
|
||||
dmaWaitCompletion(sdcp->dma);
|
||||
|
||||
SDIO->ICR = STM32_SDIO_ICR_ALL_FLAGS;
|
||||
SDIO->DCTRL = 0;
|
||||
chSysUnlock();
|
||||
#endif
|
||||
|
||||
/* Finalize transaction.*/
|
||||
if (n > 1)
|
||||
return sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_STOP_TRANSMISSION, 0, resp);
|
||||
|
||||
return CH_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets SDC errors.
|
||||
*
|
||||
* @param[in] sdcp pointer to the @p SDCDriver object
|
||||
* @param[in] sta value of the STA register
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
static void sdc_lld_collect_errors(SDCDriver *sdcp, uint32_t sta) {
|
||||
uint32_t errors = SDC_NO_ERROR;
|
||||
|
||||
if (sta & SDIO_STA_CCRCFAIL)
|
||||
errors |= SDC_CMD_CRC_ERROR;
|
||||
if (sta & SDIO_STA_DCRCFAIL)
|
||||
errors |= SDC_DATA_CRC_ERROR;
|
||||
if (sta & SDIO_STA_CTIMEOUT)
|
||||
errors |= SDC_COMMAND_TIMEOUT;
|
||||
if (sta & SDIO_STA_DTIMEOUT)
|
||||
errors |= SDC_DATA_TIMEOUT;
|
||||
if (sta & SDIO_STA_TXUNDERR)
|
||||
errors |= SDC_TX_UNDERRUN;
|
||||
if (sta & SDIO_STA_RXOVERR)
|
||||
errors |= SDC_RX_OVERRUN;
|
||||
if (sta & SDIO_STA_STBITERR)
|
||||
errors |= SDC_STARTBIT_ERROR;
|
||||
|
||||
sdcp->errors |= errors;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Performs clean transaction stopping in case of errors.
|
||||
*
|
||||
* @param[in] sdcp pointer to the @p SDCDriver object
|
||||
* @param[in] n number of blocks in transaction
|
||||
* @param[in] resp pointer to the response buffer
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
static void sdc_lld_error_cleanup(SDCDriver *sdcp,
|
||||
uint32_t n,
|
||||
uint32_t *resp) {
|
||||
uint32_t sta = SDIO->STA;
|
||||
|
||||
dmaStreamClearInterrupt(sdcp->dma);
|
||||
dmaStreamDisable(sdcp->dma);
|
||||
SDIO->ICR = STM32_SDIO_ICR_ALL_FLAGS;
|
||||
SDIO->MASK = 0;
|
||||
SDIO->DCTRL = 0;
|
||||
sdc_lld_collect_errors(sdcp, sta);
|
||||
if (n > 1)
|
||||
sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_STOP_TRANSMISSION, 0, resp);
|
||||
}
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver interrupt handlers. */
|
||||
/*===========================================================================*/
|
||||
|
||||
#if !defined(STM32_SDIO_HANDLER)
|
||||
#error "STM32_SDIO_HANDLER not defined"
|
||||
#endif
|
||||
/**
|
||||
* @brief SDIO IRQ handler.
|
||||
* @details It just wakes transaction thread. All error handling performs in
|
||||
* that thread.
|
||||
*
|
||||
* @isr
|
||||
*/
|
||||
CH_IRQ_HANDLER(STM32_SDIO_HANDLER) {
|
||||
|
||||
CH_IRQ_PROLOGUE();
|
||||
|
||||
chSysLockFromIsr()
|
||||
|
||||
/* Disables the source but the status flags are not reset because the
|
||||
read/write functions needs to check them.*/
|
||||
SDIO->MASK = 0;
|
||||
|
||||
if (SDCD1.thread != NULL) {
|
||||
chSchReadyI(SDCD1.thread);
|
||||
SDCD1.thread = NULL;
|
||||
}
|
||||
|
||||
chSysUnlockFromIsr();
|
||||
|
||||
CH_IRQ_EPILOGUE();
|
||||
}
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver exported functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Low level SDC driver initialization.
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void sdc_lld_init(void) {
|
||||
|
||||
sdcObjectInit(&SDCD1);
|
||||
SDCD1.thread = NULL;
|
||||
SDCD1.dma = STM32_DMA_STREAM(STM32_SDC_SDIO_DMA_STREAM);
|
||||
#if CH_DBG_ENABLE_ASSERTS
|
||||
SDCD1.sdio = SDIO;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Configures and activates the SDC peripheral.
|
||||
*
|
||||
* @param[in] sdcp pointer to the @p SDCDriver object
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void sdc_lld_start(SDCDriver *sdcp) {
|
||||
|
||||
sdcp->dmamode = STM32_DMA_CR_CHSEL(DMA_CHANNEL) |
|
||||
STM32_DMA_CR_PL(STM32_SDC_SDIO_DMA_PRIORITY) |
|
||||
STM32_DMA_CR_PSIZE_WORD |
|
||||
STM32_DMA_CR_MSIZE_WORD |
|
||||
STM32_DMA_CR_MINC;
|
||||
|
||||
#if (defined(STM32F4XX) || defined(STM32F2XX))
|
||||
sdcp->dmamode |= STM32_DMA_CR_PFCTRL |
|
||||
STM32_DMA_CR_PBURST_INCR4 |
|
||||
STM32_DMA_CR_MBURST_INCR4;
|
||||
#endif
|
||||
|
||||
if (sdcp->state == BLK_STOP) {
|
||||
/* Note, the DMA must be enabled before the IRQs.*/
|
||||
bool_t b;
|
||||
b = dmaStreamAllocate(sdcp->dma, STM32_SDC_SDIO_IRQ_PRIORITY, NULL, NULL);
|
||||
chDbgAssert(!b, "sdc_lld_start(), #1", "stream already allocated");
|
||||
dmaStreamSetPeripheral(sdcp->dma, &SDIO->FIFO);
|
||||
#if (defined(STM32F4XX) || defined(STM32F2XX))
|
||||
dmaStreamSetFIFO(sdcp->dma, STM32_DMA_FCR_DMDIS | STM32_DMA_FCR_FTH_FULL);
|
||||
#endif
|
||||
nvicEnableVector(STM32_SDIO_NUMBER,
|
||||
CORTEX_PRIORITY_MASK(STM32_SDC_SDIO_IRQ_PRIORITY));
|
||||
rccEnableSDIO(FALSE);
|
||||
}
|
||||
|
||||
/* Configuration, card clock is initially stopped.*/
|
||||
SDIO->POWER = 0;
|
||||
SDIO->CLKCR = 0;
|
||||
SDIO->DCTRL = 0;
|
||||
SDIO->DTIMER = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Deactivates the SDC peripheral.
|
||||
*
|
||||
* @param[in] sdcp pointer to the @p SDCDriver object
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void sdc_lld_stop(SDCDriver *sdcp) {
|
||||
|
||||
if (sdcp->state != BLK_STOP) {
|
||||
|
||||
/* SDIO deactivation.*/
|
||||
SDIO->POWER = 0;
|
||||
SDIO->CLKCR = 0;
|
||||
SDIO->DCTRL = 0;
|
||||
SDIO->DTIMER = 0;
|
||||
|
||||
/* Clock deactivation.*/
|
||||
nvicDisableVector(STM32_SDIO_NUMBER);
|
||||
dmaStreamRelease(sdcp->dma);
|
||||
rccDisableSDIO(FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Starts the SDIO clock and sets it to init mode (400kHz or less).
|
||||
*
|
||||
* @param[in] sdcp pointer to the @p SDCDriver object
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void sdc_lld_start_clk(SDCDriver *sdcp) {
|
||||
|
||||
(void)sdcp;
|
||||
|
||||
/* Initial clock setting: 400kHz, 1bit mode.*/
|
||||
SDIO->CLKCR = STM32_SDIO_DIV_LS;
|
||||
SDIO->POWER |= SDIO_POWER_PWRCTRL_0 | SDIO_POWER_PWRCTRL_1;
|
||||
SDIO->CLKCR |= SDIO_CLKCR_CLKEN;
|
||||
|
||||
/* Clock activation delay.*/
|
||||
chThdSleepMilliseconds(STM32_SDC_CLOCK_ACTIVATION_DELAY);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the SDIO clock to data mode (25MHz or less).
|
||||
*
|
||||
* @param[in] sdcp pointer to the @p SDCDriver object
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void sdc_lld_set_data_clk(SDCDriver *sdcp) {
|
||||
|
||||
(void)sdcp;
|
||||
|
||||
SDIO->CLKCR = (SDIO->CLKCR & 0xFFFFFF00) | STM32_SDIO_DIV_HS;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Stops the SDIO clock.
|
||||
*
|
||||
* @param[in] sdcp pointer to the @p SDCDriver object
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void sdc_lld_stop_clk(SDCDriver *sdcp) {
|
||||
|
||||
(void)sdcp;
|
||||
|
||||
SDIO->CLKCR = 0;
|
||||
SDIO->POWER = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Switches the bus to 4 bits mode.
|
||||
*
|
||||
* @param[in] sdcp pointer to the @p SDCDriver object
|
||||
* @param[in] mode bus mode
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void sdc_lld_set_bus_mode(SDCDriver *sdcp, sdcbusmode_t mode) {
|
||||
uint32_t clk = SDIO->CLKCR & ~SDIO_CLKCR_WIDBUS;
|
||||
|
||||
(void)sdcp;
|
||||
|
||||
switch (mode) {
|
||||
case SDC_MODE_1BIT:
|
||||
SDIO->CLKCR = clk;
|
||||
break;
|
||||
case SDC_MODE_4BIT:
|
||||
SDIO->CLKCR = clk | SDIO_CLKCR_WIDBUS_0;
|
||||
break;
|
||||
case SDC_MODE_8BIT:
|
||||
SDIO->CLKCR = clk | SDIO_CLKCR_WIDBUS_1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sends an SDIO command with no response expected.
|
||||
*
|
||||
* @param[in] sdcp pointer to the @p SDCDriver object
|
||||
* @param[in] cmd card command
|
||||
* @param[in] arg command argument
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
void sdc_lld_send_cmd_none(SDCDriver *sdcp, uint8_t cmd, uint32_t arg) {
|
||||
|
||||
(void)sdcp;
|
||||
|
||||
SDIO->ARG = arg;
|
||||
SDIO->CMD = (uint32_t)cmd | SDIO_CMD_CPSMEN;
|
||||
while ((SDIO->STA & SDIO_STA_CMDSENT) == 0)
|
||||
;
|
||||
SDIO->ICR = SDIO_ICR_CMDSENTC;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sends an SDIO command with a short response expected.
|
||||
* @note The CRC is not verified.
|
||||
*
|
||||
* @param[in] sdcp pointer to the @p SDCDriver object
|
||||
* @param[in] cmd card command
|
||||
* @param[in] arg command argument
|
||||
* @param[out] resp pointer to the response buffer (one word)
|
||||
*
|
||||
* @return The operation status.
|
||||
* @retval CH_SUCCESS operation succeeded.
|
||||
* @retval CH_FAILED operation failed.
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
bool_t sdc_lld_send_cmd_short(SDCDriver *sdcp, uint8_t cmd, uint32_t arg,
|
||||
uint32_t *resp) {
|
||||
uint32_t sta;
|
||||
|
||||
(void)sdcp;
|
||||
|
||||
SDIO->ARG = arg;
|
||||
SDIO->CMD = (uint32_t)cmd | SDIO_CMD_WAITRESP_0 | SDIO_CMD_CPSMEN;
|
||||
while (((sta = SDIO->STA) & (SDIO_STA_CMDREND | SDIO_STA_CTIMEOUT |
|
||||
SDIO_STA_CCRCFAIL)) == 0)
|
||||
;
|
||||
SDIO->ICR = sta & (SDIO_STA_CMDREND | SDIO_STA_CTIMEOUT | SDIO_STA_CCRCFAIL);
|
||||
if ((sta & (SDIO_STA_CTIMEOUT)) != 0) {
|
||||
sdc_lld_collect_errors(sdcp, sta);
|
||||
return CH_FAILED;
|
||||
}
|
||||
*resp = SDIO->RESP1;
|
||||
return CH_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sends an SDIO command with a short response expected and CRC.
|
||||
*
|
||||
* @param[in] sdcp pointer to the @p SDCDriver object
|
||||
* @param[in] cmd card command
|
||||
* @param[in] arg command argument
|
||||
* @param[out] resp pointer to the response buffer (one word)
|
||||
*
|
||||
* @return The operation status.
|
||||
* @retval CH_SUCCESS operation succeeded.
|
||||
* @retval CH_FAILED operation failed.
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
bool_t sdc_lld_send_cmd_short_crc(SDCDriver *sdcp, uint8_t cmd, uint32_t arg,
|
||||
uint32_t *resp) {
|
||||
uint32_t sta;
|
||||
|
||||
(void)sdcp;
|
||||
|
||||
SDIO->ARG = arg;
|
||||
SDIO->CMD = (uint32_t)cmd | SDIO_CMD_WAITRESP_0 | SDIO_CMD_CPSMEN;
|
||||
while (((sta = SDIO->STA) & (SDIO_STA_CMDREND | SDIO_STA_CTIMEOUT |
|
||||
SDIO_STA_CCRCFAIL)) == 0)
|
||||
;
|
||||
SDIO->ICR = sta & (SDIO_STA_CMDREND | SDIO_STA_CTIMEOUT | SDIO_STA_CCRCFAIL);
|
||||
if ((sta & (SDIO_STA_CTIMEOUT | SDIO_STA_CCRCFAIL)) != 0) {
|
||||
sdc_lld_collect_errors(sdcp, sta);
|
||||
return CH_FAILED;
|
||||
}
|
||||
*resp = SDIO->RESP1;
|
||||
return CH_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sends an SDIO command with a long response expected and CRC.
|
||||
*
|
||||
* @param[in] sdcp pointer to the @p SDCDriver object
|
||||
* @param[in] cmd card command
|
||||
* @param[in] arg command argument
|
||||
* @param[out] resp pointer to the response buffer (four words)
|
||||
*
|
||||
* @return The operation status.
|
||||
* @retval CH_SUCCESS operation succeeded.
|
||||
* @retval CH_FAILED operation failed.
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
bool_t sdc_lld_send_cmd_long_crc(SDCDriver *sdcp, uint8_t cmd, uint32_t arg,
|
||||
uint32_t *resp) {
|
||||
uint32_t sta;
|
||||
|
||||
(void)sdcp;
|
||||
|
||||
SDIO->ARG = arg;
|
||||
SDIO->CMD = (uint32_t)cmd | SDIO_CMD_WAITRESP_0 | SDIO_CMD_WAITRESP_1 |
|
||||
SDIO_CMD_CPSMEN;
|
||||
while (((sta = SDIO->STA) & (SDIO_STA_CMDREND | SDIO_STA_CTIMEOUT |
|
||||
SDIO_STA_CCRCFAIL)) == 0)
|
||||
;
|
||||
SDIO->ICR = sta & (SDIO_STA_CMDREND | SDIO_STA_CTIMEOUT | SDIO_STA_CCRCFAIL);
|
||||
if ((sta & (STM32_SDIO_STA_ERROR_MASK)) != 0) {
|
||||
sdc_lld_collect_errors(sdcp, sta);
|
||||
return CH_FAILED;
|
||||
}
|
||||
/* Save bytes in reverse order because MSB in response comes first.*/
|
||||
*resp++ = SDIO->RESP4;
|
||||
*resp++ = SDIO->RESP3;
|
||||
*resp++ = SDIO->RESP2;
|
||||
*resp = SDIO->RESP1;
|
||||
return CH_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Reads one or more blocks.
|
||||
*
|
||||
* @param[in] sdcp pointer to the @p SDCDriver object
|
||||
* @param[in] startblk first block to read
|
||||
* @param[out] buf pointer to the read buffer
|
||||
* @param[in] n number of blocks to read
|
||||
*
|
||||
* @return The operation status.
|
||||
* @retval CH_SUCCESS operation succeeded.
|
||||
* @retval CH_FAILED operation failed.
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
bool_t sdc_lld_read_aligned(SDCDriver *sdcp, uint32_t startblk,
|
||||
uint8_t *buf, uint32_t n) {
|
||||
uint32_t resp[1];
|
||||
|
||||
chDbgCheck((n < (0x1000000 / MMCSD_BLOCK_SIZE)), "max transaction size");
|
||||
|
||||
SDIO->DTIMER = STM32_SDC_READ_TIMEOUT;
|
||||
|
||||
/* Checks for errors and waits for the card to be ready for reading.*/
|
||||
if (_sdc_wait_for_transfer_state(sdcp))
|
||||
return CH_FAILED;
|
||||
|
||||
/* Prepares the DMA channel for writing.*/
|
||||
dmaStreamSetMemory0(sdcp->dma, buf);
|
||||
dmaStreamSetTransactionSize(sdcp->dma,
|
||||
(n * MMCSD_BLOCK_SIZE) / sizeof (uint32_t));
|
||||
dmaStreamSetMode(sdcp->dma, sdcp->dmamode | STM32_DMA_CR_DIR_P2M);
|
||||
dmaStreamEnable(sdcp->dma);
|
||||
|
||||
/* Setting up data transfer.*/
|
||||
SDIO->ICR = STM32_SDIO_ICR_ALL_FLAGS;
|
||||
SDIO->MASK = SDIO_MASK_DCRCFAILIE |
|
||||
SDIO_MASK_DTIMEOUTIE |
|
||||
SDIO_MASK_STBITERRIE |
|
||||
SDIO_MASK_RXOVERRIE |
|
||||
SDIO_MASK_DATAENDIE;
|
||||
SDIO->DLEN = n * MMCSD_BLOCK_SIZE;
|
||||
|
||||
/* Transaction starts just after DTEN bit setting.*/
|
||||
SDIO->DCTRL = SDIO_DCTRL_DTDIR |
|
||||
SDIO_DCTRL_DBLOCKSIZE_3 |
|
||||
SDIO_DCTRL_DBLOCKSIZE_0 |
|
||||
SDIO_DCTRL_DMAEN |
|
||||
SDIO_DCTRL_DTEN;
|
||||
|
||||
/* Talk to card what we want from it.*/
|
||||
if (sdc_lld_prepare_read(sdcp, startblk, n, resp) == TRUE)
|
||||
goto error;
|
||||
if (sdc_lld_wait_transaction_end(sdcp, n, resp) == TRUE)
|
||||
goto error;
|
||||
|
||||
return CH_SUCCESS;
|
||||
|
||||
error:
|
||||
sdc_lld_error_cleanup(sdcp, n, resp);
|
||||
return CH_FAILED;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Writes one or more blocks.
|
||||
*
|
||||
* @param[in] sdcp pointer to the @p SDCDriver object
|
||||
* @param[in] startblk first block to write
|
||||
* @param[out] buf pointer to the write buffer
|
||||
* @param[in] n number of blocks to write
|
||||
*
|
||||
* @return The operation status.
|
||||
* @retval CH_SUCCESS operation succeeded.
|
||||
* @retval CH_FAILED operation failed.
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
bool_t sdc_lld_write_aligned(SDCDriver *sdcp, uint32_t startblk,
|
||||
const uint8_t *buf, uint32_t n) {
|
||||
uint32_t resp[1];
|
||||
|
||||
chDbgCheck((n < (0x1000000 / MMCSD_BLOCK_SIZE)), "max transaction size");
|
||||
|
||||
SDIO->DTIMER = STM32_SDC_WRITE_TIMEOUT;
|
||||
|
||||
/* Checks for errors and waits for the card to be ready for writing.*/
|
||||
if (_sdc_wait_for_transfer_state(sdcp))
|
||||
return CH_FAILED;
|
||||
|
||||
/* Prepares the DMA channel for writing.*/
|
||||
dmaStreamSetMemory0(sdcp->dma, buf);
|
||||
dmaStreamSetTransactionSize(sdcp->dma,
|
||||
(n * MMCSD_BLOCK_SIZE) / sizeof (uint32_t));
|
||||
dmaStreamSetMode(sdcp->dma, sdcp->dmamode | STM32_DMA_CR_DIR_M2P);
|
||||
dmaStreamEnable(sdcp->dma);
|
||||
|
||||
/* Setting up data transfer.*/
|
||||
SDIO->ICR = STM32_SDIO_ICR_ALL_FLAGS;
|
||||
SDIO->MASK = SDIO_MASK_DCRCFAILIE |
|
||||
SDIO_MASK_DTIMEOUTIE |
|
||||
SDIO_MASK_STBITERRIE |
|
||||
SDIO_MASK_TXUNDERRIE |
|
||||
SDIO_MASK_DATAENDIE;
|
||||
SDIO->DLEN = n * MMCSD_BLOCK_SIZE;
|
||||
|
||||
/* Talk to card what we want from it.*/
|
||||
if (sdc_lld_prepare_write(sdcp, startblk, n, resp) == TRUE)
|
||||
goto error;
|
||||
|
||||
/* Transaction starts just after DTEN bit setting.*/
|
||||
SDIO->DCTRL = SDIO_DCTRL_DBLOCKSIZE_3 |
|
||||
SDIO_DCTRL_DBLOCKSIZE_0 |
|
||||
SDIO_DCTRL_DMAEN |
|
||||
SDIO_DCTRL_DTEN;
|
||||
if (sdc_lld_wait_transaction_end(sdcp, n, resp) == TRUE)
|
||||
goto error;
|
||||
|
||||
return CH_SUCCESS;
|
||||
|
||||
error:
|
||||
sdc_lld_error_cleanup(sdcp, n, resp);
|
||||
return CH_FAILED;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Reads one or more blocks.
|
||||
*
|
||||
* @param[in] sdcp pointer to the @p SDCDriver object
|
||||
* @param[in] startblk first block to read
|
||||
* @param[out] buf pointer to the read buffer
|
||||
* @param[in] n number of blocks to read
|
||||
*
|
||||
* @return The operation status.
|
||||
* @retval CH_SUCCESS operation succeeded.
|
||||
* @retval CH_FAILED operation failed.
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
bool_t sdc_lld_read(SDCDriver *sdcp, uint32_t startblk,
|
||||
uint8_t *buf, uint32_t n) {
|
||||
|
||||
#if STM32_SDC_SDIO_UNALIGNED_SUPPORT
|
||||
if (((unsigned)buf & 3) != 0) {
|
||||
uint32_t i;
|
||||
for (i = 0; i < n; i++) {
|
||||
if (sdc_lld_read_aligned(sdcp, startblk, u.buf, 1))
|
||||
return CH_FAILED;
|
||||
memcpy(buf, u.buf, MMCSD_BLOCK_SIZE);
|
||||
buf += MMCSD_BLOCK_SIZE;
|
||||
startblk++;
|
||||
}
|
||||
return CH_SUCCESS;
|
||||
}
|
||||
#endif /* STM32_SDC_SDIO_UNALIGNED_SUPPORT */
|
||||
return sdc_lld_read_aligned(sdcp, startblk, buf, n);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Writes one or more blocks.
|
||||
*
|
||||
* @param[in] sdcp pointer to the @p SDCDriver object
|
||||
* @param[in] startblk first block to write
|
||||
* @param[out] buf pointer to the write buffer
|
||||
* @param[in] n number of blocks to write
|
||||
*
|
||||
* @return The operation status.
|
||||
* @retval CH_SUCCESS operation succeeded.
|
||||
* @retval CH_FAILED operation failed.
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
bool_t sdc_lld_write(SDCDriver *sdcp, uint32_t startblk,
|
||||
const uint8_t *buf, uint32_t n) {
|
||||
|
||||
#if STM32_SDC_SDIO_UNALIGNED_SUPPORT
|
||||
if (((unsigned)buf & 3) != 0) {
|
||||
uint32_t i;
|
||||
for (i = 0; i < n; i++) {
|
||||
memcpy(u.buf, buf, MMCSD_BLOCK_SIZE);
|
||||
buf += MMCSD_BLOCK_SIZE;
|
||||
if (sdc_lld_write_aligned(sdcp, startblk, u.buf, 1))
|
||||
return CH_FAILED;
|
||||
startblk++;
|
||||
}
|
||||
return CH_SUCCESS;
|
||||
}
|
||||
#endif /* STM32_SDC_SDIO_UNALIGNED_SUPPORT */
|
||||
return sdc_lld_write_aligned(sdcp, startblk, buf, n);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Waits for card idle condition.
|
||||
*
|
||||
* @param[in] sdcp pointer to the @p SDCDriver object
|
||||
*
|
||||
* @return The operation status.
|
||||
* @retval CH_SUCCESS the operation succeeded.
|
||||
* @retval CH_FAILED the operation failed.
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
bool_t sdc_lld_sync(SDCDriver *sdcp) {
|
||||
|
||||
/* TODO: Implement.*/
|
||||
(void)sdcp;
|
||||
return CH_SUCCESS;
|
||||
}
|
||||
|
||||
#endif /* HAL_USE_SDC */
|
||||
|
||||
/** @} */
|
||||
+317
@@ -0,0 +1,317 @@
|
||||
/*
|
||||
ChibiOS/RT - Copyright (C) 2006-2013 Giovanni Di Sirio
|
||||
|
||||
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 STM32/sdc_lld.h
|
||||
* @brief STM32 SDC subsystem low level driver header.
|
||||
*
|
||||
* @addtogroup SDC
|
||||
* @{
|
||||
*/
|
||||
|
||||
#ifndef _SDC_LLD_H_
|
||||
#define _SDC_LLD_H_
|
||||
|
||||
#if HAL_USE_SDC || defined(__DOXYGEN__)
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver constants. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Value to clear all interrupts flag at once.
|
||||
*/
|
||||
#define STM32_SDIO_ICR_ALL_FLAGS (SDIO_ICR_CCRCFAILC | SDIO_ICR_DCRCFAILC | \
|
||||
SDIO_ICR_CTIMEOUTC | SDIO_ICR_DTIMEOUTC | \
|
||||
SDIO_ICR_TXUNDERRC | SDIO_ICR_RXOVERRC | \
|
||||
SDIO_ICR_CMDRENDC | SDIO_ICR_CMDSENTC | \
|
||||
SDIO_ICR_DATAENDC | SDIO_ICR_STBITERRC | \
|
||||
SDIO_ICR_DBCKENDC | SDIO_ICR_SDIOITC | \
|
||||
SDIO_ICR_CEATAENDC)
|
||||
|
||||
/**
|
||||
* @brief Mask of error flags in STA register.
|
||||
*/
|
||||
#define STM32_SDIO_STA_ERROR_MASK (SDIO_STA_CCRCFAIL | SDIO_STA_DCRCFAIL | \
|
||||
SDIO_STA_CTIMEOUT | SDIO_STA_DTIMEOUT | \
|
||||
SDIO_STA_TXUNDERR | SDIO_STA_RXOVERR)
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver pre-compile time settings. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @name Configuration options
|
||||
* @{
|
||||
*/
|
||||
/**
|
||||
* @brief SDIO DMA priority (0..3|lowest..highest).
|
||||
*/
|
||||
#if !defined(STM32_SDC_SDIO_DMA_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define STM32_SDC_SDIO_DMA_PRIORITY 3
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief SDIO interrupt priority level setting.
|
||||
*/
|
||||
#if !defined(STM32_SDC_SDIO_IRQ_PRIORITY) || defined(__DOXYGEN__)
|
||||
#define STM32_SDC_SDIO_IRQ_PRIORITY 9
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Write timeout in milliseconds.
|
||||
*/
|
||||
#if !defined(SDC_WRITE_TIMEOUT_MS) || defined(__DOXYGEN__)
|
||||
#define SDC_WRITE_TIMEOUT_MS 250
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Read timeout in milliseconds.
|
||||
*/
|
||||
#if !defined(SDC_READ_TIMEOUT_MS) || defined(__DOXYGEN__)
|
||||
#define SDC_READ_TIMEOUT_MS 25
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Card clock activation delay in milliseconds.
|
||||
*/
|
||||
#if !defined(STM32_SDC_CLOCK_ACTIVATION_DELAY) || defined(__DOXYGEN__)
|
||||
#define STM32_SDC_CLOCK_ACTIVATION_DELAY 10
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Support for unaligned transfers.
|
||||
* @note Unaligned transfers are much slower.
|
||||
*/
|
||||
#if !defined(STM32_SDC_SDIO_UNALIGNED_SUPPORT) || defined(__DOXYGEN__)
|
||||
#define STM32_SDC_SDIO_UNALIGNED_SUPPORT TRUE
|
||||
#endif
|
||||
|
||||
#if STM32_ADVANCED_DMA || defined(__DOXYGEN__)
|
||||
|
||||
/**
|
||||
* @brief DMA stream used for SDC operations.
|
||||
* @note This option is only available on platforms with enhanced DMA.
|
||||
*/
|
||||
#if !defined(STM32_SDC_SDIO_DMA_STREAM) || defined(__DOXYGEN__)
|
||||
#define STM32_SDC_SDIO_DMA_STREAM STM32_DMA_STREAM_ID(2, 3)
|
||||
#endif
|
||||
|
||||
#else /* !STM32_ADVANCED_DMA*/
|
||||
#define STM32_SDC_SDIO_DMA_STREAM STM32_DMA_STREAM_ID(2, 4)
|
||||
|
||||
#endif /* !STM32_ADVANCED_DMA*/
|
||||
/** @} */
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Derived constants and error checks. */
|
||||
/*===========================================================================*/
|
||||
|
||||
#if !STM32_HAS_SDIO
|
||||
#error "SDIO not present in the selected device"
|
||||
#endif
|
||||
|
||||
#if !CORTEX_IS_VALID_KERNEL_PRIORITY(STM32_SDC_SDIO_IRQ_PRIORITY)
|
||||
#error "Invalid IRQ priority assigned to SDIO"
|
||||
#endif
|
||||
|
||||
#if !STM32_DMA_IS_VALID_PRIORITY(STM32_SDC_SDIO_DMA_PRIORITY)
|
||||
#error "Invalid DMA priority assigned to SDIO"
|
||||
#endif
|
||||
|
||||
#if !defined(STM32_DMA_REQUIRED)
|
||||
#define STM32_DMA_REQUIRED
|
||||
#endif
|
||||
|
||||
/*
|
||||
* SDIO clock divider.
|
||||
*/
|
||||
#if (defined(STM32F4XX) || defined(STM32F2XX))
|
||||
#define STM32_SDIO_DIV_HS 0
|
||||
#define STM32_SDIO_DIV_LS 120
|
||||
|
||||
#elif STM32_HCLK > 48000000
|
||||
#define STM32_SDIO_DIV_HS 1
|
||||
#define STM32_SDIO_DIV_LS 178
|
||||
#else
|
||||
|
||||
#define STM32_SDIO_DIV_HS 0
|
||||
#define STM32_SDIO_DIV_LS 118
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief SDIO data timeouts in SDIO clock cycles.
|
||||
*/
|
||||
#if (defined(STM32F4XX) || defined(STM32F2XX))
|
||||
#if !STM32_CLOCK48_REQUIRED
|
||||
#error "SDIO requires STM32_CLOCK48_REQUIRED to be enabled"
|
||||
#endif
|
||||
|
||||
#define STM32_SDC_WRITE_TIMEOUT \
|
||||
(((STM32_PLL48CLK / (STM32_SDIO_DIV_HS + 2)) / 1000) * SDC_WRITE_TIMEOUT_MS)
|
||||
#define STM32_SDC_READ_TIMEOUT \
|
||||
(((STM32_PLL48CLK / (STM32_SDIO_DIV_HS + 2)) / 1000) * SDC_READ_TIMEOUT_MS)
|
||||
|
||||
#else
|
||||
#define STM32_SDC_WRITE_TIMEOUT \
|
||||
(((STM32_HCLK / (STM32_SDIO_DIV_HS + 2)) / 1000) * SDC_WRITE_TIMEOUT_MS)
|
||||
#define STM32_SDC_READ_TIMEOUT \
|
||||
(((STM32_HCLK / (STM32_SDIO_DIV_HS + 2)) / 1000) * SDC_READ_TIMEOUT_MS)
|
||||
#endif
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver data structures and types. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Type of SDIO bus mode.
|
||||
*/
|
||||
typedef enum {
|
||||
SDC_MODE_1BIT = 0,
|
||||
SDC_MODE_4BIT,
|
||||
SDC_MODE_8BIT
|
||||
} sdcbusmode_t;
|
||||
|
||||
/**
|
||||
* @brief Type of card flags.
|
||||
*/
|
||||
typedef uint32_t sdcmode_t;
|
||||
|
||||
/**
|
||||
* @brief SDC Driver condition flags type.
|
||||
*/
|
||||
typedef uint32_t sdcflags_t;
|
||||
|
||||
/**
|
||||
* @brief Type of a structure representing an SDC driver.
|
||||
*/
|
||||
typedef struct SDCDriver SDCDriver;
|
||||
|
||||
/**
|
||||
* @brief Driver configuration structure.
|
||||
* @note It could be empty on some architectures.
|
||||
*/
|
||||
typedef struct {
|
||||
uint32_t dummy;
|
||||
} SDCConfig;
|
||||
|
||||
/**
|
||||
* @brief @p SDCDriver specific methods.
|
||||
*/
|
||||
#define _sdc_driver_methods \
|
||||
_mmcsd_block_device_methods
|
||||
|
||||
/**
|
||||
* @extends MMCSDBlockDeviceVMT
|
||||
*
|
||||
* @brief @p SDCDriver virtual methods table.
|
||||
*/
|
||||
struct SDCDriverVMT {
|
||||
_sdc_driver_methods
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Structure representing an SDC driver.
|
||||
*/
|
||||
struct SDCDriver {
|
||||
/**
|
||||
* @brief Virtual Methods Table.
|
||||
*/
|
||||
const struct SDCDriverVMT *vmt;
|
||||
_mmcsd_block_device_data
|
||||
/**
|
||||
* @brief Current configuration data.
|
||||
*/
|
||||
const SDCConfig *config;
|
||||
/**
|
||||
* @brief Various flags regarding the mounted card.
|
||||
*/
|
||||
sdcmode_t cardmode;
|
||||
/**
|
||||
* @brief Errors flags.
|
||||
*/
|
||||
sdcflags_t errors;
|
||||
/**
|
||||
* @brief Card RCA.
|
||||
*/
|
||||
uint32_t rca;
|
||||
/* End of the mandatory fields.*/
|
||||
/**
|
||||
* @brief Thread waiting for I/O completion IRQ.
|
||||
*/
|
||||
Thread *thread;
|
||||
/**
|
||||
* @brief DMA mode bit mask.
|
||||
*/
|
||||
uint32_t dmamode;
|
||||
/**
|
||||
* @brief Transmit DMA channel.
|
||||
*/
|
||||
const stm32_dma_stream_t *dma;
|
||||
/**
|
||||
* @brief Pointer to the SDIO registers block.
|
||||
* @note Used only for dubugging purpose.
|
||||
*/
|
||||
#if CH_DBG_ENABLE_ASSERTS
|
||||
SDIO_TypeDef *sdio;
|
||||
#endif
|
||||
};
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver macros. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* External declarations. */
|
||||
/*===========================================================================*/
|
||||
|
||||
#if !defined(__DOXYGEN__)
|
||||
extern SDCDriver SDCD1;
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
void sdc_lld_init(void);
|
||||
void sdc_lld_start(SDCDriver *sdcp);
|
||||
void sdc_lld_stop(SDCDriver *sdcp);
|
||||
void sdc_lld_start_clk(SDCDriver *sdcp);
|
||||
void sdc_lld_set_data_clk(SDCDriver *sdcp);
|
||||
void sdc_lld_stop_clk(SDCDriver *sdcp);
|
||||
void sdc_lld_set_bus_mode(SDCDriver *sdcp, sdcbusmode_t mode);
|
||||
void sdc_lld_send_cmd_none(SDCDriver *sdcp, uint8_t cmd, uint32_t arg);
|
||||
bool_t sdc_lld_send_cmd_short(SDCDriver *sdcp, uint8_t cmd, uint32_t arg,
|
||||
uint32_t *resp);
|
||||
bool_t sdc_lld_send_cmd_short_crc(SDCDriver *sdcp, uint8_t cmd, uint32_t arg,
|
||||
uint32_t *resp);
|
||||
bool_t sdc_lld_send_cmd_long_crc(SDCDriver *sdcp, uint8_t cmd, uint32_t arg,
|
||||
uint32_t *resp);
|
||||
bool_t sdc_lld_read(SDCDriver *sdcp, uint32_t startblk,
|
||||
uint8_t *buf, uint32_t n);
|
||||
bool_t sdc_lld_write(SDCDriver *sdcp, uint32_t startblk,
|
||||
const uint8_t *buf, uint32_t n);
|
||||
bool_t sdc_lld_sync(SDCDriver *sdcp);
|
||||
bool_t sdc_lld_is_card_inserted(SDCDriver *sdcp);
|
||||
bool_t sdc_lld_is_write_protected(SDCDriver *sdcp);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* HAL_USE_SDC */
|
||||
|
||||
#endif /* _SDC_LLD_H_ */
|
||||
|
||||
/** @} */
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
ChibiOS/RT - Copyright (C) 2006-2013 Giovanni Di Sirio
|
||||
|
||||
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 STM32/stm32.h
|
||||
* @brief STM32 common header.
|
||||
* @pre One of the following macros must be defined before including
|
||||
* this header, the macro selects the inclusion of the appropriate
|
||||
* vendor header:
|
||||
* - STM32F0XX for Entry Level devices.
|
||||
* - STM32F10X_LD_VL for Value Line Low Density devices.
|
||||
* - STM32F10X_MD_VL for Value Line Medium Density devices.
|
||||
* - STM32F10X_LD for Performance Low Density devices.
|
||||
* - STM32F10X_MD for Performance Medium Density devices.
|
||||
* - STM32F10X_HD for Performance High Density devices.
|
||||
* - STM32F10X_XL for Performance eXtra Density devices.
|
||||
* - STM32F10X_CL for Connectivity Line devices.
|
||||
* - STM32F2XX for High-performance STM32 F-2 devices.
|
||||
* - STM32F30X for Analog & DSP devices.
|
||||
* - STM32F37X for Analog & DSP devices.
|
||||
* - STM32F4XX for High-performance STM32 F-4 devices.
|
||||
* - STM32L1XX_MD for Ultra Low Power Medium-density devices.
|
||||
* - STM32L1XX_MDP for Ultra Low Power Medium-density Plus devices.
|
||||
* - STM32L1XX_HD for Ultra Low Power High-density devices.
|
||||
* .
|
||||
*
|
||||
* @addtogroup HAL
|
||||
* @{
|
||||
*/
|
||||
|
||||
#ifndef _STM32_H_
|
||||
#define _STM32_H_
|
||||
|
||||
#if defined(STM32F030) || defined(STM32F0XX_LD) || \
|
||||
defined(STM32F0XX_MD)
|
||||
#include "stm32f0xx.h"
|
||||
|
||||
#elif defined(STM32F10X_LD_VL) || defined(STM32F10X_MD_VL) || \
|
||||
defined(STM32F10X_HD_VL) || defined(STM32F10X_LD) || \
|
||||
defined(STM32F10X_MD) || defined(STM32F10X_HD) || \
|
||||
defined(STM32F10X_XL) || defined(STM32F10X_CL) || \
|
||||
defined(__DOXYGEN__)
|
||||
#include "stm32f10x.h"
|
||||
|
||||
#elif defined(STM32F2XX)
|
||||
#include "stm32f2xx.h"
|
||||
|
||||
#elif defined(STM32F30X)
|
||||
#include "stm32f30x.h"
|
||||
|
||||
#elif defined(STM32F37X)
|
||||
#include "stm32f37x.h"
|
||||
|
||||
#elif defined(STM32F401xx) || defined(STM32F40_41xxx) || \
|
||||
defined(STM32F427_437xx) || defined(STM32F429_439xx)
|
||||
#include "stm32f4xx.h"
|
||||
|
||||
#elif defined(STM32L1XX_MD) || defined(STM32L1XX_MDP) || \
|
||||
defined(STM32L1XX_HD)
|
||||
#include "stm32l1xx.h"
|
||||
|
||||
#else
|
||||
#error "STM32 device not specified"
|
||||
#endif
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver constants. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver pre-compile time settings. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Derived constants and error checks. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver data structures and types. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver macros. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* External declarations. */
|
||||
/*===========================================================================*/
|
||||
|
||||
#endif /* _STM32_H_ */
|
||||
|
||||
/** @} */
|
||||
Reference in New Issue
Block a user