ChibiOS 2.6.8, until I can figure out where to get it from git.

This commit is contained in:
Jared Boone
2015-07-08 08:40:23 -07:00
parent dc6fee8370
commit e1eea8e08a
1929 changed files with 575326 additions and 0 deletions
+184
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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_ */
/** @} */
File diff suppressed because it is too large Load Diff
+552
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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, &timespec);
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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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(&ETHD1.rdsem, 0);
#if MAC_USE_EVENTS
chEvtBroadcastI(&ETHD1.rdevent);
#endif
chSysUnlockFromIsr();
}
if (dmasr & ETH_DMASR_TS) {
/* Data Transmitted.*/
chSysLockFromIsr();
chSemResetI(&ETHD1.tdsem, 0);
chSysUnlockFromIsr();
}
CH_IRQ_EPILOGUE();
}
/*===========================================================================*/
/* Driver exported functions. */
/*===========================================================================*/
/**
* @brief Low level MAC initialization.
*
* @notapi
*/
void mac_lld_init(void) {
unsigned i;
macObjectInit(&ETHD1);
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(&ETHD1);
#endif
#if defined(BOARD_PHY_RESET)
/* PHY board-specific reset procedure.*/
BOARD_PHY_RESET();
#else
/* PHY soft reset procedure.*/
mii_write(&ETHD1, MII_BMCR, BMCR_RESET);
#if defined(BOARD_PHY_RESET_DELAY)
halPolledDelay(BOARD_PHY_RESET_DELAY);
#endif
while (mii_read(&ETHD1, 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(&ETHD1, MII_BMCR, mii_read(&ETHD1, 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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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_ */
/** @} */