Add software

This commit is contained in:
RocketGod
2022-09-22 09:26:57 -07:00
parent fee0ab05fd
commit 957ea3d712
4511 changed files with 1943182 additions and 0 deletions

View File

@ -0,0 +1,350 @@
/*
ChibiOS/RT - Copyright (C) 2006,2007,2008,2009,2010,
2011,2012,2013 Giovanni Di Sirio.
This file is part of ChibiOS/RT.
ChibiOS/RT is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
ChibiOS/RT is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
---
A special exception to the GPL can be applied should you wish to distribute
a combined work that includes ChibiOS/RT, without being obliged to provide
the source code for any proprietary components. See the file exception.txt
for full details of how and when the exception can be applied.
*/
/**
* @file adc.c
* @brief ADC Driver code.
*
* @addtogroup ADC
* @{
*/
#include "ch.h"
#include "hal.h"
#if HAL_USE_ADC || defined(__DOXYGEN__)
/*===========================================================================*/
/* Driver local definitions. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver exported variables. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver local variables and types. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver local functions. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver exported functions. */
/*===========================================================================*/
/**
* @brief ADC Driver initialization.
* @note This function is implicitly invoked by @p halInit(), there is
* no need to explicitly initialize the driver.
*
* @init
*/
void adcInit(void) {
adc_lld_init();
}
/**
* @brief Initializes the standard part of a @p ADCDriver structure.
*
* @param[out] adcp pointer to the @p ADCDriver object
*
* @init
*/
void adcObjectInit(ADCDriver *adcp) {
adcp->state = ADC_STOP;
adcp->config = NULL;
adcp->samples = NULL;
adcp->depth = 0;
adcp->grpp = NULL;
#if ADC_USE_WAIT
adcp->thread = NULL;
#endif /* ADC_USE_WAIT */
#if ADC_USE_MUTUAL_EXCLUSION
#if CH_USE_MUTEXES
chMtxInit(&adcp->mutex);
#else
chSemInit(&adcp->semaphore, 1);
#endif
#endif /* ADC_USE_MUTUAL_EXCLUSION */
#if defined(ADC_DRIVER_EXT_INIT_HOOK)
ADC_DRIVER_EXT_INIT_HOOK(adcp);
#endif
}
/**
* @brief Configures and activates the ADC peripheral.
*
* @param[in] adcp pointer to the @p ADCDriver object
* @param[in] config pointer to the @p ADCConfig object. Depending on
* the implementation the value can be @p NULL.
*
* @api
*/
void adcStart(ADCDriver *adcp, const ADCConfig *config) {
chDbgCheck(adcp != NULL, "adcStart");
chSysLock();
chDbgAssert((adcp->state == ADC_STOP) || (adcp->state == ADC_READY),
"adcStart(), #1", "invalid state");
adcp->config = config;
adc_lld_start(adcp);
adcp->state = ADC_READY;
chSysUnlock();
}
/**
* @brief Deactivates the ADC peripheral.
*
* @param[in] adcp pointer to the @p ADCDriver object
*
* @api
*/
void adcStop(ADCDriver *adcp) {
chDbgCheck(adcp != NULL, "adcStop");
chSysLock();
chDbgAssert((adcp->state == ADC_STOP) || (adcp->state == ADC_READY),
"adcStop(), #1", "invalid state");
adc_lld_stop(adcp);
adcp->state = ADC_STOP;
chSysUnlock();
}
/**
* @brief Starts an ADC conversion.
* @details Starts an asynchronous conversion operation.
* @note The buffer is organized as a matrix of M*N elements where M is the
* channels number configured into the conversion group and N is the
* buffer depth. The samples are sequentially written into the buffer
* with no gaps.
*
* @param[in] adcp pointer to the @p ADCDriver object
* @param[in] grpp pointer to a @p ADCConversionGroup object
* @param[out] samples pointer to the samples buffer
* @param[in] depth buffer depth (matrix rows number). The buffer depth
* must be one or an even number.
*
* @api
*/
void adcStartConversion(ADCDriver *adcp,
const ADCConversionGroup *grpp,
adcsample_t *samples,
size_t depth) {
chSysLock();
adcStartConversionI(adcp, grpp, samples, depth);
chSysUnlock();
}
/**
* @brief Starts an ADC conversion.
* @details Starts an asynchronous conversion operation.
* @post The callbacks associated to the conversion group will be invoked
* on buffer fill and error events.
* @note The buffer is organized as a matrix of M*N elements where M is the
* channels number configured into the conversion group and N is the
* buffer depth. The samples are sequentially written into the buffer
* with no gaps.
*
* @param[in] adcp pointer to the @p ADCDriver object
* @param[in] grpp pointer to a @p ADCConversionGroup object
* @param[out] samples pointer to the samples buffer
* @param[in] depth buffer depth (matrix rows number). The buffer depth
* must be one or an even number.
*
* @iclass
*/
void adcStartConversionI(ADCDriver *adcp,
const ADCConversionGroup *grpp,
adcsample_t *samples,
size_t depth) {
chDbgCheckClassI();
chDbgCheck((adcp != NULL) && (grpp != NULL) && (samples != NULL) &&
((depth == 1) || ((depth & 1) == 0)),
"adcStartConversionI");
chDbgAssert((adcp->state == ADC_READY) ||
(adcp->state == ADC_COMPLETE) ||
(adcp->state == ADC_ERROR),
"adcStartConversionI(), #1", "not ready");
adcp->samples = samples;
adcp->depth = depth;
adcp->grpp = grpp;
adcp->state = ADC_ACTIVE;
adc_lld_start_conversion(adcp);
}
/**
* @brief Stops an ongoing conversion.
* @details This function stops the currently ongoing conversion and returns
* the driver in the @p ADC_READY state. If there was no conversion
* being processed then the function does nothing.
*
* @param[in] adcp pointer to the @p ADCDriver object
*
* @api
*/
void adcStopConversion(ADCDriver *adcp) {
chDbgCheck(adcp != NULL, "adcStopConversion");
chSysLock();
chDbgAssert((adcp->state == ADC_READY) ||
(adcp->state == ADC_ACTIVE),
"adcStopConversion(), #1", "invalid state");
if (adcp->state != ADC_READY) {
adc_lld_stop_conversion(adcp);
adcp->grpp = NULL;
adcp->state = ADC_READY;
_adc_reset_s(adcp);
}
chSysUnlock();
}
/**
* @brief Stops an ongoing conversion.
* @details This function stops the currently ongoing conversion and returns
* the driver in the @p ADC_READY state. If there was no conversion
* being processed then the function does nothing.
*
* @param[in] adcp pointer to the @p ADCDriver object
*
* @iclass
*/
void adcStopConversionI(ADCDriver *adcp) {
chDbgCheckClassI();
chDbgCheck(adcp != NULL, "adcStopConversionI");
chDbgAssert((adcp->state == ADC_READY) ||
(adcp->state == ADC_ACTIVE) ||
(adcp->state == ADC_COMPLETE),
"adcStopConversionI(), #1", "invalid state");
if (adcp->state != ADC_READY) {
adc_lld_stop_conversion(adcp);
adcp->grpp = NULL;
adcp->state = ADC_READY;
_adc_reset_i(adcp);
}
}
#if ADC_USE_WAIT || defined(__DOXYGEN__)
/**
* @brief Performs an ADC conversion.
* @details Performs a synchronous conversion operation.
* @note The buffer is organized as a matrix of M*N elements where M is the
* channels number configured into the conversion group and N is the
* buffer depth. The samples are sequentially written into the buffer
* with no gaps.
*
* @param[in] adcp pointer to the @p ADCDriver object
* @param[in] grpp pointer to a @p ADCConversionGroup object
* @param[out] samples pointer to the samples buffer
* @param[in] depth buffer depth (matrix rows number). The buffer depth
* must be one or an even number.
* @return The operation result.
* @retval RDY_OK Conversion finished.
* @retval RDY_RESET The conversion has been stopped using
* @p acdStopConversion() or @p acdStopConversionI(),
* the result buffer may contain incorrect data.
* @retval RDY_TIMEOUT The conversion has been stopped because an hardware
* error.
*
* @api
*/
msg_t adcConvert(ADCDriver *adcp,
const ADCConversionGroup *grpp,
adcsample_t *samples,
size_t depth) {
msg_t msg;
chSysLock();
chDbgAssert(adcp->thread == NULL, "adcConvert(), #1", "already waiting");
adcStartConversionI(adcp, grpp, samples, depth);
adcp->thread = chThdSelf();
chSchGoSleepS(THD_STATE_SUSPENDED);
msg = chThdSelf()->p_u.rdymsg;
chSysUnlock();
return msg;
}
#endif /* ADC_USE_WAIT */
#if ADC_USE_MUTUAL_EXCLUSION || defined(__DOXYGEN__)
/**
* @brief Gains exclusive access to the ADC peripheral.
* @details This function tries to gain ownership to the ADC bus, if the bus
* is already being used then the invoking thread is queued.
* @pre In order to use this function the option
* @p ADC_USE_MUTUAL_EXCLUSION must be enabled.
*
* @param[in] adcp pointer to the @p ADCDriver object
*
* @api
*/
void adcAcquireBus(ADCDriver *adcp) {
chDbgCheck(adcp != NULL, "adcAcquireBus");
#if CH_USE_MUTEXES
chMtxLock(&adcp->mutex);
#elif CH_USE_SEMAPHORES
chSemWait(&adcp->semaphore);
#endif
}
/**
* @brief Releases exclusive access to the ADC peripheral.
* @pre In order to use this function the option
* @p ADC_USE_MUTUAL_EXCLUSION must be enabled.
*
* @param[in] adcp pointer to the @p ADCDriver object
*
* @api
*/
void adcReleaseBus(ADCDriver *adcp) {
chDbgCheck(adcp != NULL, "adcReleaseBus");
#if CH_USE_MUTEXES
(void)adcp;
chMtxUnlock();
#elif CH_USE_SEMAPHORES
chSemSignal(&adcp->semaphore);
#endif
}
#endif /* ADC_USE_MUTUAL_EXCLUSION */
#endif /* HAL_USE_ADC */
/** @} */

View File

@ -0,0 +1,292 @@
/*
ChibiOS/RT - Copyright (C) 2006,2007,2008,2009,2010,
2011,2012,2013 Giovanni Di Sirio.
This file is part of ChibiOS/RT.
ChibiOS/RT is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
ChibiOS/RT is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
---
A special exception to the GPL can be applied should you wish to distribute
a combined work that includes ChibiOS/RT, without being obliged to provide
the source code for any proprietary components. See the file exception.txt
for full details of how and when the exception can be applied.
*/
/**
* @file can.c
* @brief CAN Driver code.
*
* @addtogroup CAN
* @{
*/
#include "ch.h"
#include "hal.h"
#if HAL_USE_CAN || defined(__DOXYGEN__)
/*===========================================================================*/
/* Driver local definitions. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver exported variables. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver local variables and types. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver local functions. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver exported functions. */
/*===========================================================================*/
/**
* @brief CAN Driver initialization.
* @note This function is implicitly invoked by @p halInit(), there is
* no need to explicitly initialize the driver.
*
* @init
*/
void canInit(void) {
can_lld_init();
}
/**
* @brief Initializes the standard part of a @p CANDriver structure.
*
* @param[out] canp pointer to the @p CANDriver object
*
* @init
*/
void canObjectInit(CANDriver *canp) {
canp->state = CAN_STOP;
canp->config = NULL;
chSemInit(&canp->txsem, 0);
chSemInit(&canp->rxsem, 0);
chEvtInit(&canp->rxfull_event);
chEvtInit(&canp->txempty_event);
chEvtInit(&canp->error_event);
#if CAN_USE_SLEEP_MODE
chEvtInit(&canp->sleep_event);
chEvtInit(&canp->wakeup_event);
#endif /* CAN_USE_SLEEP_MODE */
}
/**
* @brief Configures and activates the CAN peripheral.
* @note Activating the CAN bus can be a slow operation this this function
* is not atomic, it waits internally for the initialization to
* complete.
*
* @param[in] canp pointer to the @p CANDriver object
* @param[in] config pointer to the @p CANConfig object. Depending on
* the implementation the value can be @p NULL.
*
* @api
*/
void canStart(CANDriver *canp, const CANConfig *config) {
chDbgCheck(canp != NULL, "canStart");
chSysLock();
chDbgAssert((canp->state == CAN_STOP) ||
(canp->state == CAN_STARTING) ||
(canp->state == CAN_READY),
"canStart(), #1", "invalid state");
while (canp->state == CAN_STARTING)
chThdSleepS(1);
if (canp->state == CAN_STOP) {
canp->config = config;
can_lld_start(canp);
canp->state = CAN_READY;
}
chSysUnlock();
}
/**
* @brief Deactivates the CAN peripheral.
*
* @param[in] canp pointer to the @p CANDriver object
*
* @api
*/
void canStop(CANDriver *canp) {
chDbgCheck(canp != NULL, "canStop");
chSysLock();
chDbgAssert((canp->state == CAN_STOP) || (canp->state == CAN_READY),
"canStop(), #1", "invalid state");
can_lld_stop(canp);
canp->state = CAN_STOP;
chSemResetI(&canp->rxsem, 0);
chSemResetI(&canp->txsem, 0);
chSchRescheduleS();
chSysUnlock();
}
/**
* @brief Can frame transmission.
* @details The specified frame is queued for transmission, if the hardware
* queue is full then the invoking thread is queued.
* @note Trying to transmit while in sleep mode simply enqueues the thread.
*
* @param[in] canp pointer to the @p CANDriver object
* @param[in] mailbox mailbox number, @p CAN_ANY_MAILBOX for any mailbox
* @param[in] ctfp pointer to the CAN frame to be transmitted
* @param[in] timeout the number of ticks before the operation timeouts,
* the following special values are allowed:
* - @a TIME_IMMEDIATE immediate timeout.
* - @a TIME_INFINITE no timeout.
* .
* @return The operation result.
* @retval RDY_OK the frame has been queued for transmission.
* @retval RDY_TIMEOUT The operation has timed out.
* @retval RDY_RESET The driver has been stopped while waiting.
*
* @api
*/
msg_t canTransmit(CANDriver *canp,
canmbx_t mailbox,
const CANTxFrame *ctfp,
systime_t timeout) {
chDbgCheck((canp != NULL) && (ctfp != NULL) && (mailbox <= CAN_TX_MAILBOXES),
"canTransmit");
chSysLock();
chDbgAssert((canp->state == CAN_READY) || (canp->state == CAN_SLEEP),
"canTransmit(), #1", "invalid state");
while ((canp->state == CAN_SLEEP) || !can_lld_is_tx_empty(canp, mailbox)) {
msg_t msg = chSemWaitTimeoutS(&canp->txsem, timeout);
if (msg != RDY_OK) {
chSysUnlock();
return msg;
}
}
can_lld_transmit(canp, mailbox, ctfp);
chSysUnlock();
return RDY_OK;
}
/**
* @brief Can frame receive.
* @details The function waits until a frame is received.
* @note Trying to receive while in sleep mode simply enqueues the thread.
*
* @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
* @param[in] timeout the number of ticks before the operation timeouts,
* the following special values are allowed:
* - @a TIME_IMMEDIATE immediate timeout (useful in an
* event driven scenario where a thread never blocks
* for I/O).
* - @a TIME_INFINITE no timeout.
* .
* @return The operation result.
* @retval RDY_OK a frame has been received and placed in the buffer.
* @retval RDY_TIMEOUT The operation has timed out.
* @retval RDY_RESET The driver has been stopped while waiting.
*
* @api
*/
msg_t canReceive(CANDriver *canp,
canmbx_t mailbox,
CANRxFrame *crfp,
systime_t timeout) {
chDbgCheck((canp != NULL) && (crfp != NULL) && (mailbox < CAN_RX_MAILBOXES),
"canReceive");
chSysLock();
chDbgAssert((canp->state == CAN_READY) || (canp->state == CAN_SLEEP),
"canReceive(), #1", "invalid state");
while ((canp->state == CAN_SLEEP) || !can_lld_is_rx_nonempty(canp, mailbox)) {
msg_t msg = chSemWaitTimeoutS(&canp->rxsem, timeout);
if (msg != RDY_OK) {
chSysUnlock();
return msg;
}
}
can_lld_receive(canp, mailbox, crfp);
chSysUnlock();
return RDY_OK;
}
#if CAN_USE_SLEEP_MODE || defined(__DOXYGEN__)
/**
* @brief Enters the sleep mode.
* @details This function puts the CAN driver in sleep mode and broadcasts
* the @p sleep_event event source.
* @pre In order to use this function the option @p CAN_USE_SLEEP_MODE must
* be enabled and the @p CAN_SUPPORTS_SLEEP mode must be supported
* by the low level driver.
*
* @param[in] canp pointer to the @p CANDriver object
*
* @api
*/
void canSleep(CANDriver *canp) {
chDbgCheck(canp != NULL, "canSleep");
chSysLock();
chDbgAssert((canp->state == CAN_READY) || (canp->state == CAN_SLEEP),
"canSleep(), #1", "invalid state");
if (canp->state == CAN_READY) {
can_lld_sleep(canp);
canp->state = CAN_SLEEP;
chEvtBroadcastI(&canp->sleep_event);
chSchRescheduleS();
}
chSysUnlock();
}
/**
* @brief Enforces leaving the sleep mode.
* @note The sleep mode is supposed to be usually exited automatically by
* an hardware event.
*
* @param[in] canp pointer to the @p CANDriver object
*/
void canWakeup(CANDriver *canp) {
chDbgCheck(canp != NULL, "canWakeup");
chSysLock();
chDbgAssert((canp->state == CAN_READY) || (canp->state == CAN_SLEEP),
"canWakeup(), #1", "invalid state");
if (canp->state == CAN_SLEEP) {
can_lld_wakeup(canp);
canp->state = CAN_READY;
chEvtBroadcastI(&canp->wakeup_event);
chSchRescheduleS();
}
chSysUnlock();
}
#endif /* CAN_USE_SLEEP_MODE */
#endif /* HAL_USE_CAN */
/** @} */

View File

@ -0,0 +1,214 @@
/*
ChibiOS/RT - Copyright (C) 2006,2007,2008,2009,2010,
2011,2012,2013 Giovanni Di Sirio.
This file is part of ChibiOS/RT.
ChibiOS/RT is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
ChibiOS/RT is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
---
A special exception to the GPL can be applied should you wish to distribute
a combined work that includes ChibiOS/RT, without being obliged to provide
the source code for any proprietary components. See the file exception.txt
for full details of how and when the exception can be applied.
*/
/**
* @file ext.c
* @brief EXT Driver code.
*
* @addtogroup EXT
* @{
*/
#include "ch.h"
#include "hal.h"
#if HAL_USE_EXT || defined(__DOXYGEN__)
/*===========================================================================*/
/* Driver local definitions. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver exported variables. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver local variables and types. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver local functions. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver exported functions. */
/*===========================================================================*/
/**
* @brief EXT Driver initialization.
* @note This function is implicitly invoked by @p halInit(), there is
* no need to explicitly initialize the driver.
*
* @init
*/
void extInit(void) {
ext_lld_init();
}
/**
* @brief Initializes the standard part of a @p EXTDriver structure.
*
* @param[out] extp pointer to the @p EXTDriver object
*
* @init
*/
void extObjectInit(EXTDriver *extp) {
extp->state = EXT_STOP;
extp->config = NULL;
}
/**
* @brief Configures and activates the EXT peripheral.
* @post After activation all EXT channels are in the disabled state,
* use @p extChannelEnable() in order to activate them.
*
* @param[in] extp pointer to the @p EXTDriver object
* @param[in] config pointer to the @p EXTConfig object
*
* @api
*/
void extStart(EXTDriver *extp, const EXTConfig *config) {
chDbgCheck((extp != NULL) && (config != NULL), "extStart");
chSysLock();
chDbgAssert((extp->state == EXT_STOP) || (extp->state == EXT_ACTIVE),
"extStart(), #1", "invalid state");
extp->config = config;
ext_lld_start(extp);
extp->state = EXT_ACTIVE;
chSysUnlock();
}
/**
* @brief Deactivates the EXT peripheral.
*
* @param[in] extp pointer to the @p EXTDriver object
*
* @api
*/
void extStop(EXTDriver *extp) {
chDbgCheck(extp != NULL, "extStop");
chSysLock();
chDbgAssert((extp->state == EXT_STOP) || (extp->state == EXT_ACTIVE),
"extStop(), #1", "invalid state");
ext_lld_stop(extp);
extp->state = EXT_STOP;
chSysUnlock();
}
/**
* @brief Enables an EXT channel.
* @pre The channel must not be in @p EXT_CH_MODE_DISABLED mode.
*
* @param[in] extp pointer to the @p EXTDriver object
* @param[in] channel channel to be enabled
*
* @api
*/
void extChannelEnable(EXTDriver *extp, expchannel_t channel) {
chDbgCheck((extp != NULL) && (channel < EXT_MAX_CHANNELS),
"extChannelEnable");
chSysLock();
chDbgAssert((extp->state == EXT_ACTIVE) &&
((extp->config->channels[channel].mode &
EXT_CH_MODE_EDGES_MASK) != EXT_CH_MODE_DISABLED),
"extChannelEnable(), #1", "invalid state");
extChannelEnableI(extp, channel);
chSysUnlock();
}
/**
* @brief Disables an EXT channel.
* @pre The channel must not be in @p EXT_CH_MODE_DISABLED mode.
*
* @param[in] extp pointer to the @p EXTDriver object
* @param[in] channel channel to be disabled
*
* @api
*/
void extChannelDisable(EXTDriver *extp, expchannel_t channel) {
chDbgCheck((extp != NULL) && (channel < EXT_MAX_CHANNELS),
"extChannelDisable");
chSysLock();
chDbgAssert((extp->state == EXT_ACTIVE) &&
((extp->config->channels[channel].mode &
EXT_CH_MODE_EDGES_MASK) != EXT_CH_MODE_DISABLED),
"extChannelDisable(), #1", "invalid state");
extChannelDisableI(extp, channel);
chSysUnlock();
}
/**
* @brief Changes the operation mode of a channel.
* @note This function attempts to write over the current configuration
* structure that must have been not declared constant. This
* violates the @p const qualifier in @p extStart() but it is
* intentional.
* @note This function cannot be used if the configuration structure is
* declared @p const.
* @note The effect of this function on constant configuration structures
* is not defined.
*
* @param[in] extp pointer to the @p EXTDriver object
* @param[in] channel channel to be changed
* @param[in] extcp new configuration for the channel
*
* @iclass
*/
void extSetChannelModeI(EXTDriver *extp,
expchannel_t channel,
const EXTChannelConfig *extcp) {
EXTChannelConfig *oldcp;
chDbgCheck((extp != NULL) && (channel < EXT_MAX_CHANNELS) &&
(extcp != NULL), "extSetChannelModeI");
chDbgAssert(extp->state == EXT_ACTIVE,
"extSetChannelModeI(), #1", "invalid state");
/* Note that here the access is enforced as non-const, known access
violation.*/
oldcp = (EXTChannelConfig *)&extp->config->channels[channel];
/* Overwiting the old channels configuration then the channel is reconfigured
by the low level driver.*/
*oldcp = *extcp;
ext_lld_channel_enable(extp, channel);
}
#endif /* HAL_USE_EXT */
/** @} */

View File

@ -0,0 +1,275 @@
/*
ChibiOS/RT - Copyright (C) 2006,2007,2008,2009,2010,
2011,2012,2013 Giovanni Di Sirio.
This file is part of ChibiOS/RT.
ChibiOS/RT is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
ChibiOS/RT is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
---
A special exception to the GPL can be applied should you wish to distribute
a combined work that includes ChibiOS/RT, without being obliged to provide
the source code for any proprietary components. See the file exception.txt
for full details of how and when the exception can be applied.
*/
/**
* @file gpt.c
* @brief GPT Driver code.
*
* @addtogroup GPT
* @{
*/
#include "ch.h"
#include "hal.h"
#if HAL_USE_GPT || defined(__DOXYGEN__)
/*===========================================================================*/
/* Driver local definitions. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver exported variables. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver local variables and types. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver local functions. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver exported functions. */
/*===========================================================================*/
/**
* @brief GPT Driver initialization.
* @note This function is implicitly invoked by @p halInit(), there is
* no need to explicitly initialize the driver.
*
* @init
*/
void gptInit(void) {
gpt_lld_init();
}
/**
* @brief Initializes the standard part of a @p GPTDriver structure.
*
* @param[out] gptp pointer to the @p GPTDriver object
*
* @init
*/
void gptObjectInit(GPTDriver *gptp) {
gptp->state = GPT_STOP;
gptp->config = NULL;
}
/**
* @brief Configures and activates the GPT peripheral.
*
* @param[in] gptp pointer to the @p GPTDriver object
* @param[in] config pointer to the @p GPTConfig object
*
* @api
*/
void gptStart(GPTDriver *gptp, const GPTConfig *config) {
chDbgCheck((gptp != NULL) && (config != NULL), "gptStart");
chSysLock();
chDbgAssert((gptp->state == GPT_STOP) || (gptp->state == GPT_READY),
"gptStart(), #1", "invalid state");
gptp->config = config;
gpt_lld_start(gptp);
gptp->state = GPT_READY;
chSysUnlock();
}
/**
* @brief Deactivates the GPT peripheral.
*
* @param[in] gptp pointer to the @p GPTDriver object
*
* @api
*/
void gptStop(GPTDriver *gptp) {
chDbgCheck(gptp != NULL, "gptStop");
chSysLock();
chDbgAssert((gptp->state == GPT_STOP) || (gptp->state == GPT_READY),
"gptStop(), #1", "invalid state");
gpt_lld_stop(gptp);
gptp->state = GPT_STOP;
chSysUnlock();
}
/**
* @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.
*
* @param[in] gptp pointer to a @p GPTDriver object
* @param[in] interval new cycle time in timer ticks
*
* @api
*/
void gptChangeInterval(GPTDriver *gptp, gptcnt_t interval) {
chDbgCheck(gptp != NULL, "gptChangeInterval");
chSysLock();
chDbgAssert(gptp->state == GPT_CONTINUOUS,
"gptChangeInterval(), #1", "invalid state");
gptChangeIntervalI(gptp, interval);
chSysUnlock();
}
/**
* @brief Starts the timer in continuous mode.
*
* @param[in] gptp pointer to the @p GPTDriver object
* @param[in] interval period in ticks
*
* @api
*/
void gptStartContinuous(GPTDriver *gptp, gptcnt_t interval) {
chSysLock();
gptStartContinuousI(gptp, interval);
chSysUnlock();
}
/**
* @brief Starts the timer in continuous mode.
*
* @param[in] gptp pointer to the @p GPTDriver object
* @param[in] interval period in ticks
*
* @iclass
*/
void gptStartContinuousI(GPTDriver *gptp, gptcnt_t interval) {
chDbgCheckClassI();
chDbgCheck(gptp != NULL, "gptStartContinuousI");
chDbgAssert(gptp->state == GPT_READY,
"gptStartContinuousI(), #1", "invalid state");
gptp->state = GPT_CONTINUOUS;
gpt_lld_start_timer(gptp, interval);
}
/**
* @brief Starts the timer in one shot mode.
*
* @param[in] gptp pointer to the @p GPTDriver object
* @param[in] interval time interval in ticks
*
* @api
*/
void gptStartOneShot(GPTDriver *gptp, gptcnt_t interval) {
chSysLock();
gptStartOneShotI(gptp, interval);
chSysUnlock();
}
/**
* @brief Starts the timer in one shot mode.
*
* @param[in] gptp pointer to the @p GPTDriver object
* @param[in] interval time interval in ticks
*
* @api
*/
void gptStartOneShotI(GPTDriver *gptp, gptcnt_t interval) {
chDbgCheckClassI();
chDbgCheck(gptp != NULL, "gptStartOneShotI");
chDbgAssert(gptp->state == GPT_READY,
"gptStartOneShotI(), #1", "invalid state");
gptp->state = GPT_ONESHOT;
gpt_lld_start_timer(gptp, interval);
}
/**
* @brief Stops the timer.
*
* @param[in] gptp pointer to the @p GPTDriver object
*
* @api
*/
void gptStopTimer(GPTDriver *gptp) {
chSysLock();
gptStopTimerI(gptp);
chSysUnlock();
}
/**
* @brief Stops the timer.
*
* @param[in] gptp pointer to the @p GPTDriver object
*
* @api
*/
void gptStopTimerI(GPTDriver *gptp) {
chDbgCheckClassI();
chDbgCheck(gptp != NULL, "gptStopTimerI");
chDbgAssert((gptp->state == GPT_READY) || (gptp->state == GPT_CONTINUOUS) ||
(gptp->state == GPT_ONESHOT),
"gptStopTimerI(), #1", "invalid state");
gptp->state = GPT_READY;
gpt_lld_stop_timer(gptp);
}
/**
* @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.
* @note The configured callback is not invoked when using this function.
*
* @param[in] gptp pointer to the @p GPTDriver object
* @param[in] interval time interval in ticks
*
* @api
*/
void gptPolledDelay(GPTDriver *gptp, gptcnt_t interval) {
chDbgAssert(gptp->state == GPT_READY,
"gptPolledDelay(), #1", "invalid state");
gptp->state = GPT_ONESHOT;
gpt_lld_polled_delay(gptp, interval);
gptp->state = GPT_READY;
}
#endif /* HAL_USE_GPT */
/** @} */

View File

@ -0,0 +1,201 @@
/*
ChibiOS/RT - Copyright (C) 2006,2007,2008,2009,2010,
2011,2012,2013 Giovanni Di Sirio.
This file is part of ChibiOS/RT.
ChibiOS/RT is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
ChibiOS/RT is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
---
A special exception to the GPL can be applied should you wish to distribute
a combined work that includes ChibiOS/RT, without being obliged to provide
the source code for any proprietary components. See the file exception.txt
for full details of how and when the exception can be applied.
*/
/**
* @file hal.c
* @brief HAL subsystem code.
*
* @addtogroup HAL
* @{
*/
#include "ch.h"
#include "hal.h"
/*===========================================================================*/
/* Driver local definitions. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver exported variables. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver local variables and types. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver local functions. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver exported functions. */
/*===========================================================================*/
/**
* @brief HAL initialization.
* @details This function invokes the low level initialization code then
* initializes all the drivers enabled in the HAL. Finally the
* board-specific initialization is performed by invoking
* @p boardInit() (usually defined in @p board.c).
*
* @init
*/
void halInit(void) {
hal_lld_init();
#if HAL_USE_TM || defined(__DOXYGEN__)
tmInit();
#endif
#if HAL_USE_PAL || defined(__DOXYGEN__)
palInit(&pal_default_config);
#endif
#if HAL_USE_ADC || defined(__DOXYGEN__)
adcInit();
#endif
#if HAL_USE_CAN || defined(__DOXYGEN__)
canInit();
#endif
#if HAL_USE_EXT || defined(__DOXYGEN__)
extInit();
#endif
#if HAL_USE_GPT || defined(__DOXYGEN__)
gptInit();
#endif
#if HAL_USE_I2C || defined(__DOXYGEN__)
i2cInit();
#endif
#if HAL_USE_ICU || defined(__DOXYGEN__)
icuInit();
#endif
#if HAL_USE_MAC || defined(__DOXYGEN__)
macInit();
#endif
#if HAL_USE_PWM || defined(__DOXYGEN__)
pwmInit();
#endif
#if HAL_USE_SERIAL || defined(__DOXYGEN__)
sdInit();
#endif
#if HAL_USE_SDC || defined(__DOXYGEN__)
sdcInit();
#endif
#if HAL_USE_SPI || defined(__DOXYGEN__)
spiInit();
#endif
#if HAL_USE_UART || defined(__DOXYGEN__)
uartInit();
#endif
#if HAL_USE_USB || defined(__DOXYGEN__)
usbInit();
#endif
#if HAL_USE_MMC_SPI || defined(__DOXYGEN__)
mmcInit();
#endif
#if HAL_USE_SERIAL_USB || defined(__DOXYGEN__)
sduInit();
#endif
#if HAL_USE_RTC || defined(__DOXYGEN__)
rtcInit();
#endif
/* Board specific initialization.*/
boardInit();
}
#if HAL_IMPLEMENTS_COUNTERS || defined(__DOXYGEN__)
/**
* @brief Realtime window test.
* @details This function verifies if the current realtime counter value
* lies within the specified range or not. The test takes care
* of the realtime counter wrapping to zero on overflow.
* @note When start==end then the function returns always true because the
* whole time range is specified.
* @note This is an optional service that could not be implemented in
* all HAL implementations.
* @note This function can be called from any context.
*
* @par Example 1
* Example of a guarded loop using the realtime counter. The loop implements
* a timeout after one second.
* @code
* halrtcnt_t start = halGetCounterValue();
* halrtcnt_t timeout = start + S2RTT(1);
* while (my_condition) {
* if (!halIsCounterWithin(start, timeout)
* return TIMEOUT;
* // Do something.
* }
* // Continue.
* @endcode
*
* @par Example 2
* Example of a loop that lasts exactly 50 microseconds.
* @code
* halrtcnt_t start = halGetCounterValue();
* halrtcnt_t timeout = start + US2RTT(50);
* while (halIsCounterWithin(start, timeout)) {
* // Do something.
* }
* // Continue.
* @endcode
*
* @param[in] start the start of the time window (inclusive)
* @param[in] end the end of the time window (non inclusive)
* @retval TRUE current time within the specified time window.
* @retval FALSE current time not within the specified time window.
*
* @special
*/
bool_t halIsCounterWithin(halrtcnt_t start, halrtcnt_t end) {
halrtcnt_t now = halGetCounterValue();
return end > start ? (now >= start) && (now < end) :
(now >= start) || (now < end);
}
/**
* @brief Polled delay.
* @note The real delays is always few cycles in excess of the specified
* value.
* @note This is an optional service that could not be implemented in
* all HAL implementations.
* @note This function can be called from any context.
*
* @param[in] ticks number of ticks
*
* @special
*/
void halPolledDelay(halrtcnt_t ticks) {
halrtcnt_t start = halGetCounterValue();
halrtcnt_t timeout = start + (ticks);
while (halIsCounterWithin(start, timeout))
;
}
#endif /* HAL_IMPLEMENTS_COUNTERS */
/** @} */

View File

@ -0,0 +1,310 @@
/*
ChibiOS/RT - Copyright (C) 2006,2007,2008,2009,2010,
2011,2012,2013 Giovanni Di Sirio.
This file is part of ChibiOS/RT.
ChibiOS/RT is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
ChibiOS/RT is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
---
A special exception to the GPL can be applied should you wish to distribute
a combined work that includes ChibiOS/RT, without being obliged to provide
the source code for any proprietary components. See the file exception.txt
for full details of how and when the exception can be applied.
*/
/*
Concepts and parts of this file have been contributed by Uladzimir Pylinsky
aka barthess.
*/
/**
* @file i2c.c
* @brief I2C Driver code.
*
* @addtogroup I2C
* @{
*/
#include "ch.h"
#include "hal.h"
#if HAL_USE_I2C || defined(__DOXYGEN__)
/*===========================================================================*/
/* Driver local definitions. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver exported variables. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver local variables and types. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver local functions. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver exported functions. */
/*===========================================================================*/
/**
* @brief I2C Driver initialization.
* @note This function is implicitly invoked by @p halInit(), there is
* no need to explicitly initialize the driver.
*
* @init
*/
void i2cInit(void) {
i2c_lld_init();
}
/**
* @brief Initializes the standard part of a @p I2CDriver structure.
*
* @param[out] i2cp pointer to the @p I2CDriver object
*
* @init
*/
void i2cObjectInit(I2CDriver *i2cp) {
i2cp->state = I2C_STOP;
i2cp->config = NULL;
#if I2C_USE_MUTUAL_EXCLUSION
#if CH_USE_MUTEXES
chMtxInit(&i2cp->mutex);
#else
chSemInit(&i2cp->semaphore, 1);
#endif /* CH_USE_MUTEXES */
#endif /* I2C_USE_MUTUAL_EXCLUSION */
#if defined(I2C_DRIVER_EXT_INIT_HOOK)
I2C_DRIVER_EXT_INIT_HOOK(i2cp);
#endif
}
/**
* @brief Configures and activates the I2C peripheral.
*
* @param[in] i2cp pointer to the @p I2CDriver object
* @param[in] config pointer to the @p I2CConfig object
*
* @api
*/
void i2cStart(I2CDriver *i2cp, const I2CConfig *config) {
chDbgCheck((i2cp != NULL) && (config != NULL), "i2cStart");
chDbgAssert((i2cp->state == I2C_STOP) || (i2cp->state == I2C_READY) ||
(i2cp->state == I2C_LOCKED),
"i2cStart(), #1",
"invalid state");
chSysLock();
i2cp->config = config;
i2c_lld_start(i2cp);
i2cp->state = I2C_READY;
chSysUnlock();
}
/**
* @brief Deactivates the I2C peripheral.
*
* @param[in] i2cp pointer to the @p I2CDriver object
*
* @api
*/
void i2cStop(I2CDriver *i2cp) {
chDbgCheck(i2cp != NULL, "i2cStop");
chDbgAssert((i2cp->state == I2C_STOP) || (i2cp->state == I2C_READY) ||
(i2cp->state == I2C_LOCKED),
"i2cStop(), #1",
"invalid state");
chSysLock();
i2c_lld_stop(i2cp);
i2cp->state = I2C_STOP;
chSysUnlock();
}
/**
* @brief Returns the errors mask associated to the previous operation.
*
* @param[in] i2cp pointer to the @p I2CDriver object
* @return The errors mask.
*
* @api
*/
i2cflags_t i2cGetErrors(I2CDriver *i2cp) {
chDbgCheck(i2cp != NULL, "i2cGetErrors");
return i2c_lld_get_errors(i2cp);
}
/**
* @brief Sends data via the I2C bus.
* @details Function designed to realize "read-through-write" transfer
* paradigm. If you want transmit data without any further read,
* than set @b rxbytes field to 0.
*
* @param[in] i2cp pointer to the @p I2CDriver object
* @param[in] addr slave device address (7 bits) without R/W bit
* @param[in] txbuf pointer to transmit buffer
* @param[in] txbytes number of bytes to be transmitted
* @param[out] rxbuf pointer to receive buffer
* @param[in] rxbytes number of bytes to be received, set it to 0 if
* you want transmit only
* @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.
*
* @api
*/
msg_t i2cMasterTransmitTimeout(I2CDriver *i2cp,
i2caddr_t addr,
const uint8_t *txbuf,
size_t txbytes,
uint8_t *rxbuf,
size_t rxbytes,
systime_t timeout) {
msg_t rdymsg;
chDbgCheck((i2cp != NULL) && (addr != 0) &&
(txbytes > 0) && (txbuf != NULL) &&
((rxbytes == 0) || ((rxbytes > 0) && (rxbuf != NULL))) &&
(timeout != TIME_IMMEDIATE),
"i2cMasterTransmitTimeout");
chDbgAssert(i2cp->state == I2C_READY,
"i2cMasterTransmitTimeout(), #1", "not ready");
chSysLock();
i2cp->errors = I2CD_NO_ERROR;
i2cp->state = I2C_ACTIVE_TX;
rdymsg = i2c_lld_master_transmit_timeout(i2cp, addr, txbuf, txbytes,
rxbuf, rxbytes, timeout);
if (rdymsg == RDY_TIMEOUT)
i2cp->state = I2C_LOCKED;
else
i2cp->state = I2C_READY;
chSysUnlock();
return rdymsg;
}
/**
* @brief Receives data from the I2C bus.
*
* @param[in] i2cp pointer to the @p I2CDriver object
* @param[in] addr slave device address (7 bits) without R/W bit
* @param[out] rxbuf pointer to 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.
*
* @api
*/
msg_t i2cMasterReceiveTimeout(I2CDriver *i2cp,
i2caddr_t addr,
uint8_t *rxbuf,
size_t rxbytes,
systime_t timeout){
msg_t rdymsg;
chDbgCheck((i2cp != NULL) && (addr != 0) &&
(rxbytes > 0) && (rxbuf != NULL) &&
(timeout != TIME_IMMEDIATE),
"i2cMasterReceiveTimeout");
chDbgAssert(i2cp->state == I2C_READY,
"i2cMasterReceive(), #1", "not ready");
chSysLock();
i2cp->errors = I2CD_NO_ERROR;
i2cp->state = I2C_ACTIVE_RX;
rdymsg = i2c_lld_master_receive_timeout(i2cp, addr, rxbuf, rxbytes, timeout);
if (rdymsg == RDY_TIMEOUT)
i2cp->state = I2C_LOCKED;
else
i2cp->state = I2C_READY;
chSysUnlock();
return rdymsg;
}
#if I2C_USE_MUTUAL_EXCLUSION || defined(__DOXYGEN__)
/**
* @brief Gains exclusive access to the I2C bus.
* @details This function tries to gain ownership to the I2C bus, if the bus
* is already being used then the invoking thread is queued.
* @pre In order to use this function the option @p I2C_USE_MUTUAL_EXCLUSION
* must be enabled.
*
* @param[in] i2cp pointer to the @p I2CDriver object
*
* @api
*/
void i2cAcquireBus(I2CDriver *i2cp) {
chDbgCheck(i2cp != NULL, "i2cAcquireBus");
#if CH_USE_MUTEXES
chMtxLock(&i2cp->mutex);
#elif CH_USE_SEMAPHORES
chSemWait(&i2cp->semaphore);
#endif
}
/**
* @brief Releases exclusive access to the I2C bus.
* @pre In order to use this function the option @p I2C_USE_MUTUAL_EXCLUSION
* must be enabled.
*
* @param[in] i2cp pointer to the @p I2CDriver object
*
* @api
*/
void i2cReleaseBus(I2CDriver *i2cp) {
chDbgCheck(i2cp != NULL, "i2cReleaseBus");
#if CH_USE_MUTEXES
chMtxUnlock();
#elif CH_USE_SEMAPHORES
chSemSignal(&i2cp->semaphore);
#endif
}
#endif /* I2C_USE_MUTUAL_EXCLUSION */
#endif /* HAL_USE_I2C */
/** @} */

View File

@ -0,0 +1,166 @@
/*
ChibiOS/RT - Copyright (C) 2006,2007,2008,2009,2010,
2011,2012,2013 Giovanni Di Sirio.
This file is part of ChibiOS/RT.
ChibiOS/RT is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
ChibiOS/RT is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
---
A special exception to the GPL can be applied should you wish to distribute
a combined work that includes ChibiOS/RT, without being obliged to provide
the source code for any proprietary components. See the file exception.txt
for full details of how and when the exception can be applied.
*/
/**
* @file icu.c
* @brief ICU Driver code.
*
* @addtogroup ICU
* @{
*/
#include "ch.h"
#include "hal.h"
#if HAL_USE_ICU || defined(__DOXYGEN__)
/*===========================================================================*/
/* Driver local definitions. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver exported variables. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver local variables and types. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver local functions. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver exported functions. */
/*===========================================================================*/
/**
* @brief ICU Driver initialization.
* @note This function is implicitly invoked by @p halInit(), there is
* no need to explicitly initialize the driver.
*
* @init
*/
void icuInit(void) {
icu_lld_init();
}
/**
* @brief Initializes the standard part of a @p ICUDriver structure.
*
* @param[out] icup pointer to the @p ICUDriver object
*
* @init
*/
void icuObjectInit(ICUDriver *icup) {
icup->state = ICU_STOP;
icup->config = NULL;
}
/**
* @brief Configures and activates the ICU peripheral.
*
* @param[in] icup pointer to the @p ICUDriver object
* @param[in] config pointer to the @p ICUConfig object
*
* @api
*/
void icuStart(ICUDriver *icup, const ICUConfig *config) {
chDbgCheck((icup != NULL) && (config != NULL), "icuStart");
chSysLock();
chDbgAssert((icup->state == ICU_STOP) || (icup->state == ICU_READY),
"icuStart(), #1", "invalid state");
icup->config = config;
icu_lld_start(icup);
icup->state = ICU_READY;
chSysUnlock();
}
/**
* @brief Deactivates the ICU peripheral.
*
* @param[in] icup pointer to the @p ICUDriver object
*
* @api
*/
void icuStop(ICUDriver *icup) {
chDbgCheck(icup != NULL, "icuStop");
chSysLock();
chDbgAssert((icup->state == ICU_STOP) || (icup->state == ICU_READY),
"icuStop(), #1", "invalid state");
icu_lld_stop(icup);
icup->state = ICU_STOP;
chSysUnlock();
}
/**
* @brief Enables the input capture.
*
* @param[in] icup pointer to the @p ICUDriver object
*
* @api
*/
void icuEnable(ICUDriver *icup) {
chDbgCheck(icup != NULL, "icuEnable");
chSysLock();
chDbgAssert(icup->state == ICU_READY, "icuEnable(), #1", "invalid state");
icu_lld_enable(icup);
icup->state = ICU_WAITING;
chSysUnlock();
}
/**
* @brief Disables the input capture.
*
* @param[in] icup pointer to the @p ICUDriver object
*
* @api
*/
void icuDisable(ICUDriver *icup) {
chDbgCheck(icup != NULL, "icuDisable");
chSysLock();
chDbgAssert((icup->state == ICU_READY) || (icup->state == ICU_WAITING) ||
(icup->state == ICU_ACTIVE) || (icup->state == ICU_IDLE),
"icuDisable(), #1", "invalid state");
icu_lld_disable(icup);
icup->state = ICU_READY;
chSysUnlock();
}
#endif /* HAL_USE_ICU */
/** @} */

View File

@ -0,0 +1,279 @@
/*
ChibiOS/RT - Copyright (C) 2006,2007,2008,2009,2010,
2011,2012,2013 Giovanni Di Sirio.
This file is part of ChibiOS/RT.
ChibiOS/RT is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
ChibiOS/RT is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
---
A special exception to the GPL can be applied should you wish to distribute
a combined work that includes ChibiOS/RT, without being obliged to provide
the source code for any proprietary components. See the file exception.txt
for full details of how and when the exception can be applied.
*/
/**
* @file mac.c
* @brief MAC Driver code.
*
* @addtogroup MAC
* @{
*/
#include "ch.h"
#include "hal.h"
#if HAL_USE_MAC || defined(__DOXYGEN__)
/*===========================================================================*/
/* Driver local definitions. */
/*===========================================================================*/
#if MAC_USE_ZERO_COPY && !MAC_SUPPORTS_ZERO_COPY
#error "MAC_USE_ZERO_COPY not supported by this implementation"
#endif
/*===========================================================================*/
/* Driver exported variables. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver local variables and types. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver local functions. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver interrupt handlers. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver exported functions. */
/*===========================================================================*/
/**
* @brief MAC Driver initialization.
* @note This function is implicitly invoked by @p halInit(), there is
* no need to explicitly initialize the driver.
*
* @init
*/
void macInit(void) {
mac_lld_init();
}
/**
* @brief Initialize the standard part of a @p MACDriver structure.
*
* @param[out] macp pointer to the @p MACDriver object
*
* @init
*/
void macObjectInit(MACDriver *macp) {
macp->state = MAC_STOP;
macp->config = NULL;
chSemInit(&macp->tdsem, 0);
chSemInit(&macp->rdsem, 0);
#if MAC_USE_EVENTS
chEvtInit(&macp->rdevent);
#endif
}
/**
* @brief Configures and activates the MAC peripheral.
*
* @param[in] macp pointer to the @p MACDriver object
* @param[in] config pointer to the @p MACConfig object
*
* @api
*/
void macStart(MACDriver *macp, const MACConfig *config) {
chDbgCheck((macp != NULL) && (config != NULL), "macStart");
chSysLock();
chDbgAssert(macp->state == MAC_STOP,
"macStart(), #1", "invalid state");
macp->config = config;
mac_lld_start(macp);
macp->state = MAC_ACTIVE;
chSysUnlock();
}
/**
* @brief Deactivates the MAC peripheral.
*
* @param[in] macp pointer to the @p MACDriver object
*
* @api
*/
void macStop(MACDriver *macp) {
chDbgCheck(macp != NULL, "macStop");
chSysLock();
chDbgAssert((macp->state == MAC_STOP) || (macp->state == MAC_ACTIVE),
"macStop(), #1", "invalid state");
mac_lld_stop(macp);
macp->state = MAC_STOP;
chSysUnlock();
}
/**
* @brief Allocates a transmission descriptor.
* @details One of the available transmission descriptors is locked and
* returned. If a descriptor is not currently available then the
* invoking thread is queued until one is freed.
*
* @param[in] macp pointer to the @p MACDriver object
* @param[out] tdp pointer to a @p MACTransmitDescriptor structure
* @param[in] time the number of ticks before the operation timeouts,
* the following special values are allowed:
* - @a TIME_IMMEDIATE immediate timeout.
* - @a TIME_INFINITE no timeout.
* .
* @return The operation status.
* @retval RDY_OK the descriptor was obtained.
* @retval RDY_TIMEOUT the operation timed out, descriptor not initialized.
*
* @api
*/
msg_t macWaitTransmitDescriptor(MACDriver *macp,
MACTransmitDescriptor *tdp,
systime_t time) {
msg_t msg;
systime_t now;
chDbgCheck((macp != NULL) && (tdp != NULL), "macWaitTransmitDescriptor");
chDbgAssert(macp->state == MAC_ACTIVE, "macWaitTransmitDescriptor(), #1",
"not active");
while (((msg = mac_lld_get_transmit_descriptor(macp, tdp)) != RDY_OK) &&
(time > 0)) {
chSysLock();
now = chTimeNow();
if ((msg = chSemWaitTimeoutS(&macp->tdsem, time)) == RDY_TIMEOUT) {
chSysUnlock();
break;
}
if (time != TIME_INFINITE)
time -= (chTimeNow() - now);
chSysUnlock();
}
return msg;
}
/**
* @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
*
* @api
*/
void macReleaseTransmitDescriptor(MACTransmitDescriptor *tdp) {
chDbgCheck((tdp != NULL), "macReleaseTransmitDescriptor");
mac_lld_release_transmit_descriptor(tdp);
}
/**
* @brief Waits for a received frame.
* @details Stops until a frame is received and buffered. If a frame is
* not immediately available then the invoking thread is queued
* until one is received.
*
* @param[in] macp pointer to the @p MACDriver object
* @param[out] rdp pointer to a @p MACReceiveDescriptor structure
* @param[in] time the number of ticks before the operation timeouts,
* the following special values are allowed:
* - @a TIME_IMMEDIATE immediate timeout.
* - @a TIME_INFINITE no timeout.
* .
* @return The operation status.
* @retval RDY_OK the descriptor was obtained.
* @retval RDY_TIMEOUT the operation timed out, descriptor not initialized.
*
* @api
*/
msg_t macWaitReceiveDescriptor(MACDriver *macp,
MACReceiveDescriptor *rdp,
systime_t time) {
msg_t msg;
systime_t now;
chDbgCheck((macp != NULL) && (rdp != NULL), "macWaitReceiveDescriptor");
chDbgAssert(macp->state == MAC_ACTIVE, "macWaitReceiveDescriptor(), #1",
"not active");
while (((msg = mac_lld_get_receive_descriptor(macp, rdp)) != RDY_OK) &&
(time > 0)) {
chSysLock();
now = chTimeNow();
if ((msg = chSemWaitTimeoutS(&macp->rdsem, time)) == RDY_TIMEOUT) {
chSysUnlock();
break;
}
if (time != TIME_INFINITE)
time -= (chTimeNow() - now);
chSysUnlock();
}
return msg;
}
/**
* @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
*
* @api
*/
void macReleaseReceiveDescriptor(MACReceiveDescriptor *rdp) {
chDbgCheck((rdp != NULL), "macReleaseReceiveDescriptor");
mac_lld_release_receive_descriptor(rdp);
}
/**
* @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.
*
* @api
*/
bool_t macPollLinkStatus(MACDriver *macp) {
chDbgCheck((macp != NULL), "macPollLinkStatus");
chDbgAssert(macp->state == MAC_ACTIVE, "macPollLinkStatus(), #1",
"not active");
return mac_lld_poll_link_status(macp);
}
#endif /* HAL_USE_MAC */
/** @} */

View File

@ -0,0 +1,886 @@
/*
ChibiOS/RT - Copyright (C) 2006,2007,2008,2009,2010,
2011,2012,2013 Giovanni Di Sirio.
This file is part of ChibiOS/RT.
ChibiOS/RT is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
ChibiOS/RT is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
---
A special exception to the GPL can be applied should you wish to distribute
a combined work that includes ChibiOS/RT, without being obliged to provide
the source code for any proprietary components. See the file exception.txt
for full details of how and when the exception can be applied.
*/
/*
Parts of this file have been contributed by Matthias Blaicher.
*/
/**
* @file mmc_spi.c
* @brief MMC over SPI driver code.
*
* @addtogroup MMC_SPI
* @{
*/
#include <string.h>
#include "ch.h"
#include "hal.h"
#if HAL_USE_MMC_SPI || defined(__DOXYGEN__)
/*===========================================================================*/
/* Driver local definitions. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver exported variables. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver local variables and types. */
/*===========================================================================*/
/* Forward declarations required by mmc_vmt.*/
static bool_t mmc_read(void *instance, uint32_t startblk,
uint8_t *buffer, uint32_t n);
static bool_t mmc_write(void *instance, uint32_t startblk,
const uint8_t *buffer, uint32_t n);
/**
* @brief Virtual methods table.
*/
static const struct MMCDriverVMT mmc_vmt = {
(bool_t (*)(void *))mmc_lld_is_card_inserted,
(bool_t (*)(void *))mmc_lld_is_write_protected,
(bool_t (*)(void *))mmcConnect,
(bool_t (*)(void *))mmcDisconnect,
mmc_read,
mmc_write,
(bool_t (*)(void *))mmcSync,
(bool_t (*)(void *, BlockDeviceInfo *))mmcGetInfo
};
/**
* @brief Lookup table for CRC-7 ( based on polynomial x^7 + x^3 + 1).
*/
static const uint8_t crc7_lookup_table[256] = {
0x00, 0x09, 0x12, 0x1b, 0x24, 0x2d, 0x36, 0x3f, 0x48, 0x41, 0x5a, 0x53,
0x6c, 0x65, 0x7e, 0x77, 0x19, 0x10, 0x0b, 0x02, 0x3d, 0x34, 0x2f, 0x26,
0x51, 0x58, 0x43, 0x4a, 0x75, 0x7c, 0x67, 0x6e, 0x32, 0x3b, 0x20, 0x29,
0x16, 0x1f, 0x04, 0x0d, 0x7a, 0x73, 0x68, 0x61, 0x5e, 0x57, 0x4c, 0x45,
0x2b, 0x22, 0x39, 0x30, 0x0f, 0x06, 0x1d, 0x14, 0x63, 0x6a, 0x71, 0x78,
0x47, 0x4e, 0x55, 0x5c, 0x64, 0x6d, 0x76, 0x7f, 0x40, 0x49, 0x52, 0x5b,
0x2c, 0x25, 0x3e, 0x37, 0x08, 0x01, 0x1a, 0x13, 0x7d, 0x74, 0x6f, 0x66,
0x59, 0x50, 0x4b, 0x42, 0x35, 0x3c, 0x27, 0x2e, 0x11, 0x18, 0x03, 0x0a,
0x56, 0x5f, 0x44, 0x4d, 0x72, 0x7b, 0x60, 0x69, 0x1e, 0x17, 0x0c, 0x05,
0x3a, 0x33, 0x28, 0x21, 0x4f, 0x46, 0x5d, 0x54, 0x6b, 0x62, 0x79, 0x70,
0x07, 0x0e, 0x15, 0x1c, 0x23, 0x2a, 0x31, 0x38, 0x41, 0x48, 0x53, 0x5a,
0x65, 0x6c, 0x77, 0x7e, 0x09, 0x00, 0x1b, 0x12, 0x2d, 0x24, 0x3f, 0x36,
0x58, 0x51, 0x4a, 0x43, 0x7c, 0x75, 0x6e, 0x67, 0x10, 0x19, 0x02, 0x0b,
0x34, 0x3d, 0x26, 0x2f, 0x73, 0x7a, 0x61, 0x68, 0x57, 0x5e, 0x45, 0x4c,
0x3b, 0x32, 0x29, 0x20, 0x1f, 0x16, 0x0d, 0x04, 0x6a, 0x63, 0x78, 0x71,
0x4e, 0x47, 0x5c, 0x55, 0x22, 0x2b, 0x30, 0x39, 0x06, 0x0f, 0x14, 0x1d,
0x25, 0x2c, 0x37, 0x3e, 0x01, 0x08, 0x13, 0x1a, 0x6d, 0x64, 0x7f, 0x76,
0x49, 0x40, 0x5b, 0x52, 0x3c, 0x35, 0x2e, 0x27, 0x18, 0x11, 0x0a, 0x03,
0x74, 0x7d, 0x66, 0x6f, 0x50, 0x59, 0x42, 0x4b, 0x17, 0x1e, 0x05, 0x0c,
0x33, 0x3a, 0x21, 0x28, 0x5f, 0x56, 0x4d, 0x44, 0x7b, 0x72, 0x69, 0x60,
0x0e, 0x07, 0x1c, 0x15, 0x2a, 0x23, 0x38, 0x31, 0x46, 0x4f, 0x54, 0x5d,
0x62, 0x6b, 0x70, 0x79
};
/*===========================================================================*/
/* Driver local functions. */
/*===========================================================================*/
static bool_t mmc_read(void *instance, uint32_t startblk,
uint8_t *buffer, uint32_t n) {
if (mmcStartSequentialRead((MMCDriver *)instance, startblk))
return CH_FAILED;
while (n > 0) {
if (mmcSequentialRead((MMCDriver *)instance, buffer))
return CH_FAILED;
buffer += MMCSD_BLOCK_SIZE;
n--;
}
if (mmcStopSequentialRead((MMCDriver *)instance))
return CH_FAILED;
return CH_SUCCESS;
}
static bool_t mmc_write(void *instance, uint32_t startblk,
const uint8_t *buffer, uint32_t n) {
if (mmcStartSequentialWrite((MMCDriver *)instance, startblk))
return CH_FAILED;
while (n > 0) {
if (mmcSequentialWrite((MMCDriver *)instance, buffer))
return CH_FAILED;
buffer += MMCSD_BLOCK_SIZE;
n--;
}
if (mmcStopSequentialWrite((MMCDriver *)instance))
return CH_FAILED;
return CH_SUCCESS;
}
/**
* @brief Calculate the MMC standard CRC-7 based on a lookup table.
*
* @param[in] crc start value for CRC
* @param[in] buffer pointer to data buffer
* @param[in] len length of data
* @return Calculated CRC
*/
static uint8_t crc7(uint8_t crc, const uint8_t *buffer, size_t len) {
while (len--)
crc = crc7_lookup_table[(crc << 1) ^ (*buffer++)];
return crc;
}
/**
* @brief Waits an idle condition.
*
* @param[in] mmcp pointer to the @p MMCDriver object
*
* @notapi
*/
static void wait(MMCDriver *mmcp) {
int i;
uint8_t buf[4];
for (i = 0; i < 16; i++) {
spiReceive(mmcp->config->spip, 1, buf);
if (buf[0] == 0xFF)
return;
}
/* Looks like it is a long wait.*/
while (TRUE) {
spiReceive(mmcp->config->spip, 1, buf);
if (buf[0] == 0xFF)
break;
#if MMC_NICE_WAITING
/* Trying to be nice with the other threads.*/
chThdSleep(1);
#endif
}
}
/**
* @brief Sends a command header.
*
* @param[in] mmcp pointer to the @p MMCDriver object
* @param[in] cmd the command id
* @param[in] arg the command argument
*
* @notapi
*/
static void send_hdr(MMCDriver *mmcp, uint8_t cmd, uint32_t arg) {
uint8_t buf[6];
/* Wait for the bus to become idle if a write operation was in progress.*/
wait(mmcp);
buf[0] = 0x40 | cmd;
buf[1] = arg >> 24;
buf[2] = arg >> 16;
buf[3] = arg >> 8;
buf[4] = arg;
/* Calculate CRC for command header, shift to right position, add stop bit.*/
buf[5] = ((crc7(0, buf, 5) & 0x7F) << 1) | 0x01;
spiSend(mmcp->config->spip, 6, buf);
}
/**
* @brief Receives a single byte response.
*
* @param[in] mmcp pointer to the @p MMCDriver object
* @return The response as an @p uint8_t value.
* @retval 0xFF timed out.
*
* @notapi
*/
static uint8_t recvr1(MMCDriver *mmcp) {
int i;
uint8_t r1[1];
for (i = 0; i < 9; i++) {
spiReceive(mmcp->config->spip, 1, r1);
if (r1[0] != 0xFF)
return r1[0];
}
return 0xFF;
}
/**
* @brief Receives a three byte response.
*
* @param[in] mmcp pointer to the @p MMCDriver object
* @param[out] buffer pointer to four bytes wide buffer
* @return First response byte as an @p uint8_t value.
* @retval 0xFF timed out.
*
* @notapi
*/
static uint8_t recvr3(MMCDriver *mmcp, uint8_t* buffer) {
uint8_t r1;
r1 = recvr1(mmcp);
spiReceive(mmcp->config->spip, 4, buffer);
return r1;
}
/**
* @brief Sends a command an returns a single byte response.
*
* @param[in] mmcp pointer to the @p MMCDriver object
* @param[in] cmd the command id
* @param[in] arg the command argument
* @return The response as an @p uint8_t value.
* @retval 0xFF timed out.
*
* @notapi
*/
static uint8_t send_command_R1(MMCDriver *mmcp, uint8_t cmd, uint32_t arg) {
uint8_t r1;
spiSelect(mmcp->config->spip);
send_hdr(mmcp, cmd, arg);
r1 = recvr1(mmcp);
spiUnselect(mmcp->config->spip);
return r1;
}
/**
* @brief Sends a command which returns a five bytes response (R3).
*
* @param[in] mmcp pointer to the @p MMCDriver object
* @param[in] cmd the command id
* @param[in] arg the command argument
* @param[out] response pointer to four bytes wide uint8_t buffer
* @return The first byte of the response (R1) as an @p
* uint8_t value.
* @retval 0xFF timed out.
*
* @notapi
*/
static uint8_t send_command_R3(MMCDriver *mmcp, uint8_t cmd, uint32_t arg,
uint8_t *response) {
uint8_t r1;
spiSelect(mmcp->config->spip);
send_hdr(mmcp, cmd, arg);
r1 = recvr3(mmcp, response);
spiUnselect(mmcp->config->spip);
return r1;
}
/**
* @brief Reads the CSD.
*
* @param[in] mmcp pointer to the @p MMCDriver object
* @param[out] csd pointer to the CSD buffer
*
* @return The operation status.
* @retval CH_SUCCESS the operation succeeded.
* @retval CH_FAILED the operation failed.
*
* @notapi
*/
static bool_t read_CxD(MMCDriver *mmcp, uint8_t cmd, uint32_t cxd[4]) {
unsigned i;
uint8_t *bp, buf[16];
spiSelect(mmcp->config->spip);
send_hdr(mmcp, cmd, 0);
if (recvr1(mmcp) != 0x00) {
spiUnselect(mmcp->config->spip);
return CH_FAILED;
}
/* Wait for data availability.*/
for (i = 0; i < MMC_WAIT_DATA; i++) {
spiReceive(mmcp->config->spip, 1, buf);
if (buf[0] == 0xFE) {
uint32_t *wp;
spiReceive(mmcp->config->spip, 16, buf);
bp = buf;
for (wp = &cxd[3]; wp >= cxd; wp--) {
*wp = ((uint32_t)bp[0] << 24) | ((uint32_t)bp[1] << 16) |
((uint32_t)bp[2] << 8) | (uint32_t)bp[3];
bp += 4;
}
/* CRC ignored then end of transaction. */
spiIgnore(mmcp->config->spip, 2);
spiUnselect(mmcp->config->spip);
return CH_SUCCESS;
}
}
return CH_FAILED;
}
/**
* @brief Waits that the card reaches an idle state.
*
* @param[in] mmcp pointer to the @p MMCDriver object
*
* @notapi
*/
static void sync(MMCDriver *mmcp) {
uint8_t buf[1];
spiSelect(mmcp->config->spip);
while (TRUE) {
spiReceive(mmcp->config->spip, 1, buf);
if (buf[0] == 0xFF)
break;
#if MMC_NICE_WAITING
chThdSleep(1); /* Trying to be nice with the other threads.*/
#endif
}
spiUnselect(mmcp->config->spip);
}
/*===========================================================================*/
/* Driver exported functions. */
/*===========================================================================*/
/**
* @brief MMC over SPI driver initialization.
* @note This function is implicitly invoked by @p halInit(), there is
* no need to explicitly initialize the driver.
*
* @init
*/
void mmcInit(void) {
}
/**
* @brief Initializes an instance.
*
* @param[out] mmcp pointer to the @p MMCDriver object
*
* @init
*/
void mmcObjectInit(MMCDriver *mmcp) {
mmcp->vmt = &mmc_vmt;
mmcp->state = BLK_STOP;
mmcp->config = NULL;
mmcp->block_addresses = FALSE;
}
/**
* @brief Configures and activates the MMC peripheral.
*
* @param[in] mmcp pointer to the @p MMCDriver object
* @param[in] config pointer to the @p MMCConfig object.
*
* @api
*/
void mmcStart(MMCDriver *mmcp, const MMCConfig *config) {
chDbgCheck((mmcp != NULL) && (config != NULL), "mmcStart");
chDbgAssert((mmcp->state == BLK_STOP) || (mmcp->state == BLK_ACTIVE),
"mmcStart(), #1", "invalid state");
mmcp->config = config;
mmcp->state = BLK_ACTIVE;
}
/**
* @brief Disables the MMC peripheral.
*
* @param[in] mmcp pointer to the @p MMCDriver object
*
* @api
*/
void mmcStop(MMCDriver *mmcp) {
chDbgCheck(mmcp != NULL, "mmcStop");
chDbgAssert((mmcp->state == BLK_STOP) || (mmcp->state == BLK_ACTIVE),
"mmcStop(), #1", "invalid state");
spiStop(mmcp->config->spip);
mmcp->state = BLK_STOP;
}
/**
* @brief Performs the initialization procedure on the inserted card.
* @details This function should be invoked when a card is inserted and
* brings the driver in the @p MMC_READY state where it is possible
* to perform read and write operations.
* @note It is possible to invoke this function from the insertion event
* handler.
*
* @param[in] mmcp pointer to the @p MMCDriver object
*
* @return The operation status.
* @retval CH_SUCCESS the operation succeeded and the driver is now
* in the @p MMC_READY state.
* @retval CH_FAILED the operation failed.
*
* @api
*/
bool_t mmcConnect(MMCDriver *mmcp) {
unsigned i;
uint8_t r3[4];
chDbgCheck(mmcp != NULL, "mmcConnect");
chDbgAssert((mmcp->state == BLK_ACTIVE) || (mmcp->state == BLK_READY),
"mmcConnect(), #1", "invalid state");
/* Connection procedure in progress.*/
mmcp->state = BLK_CONNECTING;
mmcp->block_addresses = FALSE;
/* Slow clock mode and 128 clock pulses.*/
spiStart(mmcp->config->spip, mmcp->config->lscfg);
spiIgnore(mmcp->config->spip, 16);
/* SPI mode selection.*/
i = 0;
while (TRUE) {
if (send_command_R1(mmcp, MMCSD_CMD_GO_IDLE_STATE, 0) == 0x01)
break;
if (++i >= MMC_CMD0_RETRY)
goto failed;
chThdSleepMilliseconds(10);
}
/* Try to detect if this is a high capacity card and switch to block
addresses if possible.
This method is based on "How to support SDC Ver2 and high capacity cards"
by ElmChan.*/
if (send_command_R3(mmcp, MMCSD_CMD_SEND_IF_COND,
MMCSD_CMD8_PATTERN, r3) != 0x05) {
/* Switch to SDHC mode.*/
i = 0;
while (TRUE) {
if ((send_command_R1(mmcp, MMCSD_CMD_APP_CMD, 0) == 0x01) &&
(send_command_R3(mmcp, MMCSD_CMD_APP_OP_COND,
0x400001aa, r3) == 0x00))
break;
if (++i >= MMC_ACMD41_RETRY)
goto failed;
chThdSleepMilliseconds(10);
}
/* Execute dedicated read on OCR register */
send_command_R3(mmcp, MMCSD_CMD_READ_OCR, 0, r3);
/* Check if CCS is set in response. Card operates in block mode if set.*/
if (r3[0] & 0x40)
mmcp->block_addresses = TRUE;
}
/* Initialization.*/
i = 0;
while (TRUE) {
uint8_t b = send_command_R1(mmcp, MMCSD_CMD_INIT, 0);
if (b == 0x00)
break;
if (b != 0x01)
goto failed;
if (++i >= MMC_CMD1_RETRY)
goto failed;
chThdSleepMilliseconds(10);
}
/* Initialization complete, full speed.*/
spiStart(mmcp->config->spip, mmcp->config->hscfg);
/* Setting block size.*/
if (send_command_R1(mmcp, MMCSD_CMD_SET_BLOCKLEN,
MMCSD_BLOCK_SIZE) != 0x00)
goto failed;
/* Determine capacity.*/
if (read_CxD(mmcp, MMCSD_CMD_SEND_CSD, mmcp->csd))
goto failed;
mmcp->capacity = mmcsdGetCapacity(mmcp->csd);
if (mmcp->capacity == 0)
goto failed;
if (read_CxD(mmcp, MMCSD_CMD_SEND_CID, mmcp->cid))
goto failed;
mmcp->state = BLK_READY;
return CH_SUCCESS;
/* Connection failed, state reset to BLK_ACTIVE.*/
failed:
spiStop(mmcp->config->spip);
mmcp->state = BLK_ACTIVE;
return CH_FAILED;
}
/**
* @brief Brings the driver in a state safe for card removal.
*
* @param[in] mmcp pointer to the @p MMCDriver object
* @return The operation status.
*
* @retval CH_SUCCESS the operation succeeded and the driver is now
* in the @p MMC_INSERTED state.
* @retval CH_FAILED the operation failed.
*
* @api
*/
bool_t mmcDisconnect(MMCDriver *mmcp) {
chDbgCheck(mmcp != NULL, "mmcDisconnect");
chSysLock();
chDbgAssert((mmcp->state == BLK_ACTIVE) || (mmcp->state == BLK_READY),
"mmcDisconnect(), #1", "invalid state");
if (mmcp->state == BLK_ACTIVE) {
chSysUnlock();
return CH_SUCCESS;
}
mmcp->state = BLK_DISCONNECTING;
chSysUnlock();
/* Wait for the pending write operations to complete.*/
spiStart(mmcp->config->spip, mmcp->config->hscfg);
sync(mmcp);
spiStop(mmcp->config->spip);
mmcp->state = BLK_ACTIVE;
return CH_SUCCESS;
}
/**
* @brief Starts a sequential read.
*
* @param[in] mmcp pointer to the @p MMCDriver object
* @param[in] startblk first block to read
*
* @return The operation status.
* @retval CH_SUCCESS the operation succeeded.
* @retval CH_FAILED the operation failed.
*
* @api
*/
bool_t mmcStartSequentialRead(MMCDriver *mmcp, uint32_t startblk) {
chDbgCheck(mmcp != NULL, "mmcStartSequentialRead");
chDbgAssert(mmcp->state == BLK_READY,
"mmcStartSequentialRead(), #1", "invalid state");
/* Read operation in progress.*/
mmcp->state = BLK_READING;
/* (Re)starting the SPI in case it has been reprogrammed externally, it can
happen if the SPI bus is shared among multiple peripherals.*/
spiStart(mmcp->config->spip, mmcp->config->hscfg);
spiSelect(mmcp->config->spip);
if (mmcp->block_addresses)
send_hdr(mmcp, MMCSD_CMD_READ_MULTIPLE_BLOCK, startblk);
else
send_hdr(mmcp, MMCSD_CMD_READ_MULTIPLE_BLOCK, startblk * MMCSD_BLOCK_SIZE);
if (recvr1(mmcp) != 0x00) {
spiStop(mmcp->config->spip);
mmcp->state = BLK_READY;
return CH_FAILED;
}
return CH_SUCCESS;
}
/**
* @brief Reads a block within a sequential read operation.
*
* @param[in] mmcp pointer to the @p MMCDriver object
* @param[out] buffer pointer to the read buffer
*
* @return The operation status.
* @retval CH_SUCCESS the operation succeeded.
* @retval CH_FAILED the operation failed.
*
* @api
*/
bool_t mmcSequentialRead(MMCDriver *mmcp, uint8_t *buffer) {
int i;
chDbgCheck((mmcp != NULL) && (buffer != NULL), "mmcSequentialRead");
if (mmcp->state != BLK_READING)
return CH_FAILED;
for (i = 0; i < MMC_WAIT_DATA; i++) {
spiReceive(mmcp->config->spip, 1, buffer);
if (buffer[0] == 0xFE) {
spiReceive(mmcp->config->spip, MMCSD_BLOCK_SIZE, buffer);
/* CRC ignored. */
spiIgnore(mmcp->config->spip, 2);
return CH_SUCCESS;
}
}
/* Timeout.*/
spiUnselect(mmcp->config->spip);
spiStop(mmcp->config->spip);
mmcp->state = BLK_READY;
return CH_FAILED;
}
/**
* @brief Stops a sequential read gracefully.
*
* @param[in] mmcp pointer to the @p MMCDriver object
*
* @return The operation status.
* @retval CH_SUCCESS the operation succeeded.
* @retval CH_FAILED the operation failed.
*
* @api
*/
bool_t mmcStopSequentialRead(MMCDriver *mmcp) {
static const uint8_t stopcmd[] = {0x40 | MMCSD_CMD_STOP_TRANSMISSION,
0, 0, 0, 0, 1, 0xFF};
chDbgCheck(mmcp != NULL, "mmcStopSequentialRead");
if (mmcp->state != BLK_READING)
return CH_FAILED;
spiSend(mmcp->config->spip, sizeof(stopcmd), stopcmd);
/* result = recvr1(mmcp) != 0x00;*/
/* Note, ignored r1 response, it can be not zero, unknown issue.*/
(void) recvr1(mmcp);
/* Read operation finished.*/
spiUnselect(mmcp->config->spip);
mmcp->state = BLK_READY;
return CH_SUCCESS;
}
/**
* @brief Starts a sequential write.
*
* @param[in] mmcp pointer to the @p MMCDriver object
* @param[in] startblk first block to write
*
* @return The operation status.
* @retval CH_SUCCESS the operation succeeded.
* @retval CH_FAILED the operation failed.
*
* @api
*/
bool_t mmcStartSequentialWrite(MMCDriver *mmcp, uint32_t startblk) {
chDbgCheck(mmcp != NULL, "mmcStartSequentialWrite");
chDbgAssert(mmcp->state == BLK_READY,
"mmcStartSequentialWrite(), #1", "invalid state");
/* Write operation in progress.*/
mmcp->state = BLK_WRITING;
spiStart(mmcp->config->spip, mmcp->config->hscfg);
spiSelect(mmcp->config->spip);
if (mmcp->block_addresses)
send_hdr(mmcp, MMCSD_CMD_WRITE_MULTIPLE_BLOCK, startblk);
else
send_hdr(mmcp, MMCSD_CMD_WRITE_MULTIPLE_BLOCK,
startblk * MMCSD_BLOCK_SIZE);
if (recvr1(mmcp) != 0x00) {
spiStop(mmcp->config->spip);
mmcp->state = BLK_READY;
return CH_FAILED;
}
return CH_SUCCESS;
}
/**
* @brief Writes a block within a sequential write operation.
*
* @param[in] mmcp pointer to the @p MMCDriver object
* @param[out] buffer pointer to the write buffer
*
* @return The operation status.
* @retval CH_SUCCESS the operation succeeded.
* @retval CH_FAILED the operation failed.
*
* @api
*/
bool_t mmcSequentialWrite(MMCDriver *mmcp, const uint8_t *buffer) {
static const uint8_t start[] = {0xFF, 0xFC};
uint8_t b[1];
chDbgCheck((mmcp != NULL) && (buffer != NULL), "mmcSequentialWrite");
if (mmcp->state != BLK_WRITING)
return CH_FAILED;
spiSend(mmcp->config->spip, sizeof(start), start); /* Data prologue. */
spiSend(mmcp->config->spip, MMCSD_BLOCK_SIZE, buffer);/* Data. */
spiIgnore(mmcp->config->spip, 2); /* CRC ignored. */
spiReceive(mmcp->config->spip, 1, b);
if ((b[0] & 0x1F) == 0x05) {
wait(mmcp);
return CH_SUCCESS;
}
/* Error.*/
spiUnselect(mmcp->config->spip);
spiStop(mmcp->config->spip);
mmcp->state = BLK_READY;
return CH_FAILED;
}
/**
* @brief Stops a sequential write gracefully.
*
* @param[in] mmcp pointer to the @p MMCDriver object
*
* @return The operation status.
* @retval CH_SUCCESS the operation succeeded.
* @retval CH_FAILED the operation failed.
*
* @api
*/
bool_t mmcStopSequentialWrite(MMCDriver *mmcp) {
static const uint8_t stop[] = {0xFD, 0xFF};
chDbgCheck(mmcp != NULL, "mmcStopSequentialWrite");
if (mmcp->state != BLK_WRITING)
return CH_FAILED;
spiSend(mmcp->config->spip, sizeof(stop), stop);
spiUnselect(mmcp->config->spip);
/* Write operation finished.*/
mmcp->state = BLK_READY;
return CH_SUCCESS;
}
/**
* @brief Waits for card idle condition.
*
* @param[in] mmcp pointer to the @p MMCDriver object
*
* @return The operation status.
* @retval CH_SUCCESS the operation succeeded.
* @retval CH_FAILED the operation failed.
*
* @api
*/
bool_t mmcSync(MMCDriver *mmcp) {
chDbgCheck(mmcp != NULL, "mmcSync");
if (mmcp->state != BLK_READY)
return CH_FAILED;
/* Synchronization operation in progress.*/
mmcp->state = BLK_SYNCING;
spiStart(mmcp->config->spip, mmcp->config->hscfg);
sync(mmcp);
/* Synchronization operation finished.*/
mmcp->state = BLK_READY;
return CH_SUCCESS;
}
/**
* @brief Returns the media info.
*
* @param[in] mmcp pointer to the @p MMCDriver object
* @param[out] bdip pointer to a @p BlockDeviceInfo structure
*
* @return The operation status.
* @retval CH_SUCCESS the operation succeeded.
* @retval CH_FAILED the operation failed.
*
* @api
*/
bool_t mmcGetInfo(MMCDriver *mmcp, BlockDeviceInfo *bdip) {
chDbgCheck((mmcp != NULL) && (bdip != NULL), "mmcGetInfo");
if (mmcp->state != BLK_READY)
return CH_FAILED;
bdip->blk_num = mmcp->capacity;
bdip->blk_size = MMCSD_BLOCK_SIZE;
return CH_SUCCESS;
}
/**
* @brief Erases blocks.
*
* @param[in] mmcp pointer to the @p MMCDriver object
* @param[in] startblk starting block number
* @param[in] endblk ending block number
*
* @return The operation status.
* @retval CH_SUCCESS the operation succeeded.
* @retval CH_FAILED the operation failed.
*
* @api
*/
bool_t mmcErase(MMCDriver *mmcp, uint32_t startblk, uint32_t endblk) {
chDbgCheck((mmcp != NULL), "mmcErase");
/* Erase operation in progress.*/
mmcp->state = BLK_WRITING;
/* Handling command differences between HC and normal cards.*/
if (!mmcp->block_addresses) {
startblk *= MMCSD_BLOCK_SIZE;
endblk *= MMCSD_BLOCK_SIZE;
}
if (send_command_R1(mmcp, MMCSD_CMD_ERASE_RW_BLK_START, startblk))
goto failed;
if (send_command_R1(mmcp, MMCSD_CMD_ERASE_RW_BLK_END, endblk))
goto failed;
if (send_command_R1(mmcp, MMCSD_CMD_ERASE, 0))
goto failed;
mmcp->state = BLK_READY;
return CH_SUCCESS;
/* Command failed, state reset to BLK_ACTIVE.*/
failed:
spiStop(mmcp->config->spip);
mmcp->state = BLK_READY;
return CH_FAILED;
}
#endif /* HAL_USE_MMC_SPI */
/** @} */

View File

@ -0,0 +1,121 @@
/*
ChibiOS/RT - Copyright (C) 2006,2007,2008,2009,2010,
2011,2012,2013 Giovanni Di Sirio.
This file is part of ChibiOS/RT.
ChibiOS/RT is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
ChibiOS/RT is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
---
A special exception to the GPL can be applied should you wish to distribute
a combined work that includes ChibiOS/RT, without being obliged to provide
the source code for any proprietary components. See the file exception.txt
for full details of how and when the exception can be applied.
*/
/**
* @file mmcsd.c
* @brief MMC/SD cards common code.
*
* @addtogroup MMCSD
* @{
*/
#include "ch.h"
#include "hal.h"
#if HAL_USE_MMC_SPI || HAL_USE_SDC || defined(__DOXYGEN__)
/*===========================================================================*/
/* Driver local definitions. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver exported variables. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver local variables and types. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver local functions. */
/*===========================================================================*/
/**
* @brief Gets a bit field from a words array.
* @note The bit zero is the LSb of the first word.
*
* @param[in] data pointer to the words array
* @param[in] end bit offset of the last bit of the field, inclusive
* @param[in] start bit offset of the first bit of the field, inclusive
*
* @return The bits field value, left aligned.
*
* @notapi
*/
static uint32_t mmcsd_get_slice(uint32_t *data, uint32_t end, uint32_t start) {
unsigned startidx, endidx, startoff;
uint32_t endmask;
chDbgCheck((end >= start) && ((end - start) < 32), "mmcsd_get_slice");
startidx = start / 32;
startoff = start % 32;
endidx = end / 32;
endmask = (1 << ((end % 32) + 1)) - 1;
/* One or two pieces?*/
if (startidx < endidx)
return (data[startidx] >> startoff) | /* Two pieces case. */
((data[endidx] & endmask) << (32 - startoff));
return (data[startidx] & endmask) >> startoff; /* One piece case. */
}
/*===========================================================================*/
/* Driver exported functions. */
/*===========================================================================*/
/**
* @brief Extract card capacity from a CSD.
* @details The capacity is returned as number of available blocks.
*
* @param[in] csd the CSD record
*
* @return The card capacity.
* @retval 0 CSD format error
*/
uint32_t mmcsdGetCapacity(uint32_t csd[4]) {
switch (csd[3] >> 30) {
uint32_t a, b, c;
case 0:
/* CSD version 1.0 */
a = mmcsd_get_slice(csd, MMCSD_CSD_10_C_SIZE_SLICE);
b = mmcsd_get_slice(csd, MMCSD_CSD_10_C_SIZE_MULT_SLICE);
c = mmcsd_get_slice(csd, MMCSD_CSD_10_READ_BL_LEN_SLICE);
return (a + 1) << (b + 2) << (c - 9); /* 2^9 == MMCSD_BLOCK_SIZE. */
case 1:
/* CSD version 2.0.*/
return 1024 * (mmcsd_get_slice(csd, MMCSD_CSD_20_C_SIZE_SLICE) + 1);
default:
/* Reserved value detected.*/
return 0;
}
}
#endif /* HAL_USE_MMC_SPI || HAL_USE_SDC */
/** @} */

View File

@ -0,0 +1,137 @@
/*
ChibiOS/RT - Copyright (C) 2006,2007,2008,2009,2010,
2011,2012,2013 Giovanni Di Sirio.
This file is part of ChibiOS/RT.
ChibiOS/RT is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
ChibiOS/RT is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
---
A special exception to the GPL can be applied should you wish to distribute
a combined work that includes ChibiOS/RT, without being obliged to provide
the source code for any proprietary components. See the file exception.txt
for full details of how and when the exception can be applied.
*/
/**
* @file pal.c
* @brief I/O Ports Abstraction Layer code.
*
* @addtogroup PAL
* @{
*/
#include "ch.h"
#include "hal.h"
#if HAL_USE_PAL || defined(__DOXYGEN__)
/*===========================================================================*/
/* Driver local definitions. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver exported variables. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver local variables and types. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver local functions. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver exported functions. */
/*===========================================================================*/
/**
* @brief Read from an I/O bus.
* @note The operation is not guaranteed to be atomic on all the
* architectures, for atomicity and/or portability reasons you may
* need to enclose port I/O operations between @p chSysLock() and
* @p chSysUnlock().
* @note The function internally uses the @p palReadGroup() macro. The use
* of this function is preferred when you value code size, readability
* and error checking over speed.
* @note The function can be called from any context.
*
* @param[in] bus the I/O bus, pointer to a @p IOBus structure
* @return The bus logical states.
*
* @special
*/
ioportmask_t palReadBus(IOBus *bus) {
chDbgCheck((bus != NULL) && (bus->offset < PAL_IOPORTS_WIDTH),
"palReadBus");
return palReadGroup(bus->portid, bus->mask, bus->offset);
}
/**
* @brief Write to an I/O bus.
* @note The operation is not guaranteed to be atomic on all the
* architectures, for atomicity and/or portability reasons you may
* need to enclose port I/O operations between @p chSysLock() and
* @p chSysUnlock().
* @note The default implementation is non atomic and not necessarily
* optimal. Low level drivers may optimize the function by using
* specific hardware or coding.
* @note The function can be called from any context.
*
* @param[in] bus the I/O bus, pointer to a @p IOBus structure
* @param[in] bits the bits to be written on the I/O bus. Values exceeding
* the bus width are masked so most significant bits are
* lost.
*
* @special
*/
void palWriteBus(IOBus *bus, ioportmask_t bits) {
chDbgCheck((bus != NULL) && (bus->offset < PAL_IOPORTS_WIDTH),
"palWriteBus");
palWriteGroup(bus->portid, bus->mask, bus->offset, bits);
}
/**
* @brief Programs a bus with the specified mode.
* @note The operation is not guaranteed to be atomic on all the
* architectures, for atomicity and/or portability reasons you may
* need to enclose port I/O operations between @p chSysLock() and
* @p chSysUnlock().
* @note The default implementation is non atomic and not necessarily
* optimal. Low level drivers may optimize the function by using
* specific hardware or coding.
* @note The function can be called from any context.
*
* @param[in] bus the I/O bus, pointer to a @p IOBus structure
* @param[in] mode the mode
*
* @special
*/
void palSetBusMode(IOBus *bus, iomode_t mode) {
chDbgCheck((bus != NULL) && (bus->offset < PAL_IOPORTS_WIDTH),
"palSetBusMode");
palSetGroupMode(bus->portid, bus->mask, bus->offset, mode);
}
#endif /* HAL_USE_PAL */
/** @} */

View File

@ -0,0 +1,214 @@
/*
ChibiOS/RT - Copyright (C) 2006,2007,2008,2009,2010,
2011,2012,2013 Giovanni Di Sirio.
This file is part of ChibiOS/RT.
ChibiOS/RT is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
ChibiOS/RT is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
---
A special exception to the GPL can be applied should you wish to distribute
a combined work that includes ChibiOS/RT, without being obliged to provide
the source code for any proprietary components. See the file exception.txt
for full details of how and when the exception can be applied.
*/
/**
* @file pwm.c
* @brief PWM Driver code.
*
* @addtogroup PWM
* @{
*/
#include "ch.h"
#include "hal.h"
#if HAL_USE_PWM || defined(__DOXYGEN__)
/*===========================================================================*/
/* Driver local definitions. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver exported variables. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver local variables and types. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver local functions. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver exported functions. */
/*===========================================================================*/
/**
* @brief PWM Driver initialization.
* @note This function is implicitly invoked by @p halInit(), there is
* no need to explicitly initialize the driver.
*
* @init
*/
void pwmInit(void) {
pwm_lld_init();
}
/**
* @brief Initializes the standard part of a @p PWMDriver structure.
*
* @param[out] pwmp pointer to a @p PWMDriver object
*
* @init
*/
void pwmObjectInit(PWMDriver *pwmp) {
pwmp->state = PWM_STOP;
pwmp->config = NULL;
#if defined(PWM_DRIVER_EXT_INIT_HOOK)
PWM_DRIVER_EXT_INIT_HOOK(pwmp);
#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
* @param[in] config pointer to a @p PWMConfig object
*
* @api
*/
void pwmStart(PWMDriver *pwmp, const PWMConfig *config) {
chDbgCheck((pwmp != NULL) && (config != NULL), "pwmStart");
chSysLock();
chDbgAssert((pwmp->state == PWM_STOP) || (pwmp->state == PWM_READY),
"pwmStart(), #1", "invalid state");
pwmp->config = config;
pwmp->period = config->period;
pwm_lld_start(pwmp);
pwmp->state = PWM_READY;
chSysUnlock();
}
/**
* @brief Deactivates the PWM peripheral.
*
* @param[in] pwmp pointer to a @p PWMDriver object
*
* @api
*/
void pwmStop(PWMDriver *pwmp) {
chDbgCheck(pwmp != NULL, "pwmStop");
chSysLock();
chDbgAssert((pwmp->state == PWM_STOP) || (pwmp->state == PWM_READY),
"pwmStop(), #1", "invalid state");
pwm_lld_stop(pwmp);
pwmp->state = PWM_STOP;
chSysUnlock();
}
/**
* @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 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
*
* @api
*/
void pwmChangePeriod(PWMDriver *pwmp, pwmcnt_t period) {
chDbgCheck(pwmp != NULL, "pwmChangePeriod");
chSysLock();
chDbgAssert(pwmp->state == PWM_READY,
"pwmChangePeriod(), #1", "invalid state");
pwmChangePeriodI(pwmp, period);
chSysUnlock();
}
/**
* @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 Depending on the hardware implementation this function has
* effect starting on the next cycle (recommended implementation)
* or immediately (fallback implementation).
*
* @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
*
* @api
*/
void pwmEnableChannel(PWMDriver *pwmp,
pwmchannel_t channel,
pwmcnt_t width) {
chDbgCheck((pwmp != NULL) && (channel < PWM_CHANNELS),
"pwmEnableChannel");
chSysLock();
chDbgAssert(pwmp->state == PWM_READY,
"pwmEnableChannel(), #1", "not ready");
pwm_lld_enable_channel(pwmp, channel, width);
chSysUnlock();
}
/**
* @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 Depending on the hardware implementation this function has
* effect starting on the next cycle (recommended implementation)
* or immediately (fallback implementation).
*
* @param[in] pwmp pointer to a @p PWMDriver object
* @param[in] channel PWM channel identifier (0...PWM_CHANNELS-1)
*
* @api
*/
void pwmDisableChannel(PWMDriver *pwmp, pwmchannel_t channel) {
chDbgCheck((pwmp != NULL) && (channel < PWM_CHANNELS),
"pwmEnableChannel");
chSysLock();
chDbgAssert(pwmp->state == PWM_READY,
"pwmDisableChannel(), #1", "not ready");
pwm_lld_disable_channel(pwmp, channel);
chSysUnlock();
}
#endif /* HAL_USE_PWM */
/** @} */

View File

@ -0,0 +1,193 @@
/*
ChibiOS/RT - Copyright (C) 2006,2007,2008,2009,2010,
2011,2012,2013 Giovanni Di Sirio.
This file is part of ChibiOS/RT.
ChibiOS/RT is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
ChibiOS/RT is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
---
A special exception to the GPL can be applied should you wish to distribute
a combined work that includes ChibiOS/RT, without being obliged to provide
the source code for any proprietary components. See the file exception.txt
for full details of how and when the exception can be applied.
*/
/*
Concepts and parts of this file have been contributed by Uladzimir Pylinsky
aka barthess.
*/
/**
* @file rtc.c
* @brief RTC Driver code.
*
* @addtogroup RTC
* @{
*/
#include "ch.h"
#include "hal.h"
#if HAL_USE_RTC || defined(__DOXYGEN__)
/*===========================================================================*/
/* Driver local definitions. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver exported variables. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver local variables and types. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver local functions. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver exported functions. */
/*===========================================================================*/
/**
* @brief RTC Driver initialization.
* @note This function is implicitly invoked by @p halInit(), there is
* no need to explicitly initialize the driver.
*
* @init
*/
void rtcInit(void) {
rtc_lld_init();
}
/**
* @brief Set current time.
*
* @param[in] rtcp pointer to RTC driver structure
* @param[in] timespec pointer to a @p RTCTime structure
*
* @api
*/
void rtcSetTime(RTCDriver *rtcp, const RTCTime *timespec) {
chDbgCheck((rtcp != NULL) && (timespec != NULL), "rtcSetTime");
chSysLock();
rtcSetTimeI(rtcp, timespec);
chSysUnlock();
}
/**
* @brief Get current time.
*
* @param[in] rtcp pointer to RTC driver structure
* @param[out] timespec pointer to a @p RTCTime structure
*
* @api
*/
void rtcGetTime(RTCDriver *rtcp, RTCTime *timespec) {
chDbgCheck((rtcp != NULL) && (timespec != NULL), "rtcGetTime");
chSysLock();
rtcGetTimeI(rtcp, timespec);
chSysUnlock();
}
#if (RTC_ALARMS > 0) || defined(__DOXYGEN__)
/**
* @brief Set alarm time.
*
* @param[in] rtcp pointer to RTC driver structure
* @param[in] alarm alarm identifier
* @param[in] alarmspec pointer to a @p RTCAlarm structure or @p NULL
*
* @api
*/
void rtcSetAlarm(RTCDriver *rtcp,
rtcalarm_t alarm,
const RTCAlarm *alarmspec) {
chDbgCheck((rtcp != NULL) && (alarm < RTC_ALARMS), "rtcSetAlarm");
chSysLock();
rtcSetAlarmI(rtcp, alarm, alarmspec);
chSysUnlock();
}
/**
* @brief Get current alarm.
* @note If an alarm has not been set then the returned alarm specification
* is not meaningful.
*
* @param[in] rtcp pointer to RTC driver structure
* @param[in] alarm alarm identifier
* @param[out] alarmspec pointer to a @p RTCAlarm structure
*
* @api
*/
void rtcGetAlarm(RTCDriver *rtcp,
rtcalarm_t alarm,
RTCAlarm *alarmspec) {
chDbgCheck((rtcp != NULL) && (alarm < RTC_ALARMS) && (alarmspec != NULL),
"rtcGetAlarm");
chSysLock();
rtcGetAlarmI(rtcp, alarm, alarmspec);
chSysUnlock();
}
#endif /* RTC_ALARMS > 0 */
#if RTC_SUPPORTS_CALLBACKS || defined(__DOXYGEN__)
/**
* @brief Enables or disables RTC callbacks.
* @details This function enables or disables the callback, use a @p NULL
* pointer in order to disable it.
*
* @param[in] rtcp pointer to RTC driver structure
* @param[in] callback callback function pointer or @p NULL
*
* @api
*/
void rtcSetCallback(RTCDriver *rtcp, rtccb_t callback) {
chDbgCheck((rtcp != NULL), "rtcSetCallback");
chSysLock();
rtcSetCallbackI(rtcp, callback);
chSysUnlock();
}
#endif /* RTC_SUPPORTS_CALLBACKS */
/**
* @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 rtcGetTimeFat(RTCDriver *rtcp) {
chDbgCheck((rtcp != NULL), "rtcSetTime");
return rtc_lld_get_time_fat(rtcp);
}
#endif /* HAL_USE_RTC */
/** @} */

View File

@ -0,0 +1,587 @@
/*
ChibiOS/RT - Copyright (C) 2006,2007,2008,2009,2010,
2011,2012,2013 Giovanni Di Sirio.
This file is part of ChibiOS/RT.
ChibiOS/RT is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
ChibiOS/RT is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
---
A special exception to the GPL can be applied should you wish to distribute
a combined work that includes ChibiOS/RT, without being obliged to provide
the source code for any proprietary components. See the file exception.txt
for full details of how and when the exception can be applied.
*/
/**
* @file sdc.c
* @brief SDC Driver code.
*
* @addtogroup SDC
* @{
*/
#include "ch.h"
#include "hal.h"
#if HAL_USE_SDC || defined(__DOXYGEN__)
/*===========================================================================*/
/* Driver local definitions. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver exported variables. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver local variables and types. */
/*===========================================================================*/
/**
* @brief Virtual methods table.
*/
static const struct SDCDriverVMT sdc_vmt = {
(bool_t (*)(void *))sdc_lld_is_card_inserted,
(bool_t (*)(void *))sdc_lld_is_write_protected,
(bool_t (*)(void *))sdcConnect,
(bool_t (*)(void *))sdcDisconnect,
(bool_t (*)(void *, uint32_t, uint8_t *, uint32_t))sdcRead,
(bool_t (*)(void *, uint32_t, const uint8_t *, uint32_t))sdcWrite,
(bool_t (*)(void *))sdcSync,
(bool_t (*)(void *, BlockDeviceInfo *))sdcGetInfo
};
/*===========================================================================*/
/* Driver local functions. */
/*===========================================================================*/
/**
* @brief Wait for the card to complete pending operations.
*
* @param[in] sdcp pointer to the @p SDCDriver object
*
* @return The operation status.
* @retval CH_SUCCESS operation succeeded.
* @retval CH_FAILED operation failed.
*
* @notapi
*/
bool_t _sdc_wait_for_transfer_state(SDCDriver *sdcp) {
uint32_t resp[1];
while (TRUE) {
if (sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_SEND_STATUS,
sdcp->rca, resp) ||
MMCSD_R1_ERROR(resp[0]))
return CH_FAILED;
switch (MMCSD_R1_STS(resp[0])) {
case MMCSD_STS_TRAN:
return CH_SUCCESS;
case MMCSD_STS_DATA:
case MMCSD_STS_RCV:
case MMCSD_STS_PRG:
#if SDC_NICE_WAITING
chThdSleepMilliseconds(1);
#endif
continue;
default:
/* The card should have been initialized so any other state is not
valid and is reported as an error.*/
return CH_FAILED;
}
}
/* If something going too wrong.*/
return CH_FAILED;
}
/*===========================================================================*/
/* Driver exported functions. */
/*===========================================================================*/
/**
* @brief SDC Driver initialization.
* @note This function is implicitly invoked by @p halInit(), there is
* no need to explicitly initialize the driver.
*
* @init
*/
void sdcInit(void) {
sdc_lld_init();
}
/**
* @brief Initializes the standard part of a @p SDCDriver structure.
*
* @param[out] sdcp pointer to the @p SDCDriver object
*
* @init
*/
void sdcObjectInit(SDCDriver *sdcp) {
sdcp->vmt = &sdc_vmt;
sdcp->state = BLK_STOP;
sdcp->errors = SDC_NO_ERROR;
sdcp->config = NULL;
sdcp->capacity = 0;
}
/**
* @brief Configures and activates the SDC peripheral.
*
* @param[in] sdcp pointer to the @p SDCDriver object
* @param[in] config pointer to the @p SDCConfig object, can be @p NULL if
* the driver supports a default configuration or
* requires no configuration
*
* @api
*/
void sdcStart(SDCDriver *sdcp, const SDCConfig *config) {
chDbgCheck(sdcp != NULL, "sdcStart");
chSysLock();
chDbgAssert((sdcp->state == BLK_STOP) || (sdcp->state == BLK_ACTIVE),
"sdcStart(), #1", "invalid state");
sdcp->config = config;
sdc_lld_start(sdcp);
sdcp->state = BLK_ACTIVE;
chSysUnlock();
}
/**
* @brief Deactivates the SDC peripheral.
*
* @param[in] sdcp pointer to the @p SDCDriver object
*
* @api
*/
void sdcStop(SDCDriver *sdcp) {
chDbgCheck(sdcp != NULL, "sdcStop");
chSysLock();
chDbgAssert((sdcp->state == BLK_STOP) || (sdcp->state == BLK_ACTIVE),
"sdcStop(), #1", "invalid state");
sdc_lld_stop(sdcp);
sdcp->state = BLK_STOP;
chSysUnlock();
}
/**
* @brief Performs the initialization procedure on the inserted card.
* @details This function should be invoked when a card is inserted and
* brings the driver in the @p BLK_READY state where it is possible
* to perform read and write operations.
*
* @param[in] sdcp pointer to the @p SDCDriver object
*
* @return The operation status.
* @retval CH_SUCCESS operation succeeded.
* @retval CH_FAILED operation failed.
*
* @api
*/
bool_t sdcConnect(SDCDriver *sdcp) {
uint32_t resp[1];
chDbgCheck(sdcp != NULL, "sdcConnect");
chDbgAssert((sdcp->state == BLK_ACTIVE) || (sdcp->state == BLK_READY),
"mmcConnect(), #1", "invalid state");
/* Connection procedure in progress.*/
sdcp->state = BLK_CONNECTING;
/* Card clock initialization.*/
sdc_lld_start_clk(sdcp);
/* Enforces the initial card state.*/
sdc_lld_send_cmd_none(sdcp, MMCSD_CMD_GO_IDLE_STATE, 0);
/* V2.0 cards detection.*/
if (!sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_SEND_IF_COND,
MMCSD_CMD8_PATTERN, resp)) {
sdcp->cardmode = SDC_MODE_CARDTYPE_SDV20;
/* Voltage verification.*/
if (((resp[0] >> 8) & 0xF) != 1)
goto failed;
if (sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_APP_CMD, 0, resp) ||
MMCSD_R1_ERROR(resp[0]))
goto failed;
}
else {
#if SDC_MMC_SUPPORT
/* MMC or SD V1.1 detection.*/
if (sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_APP_CMD, 0, resp) ||
MMCSD_R1_ERROR(resp[0]))
sdcp->cardmode = SDC_MODE_CARDTYPE_MMC;
else
#endif /* SDC_MMC_SUPPORT */
{
sdcp->cardmode = SDC_MODE_CARDTYPE_SDV11;
/* Reset error flag illegal command.*/
sdc_lld_send_cmd_none(sdcp, MMCSD_CMD_GO_IDLE_STATE, 0);
}
}
#if SDC_MMC_SUPPORT
if ((sdcp->cardmode & SDC_MODE_CARDTYPE_MASK) == SDC_MODE_CARDTYPE_MMC) {
/* TODO: MMC initialization.*/
goto failed;
}
else
#endif /* SDC_MMC_SUPPORT */
{
unsigned i;
uint32_t ocr;
/* SD initialization.*/
if ((sdcp->cardmode & SDC_MODE_CARDTYPE_MASK) == SDC_MODE_CARDTYPE_SDV20)
ocr = 0xC0100000;
else
ocr = 0x80100000;
/* SD-type initialization. */
i = 0;
while (TRUE) {
if (sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_APP_CMD, 0, resp) ||
MMCSD_R1_ERROR(resp[0]))
goto failed;
if (sdc_lld_send_cmd_short(sdcp, MMCSD_CMD_APP_OP_COND, ocr, resp))
goto failed;
if ((resp[0] & 0x80000000) != 0) {
if (resp[0] & 0x40000000)
sdcp->cardmode |= SDC_MODE_HIGH_CAPACITY;
break;
}
if (++i >= SDC_INIT_RETRY)
goto failed;
chThdSleepMilliseconds(10);
}
}
/* Reads CID.*/
if (sdc_lld_send_cmd_long_crc(sdcp, MMCSD_CMD_ALL_SEND_CID, 0, sdcp->cid))
goto failed;
/* Asks for the RCA.*/
if (sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_SEND_RELATIVE_ADDR,
0, &sdcp->rca))
goto failed;
/* Reads CSD.*/
if (sdc_lld_send_cmd_long_crc(sdcp, MMCSD_CMD_SEND_CSD,
sdcp->rca, sdcp->csd))
goto failed;
/* Switches to high speed.*/
sdc_lld_set_data_clk(sdcp);
/* Selects the card for operations.*/
if (sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_SEL_DESEL_CARD,
sdcp->rca, resp))
goto failed;
/* Block length fixed at 512 bytes.*/
if (sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_SET_BLOCKLEN,
MMCSD_BLOCK_SIZE, resp) ||
MMCSD_R1_ERROR(resp[0]))
goto failed;
/* Switches to wide bus mode.*/
switch (sdcp->cardmode & SDC_MODE_CARDTYPE_MASK) {
case SDC_MODE_CARDTYPE_SDV11:
case SDC_MODE_CARDTYPE_SDV20:
sdc_lld_set_bus_mode(sdcp, SDC_MODE_4BIT);
if (sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_APP_CMD, sdcp->rca, resp) ||
MMCSD_R1_ERROR(resp[0]))
goto failed;
if (sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_SET_BUS_WIDTH, 2, resp) ||
MMCSD_R1_ERROR(resp[0]))
goto failed;
break;
}
/* Determine capacity.*/
sdcp->capacity = mmcsdGetCapacity(sdcp->csd);
if (sdcp->capacity == 0)
goto failed;
/* Initialization complete.*/
sdcp->state = BLK_READY;
return CH_SUCCESS;
/* Connection failed, state reset to BLK_ACTIVE.*/
failed:
sdc_lld_stop_clk(sdcp);
sdcp->state = BLK_ACTIVE;
return CH_FAILED;
}
/**
* @brief Brings the driver in a state safe for card removal.
*
* @param[in] sdcp pointer to the @p SDCDriver object
*
* @return The operation status.
* @retval CH_SUCCESS operation succeeded.
* @retval CH_FAILED operation failed.
*
* @api
*/
bool_t sdcDisconnect(SDCDriver *sdcp) {
chDbgCheck(sdcp != NULL, "sdcDisconnect");
chSysLock();
chDbgAssert((sdcp->state == BLK_ACTIVE) || (sdcp->state == BLK_READY),
"sdcDisconnect(), #1", "invalid state");
if (sdcp->state == BLK_ACTIVE) {
chSysUnlock();
return CH_SUCCESS;
}
sdcp->state = BLK_DISCONNECTING;
chSysUnlock();
/* Waits for eventual pending operations completion.*/
if (_sdc_wait_for_transfer_state(sdcp)) {
sdc_lld_stop_clk(sdcp);
sdcp->state = BLK_ACTIVE;
return CH_FAILED;
}
/* Card clock stopped.*/
sdc_lld_stop_clk(sdcp);
sdcp->state = BLK_ACTIVE;
return CH_SUCCESS;
}
/**
* @brief Reads one or more blocks.
* @pre The driver must be in the @p BLK_READY state after a successful
* sdcConnect() invocation.
*
* @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.
*
* @api
*/
bool_t sdcRead(SDCDriver *sdcp, uint32_t startblk,
uint8_t *buf, uint32_t n) {
bool_t status;
chDbgCheck((sdcp != NULL) && (buf != NULL) && (n > 0), "sdcRead");
chDbgAssert(sdcp->state == BLK_READY, "sdcRead(), #1", "invalid state");
if ((startblk + n - 1) > sdcp->capacity){
sdcp->errors |= SDC_OVERFLOW_ERROR;
return CH_FAILED;
}
/* Read operation in progress.*/
sdcp->state = BLK_READING;
status = sdc_lld_read(sdcp, startblk, buf, n);
/* Read operation finished.*/
sdcp->state = BLK_READY;
return status;
}
/**
* @brief Writes one or more blocks.
* @pre The driver must be in the @p BLK_READY state after a successful
* sdcConnect() invocation.
*
* @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.
*
* @api
*/
bool_t sdcWrite(SDCDriver *sdcp, uint32_t startblk,
const uint8_t *buf, uint32_t n) {
bool_t status;
chDbgCheck((sdcp != NULL) && (buf != NULL) && (n > 0), "sdcWrite");
chDbgAssert(sdcp->state == BLK_READY, "sdcWrite(), #1", "invalid state");
if ((startblk + n - 1) > sdcp->capacity){
sdcp->errors |= SDC_OVERFLOW_ERROR;
return CH_FAILED;
}
/* Write operation in progress.*/
sdcp->state = BLK_WRITING;
status = sdc_lld_write(sdcp, startblk, buf, n);
/* Write operation finished.*/
sdcp->state = BLK_READY;
return status;
}
/**
* @brief Returns the errors mask associated to the previous operation.
*
* @param[in] sdcp pointer to the @p SDCDriver object
* @return The errors mask.
*
* @api
*/
sdcflags_t sdcGetAndClearErrors(SDCDriver *sdcp) {
sdcflags_t flags;
chDbgCheck(sdcp != NULL, "sdcGetAndClearErrors");
chDbgAssert(sdcp->state == BLK_READY,
"sdcGetAndClearErrors(), #1", "invalid state");
chSysLock();
flags = sdcp->errors;
sdcp->errors = SDC_NO_ERROR;
chSysUnlock();
return flags;
}
/**
* @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 sdcSync(SDCDriver *sdcp) {
bool_t result;
chDbgCheck(sdcp != NULL, "sdcSync");
if (sdcp->state != BLK_READY)
return CH_FAILED;
/* Synchronization operation in progress.*/
sdcp->state = BLK_SYNCING;
result = sdc_lld_sync(sdcp);
/* Synchronization operation finished.*/
sdcp->state = BLK_READY;
return result;
}
/**
* @brief Returns the media info.
*
* @param[in] sdcp pointer to the @p SDCDriver object
* @param[out] bdip pointer to a @p BlockDeviceInfo structure
*
* @return The operation status.
* @retval CH_SUCCESS the operation succeeded.
* @retval CH_FAILED the operation failed.
*
* @api
*/
bool_t sdcGetInfo(SDCDriver *sdcp, BlockDeviceInfo *bdip) {
chDbgCheck((sdcp != NULL) && (bdip != NULL), "sdcGetInfo");
if (sdcp->state != BLK_READY)
return CH_FAILED;
bdip->blk_num = sdcp->capacity;
bdip->blk_size = MMCSD_BLOCK_SIZE;
return CH_SUCCESS;
}
/**
* @brief Erases the supplied blocks.
*
* @param[in] sdcp pointer to the @p SDCDriver object
* @param[in] startblk starting block number
* @param[in] endblk ending block number
*
* @return The operation status.
* @retval CH_SUCCESS the operation succeeded.
* @retval CH_FAILED the operation failed.
*
* @api
*/
bool_t sdcErase(SDCDriver *sdcp, uint32_t startblk, uint32_t endblk) {
uint32_t resp[1];
chDbgCheck((sdcp != NULL), "sdcErase");
chDbgAssert(sdcp->state == BLK_READY, "sdcErase(), #1", "invalid state");
/* Erase operation in progress.*/
sdcp->state = BLK_WRITING;
/* Handling command differences between HC and normal cards.*/
if (!(sdcp->cardmode & SDC_MODE_HIGH_CAPACITY)) {
startblk *= MMCSD_BLOCK_SIZE;
endblk *= MMCSD_BLOCK_SIZE;
}
_sdc_wait_for_transfer_state(sdcp);
if ((sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_ERASE_RW_BLK_START,
startblk, resp) != CH_SUCCESS) ||
MMCSD_R1_ERROR(resp[0]))
goto failed;
if ((sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_ERASE_RW_BLK_END,
endblk, resp) != CH_SUCCESS) ||
MMCSD_R1_ERROR(resp[0]))
goto failed;
if ((sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_ERASE,
0, resp) != CH_SUCCESS) ||
MMCSD_R1_ERROR(resp[0]))
goto failed;
/* Quick sleep to allow it to transition to programming or receiving state */
/* TODO: ??????????????????????????? */
/* Wait for it to return to transfer state to indicate it has finished erasing */
_sdc_wait_for_transfer_state(sdcp);
sdcp->state = BLK_READY;
return CH_SUCCESS;
failed:
sdcp->state = BLK_READY;
return CH_FAILED;
}
#endif /* HAL_USE_SDC */
/** @} */

View File

@ -0,0 +1,253 @@
/*
ChibiOS/RT - Copyright (C) 2006,2007,2008,2009,2010,
2011,2012,2013 Giovanni Di Sirio.
This file is part of ChibiOS/RT.
ChibiOS/RT is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
ChibiOS/RT is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
---
A special exception to the GPL can be applied should you wish to distribute
a combined work that includes ChibiOS/RT, without being obliged to provide
the source code for any proprietary components. See the file exception.txt
for full details of how and when the exception can be applied.
*/
/**
* @file serial.c
* @brief Serial Driver code.
*
* @addtogroup SERIAL
* @{
*/
#include "ch.h"
#include "hal.h"
#if HAL_USE_SERIAL || defined(__DOXYGEN__)
/*===========================================================================*/
/* Driver local definitions. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver exported variables. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver local variables and types. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver local functions. */
/*===========================================================================*/
/*
* Interface implementation, the following functions just invoke the equivalent
* queue-level function or macro.
*/
static size_t write(void *ip, const uint8_t *bp, size_t n) {
return chOQWriteTimeout(&((SerialDriver *)ip)->oqueue, bp,
n, TIME_INFINITE);
}
static size_t read(void *ip, uint8_t *bp, size_t n) {
return chIQReadTimeout(&((SerialDriver *)ip)->iqueue, bp,
n, TIME_INFINITE);
}
static msg_t put(void *ip, uint8_t b) {
return chOQPutTimeout(&((SerialDriver *)ip)->oqueue, b, TIME_INFINITE);
}
static msg_t get(void *ip) {
return chIQGetTimeout(&((SerialDriver *)ip)->iqueue, TIME_INFINITE);
}
static msg_t putt(void *ip, uint8_t b, systime_t timeout) {
return chOQPutTimeout(&((SerialDriver *)ip)->oqueue, b, timeout);
}
static msg_t gett(void *ip, systime_t timeout) {
return chIQGetTimeout(&((SerialDriver *)ip)->iqueue, timeout);
}
static size_t writet(void *ip, const uint8_t *bp, size_t n, systime_t time) {
return chOQWriteTimeout(&((SerialDriver *)ip)->oqueue, bp, n, time);
}
static size_t readt(void *ip, uint8_t *bp, size_t n, systime_t time) {
return chIQReadTimeout(&((SerialDriver *)ip)->iqueue, bp, n, time);
}
static const struct SerialDriverVMT vmt = {
write, read, put, get,
putt, gett, writet, readt
};
/*===========================================================================*/
/* Driver exported functions. */
/*===========================================================================*/
/**
* @brief Serial Driver initialization.
* @note This function is implicitly invoked by @p halInit(), there is
* no need to explicitly initialize the driver.
*
* @init
*/
void sdInit(void) {
sd_lld_init();
}
/**
* @brief Initializes a generic full duplex driver object.
* @details The HW dependent part of the initialization has to be performed
* outside, usually in the hardware initialization code.
*
* @param[out] sdp pointer to a @p SerialDriver structure
* @param[in] inotify pointer to a callback function that is invoked when
* some data is read from the Queue. The value can be
* @p NULL.
* @param[in] onotify pointer to a callback function that is invoked when
* some data is written in the Queue. The value can be
* @p NULL.
*
* @init
*/
void sdObjectInit(SerialDriver *sdp, qnotify_t inotify, qnotify_t onotify) {
sdp->vmt = &vmt;
chEvtInit(&sdp->event);
sdp->state = SD_STOP;
chIQInit(&sdp->iqueue, sdp->ib, SERIAL_BUFFERS_SIZE, inotify, sdp);
chOQInit(&sdp->oqueue, sdp->ob, SERIAL_BUFFERS_SIZE, onotify, sdp);
}
/**
* @brief Configures and starts the driver.
*
* @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.
*
* @api
*/
void sdStart(SerialDriver *sdp, const SerialConfig *config) {
chDbgCheck(sdp != NULL, "sdStart");
chSysLock();
chDbgAssert((sdp->state == SD_STOP) || (sdp->state == SD_READY),
"sdStart(), #1",
"invalid state");
sd_lld_start(sdp, config);
sdp->state = SD_READY;
chSysUnlock();
}
/**
* @brief Stops the driver.
* @details Any thread waiting on the driver's queues will be awakened with
* the message @p Q_RESET.
*
* @param[in] sdp pointer to a @p SerialDriver object
*
* @api
*/
void sdStop(SerialDriver *sdp) {
chDbgCheck(sdp != NULL, "sdStop");
chSysLock();
chDbgAssert((sdp->state == SD_STOP) || (sdp->state == SD_READY),
"sdStop(), #1",
"invalid state");
sd_lld_stop(sdp);
sdp->state = SD_STOP;
chOQResetI(&sdp->oqueue);
chIQResetI(&sdp->iqueue);
chSchRescheduleS();
chSysUnlock();
}
/**
* @brief Handles incoming data.
* @details This function must be called from the input interrupt service
* routine in order to enqueue incoming data and generate the
* related events.
* @note The incoming data event is only generated when the input queue
* becomes non-empty.
* @note In order to gain some performance it is suggested to not use
* this function directly but copy this code directly into the
* interrupt service routine.
*
* @param[in] sdp pointer to a @p SerialDriver structure
* @param[in] b the byte to be written in the driver's Input Queue
*
* @iclass
*/
void sdIncomingDataI(SerialDriver *sdp, uint8_t b) {
chDbgCheckClassI();
chDbgCheck(sdp != NULL, "sdIncomingDataI");
if (chIQIsEmptyI(&sdp->iqueue))
chnAddFlagsI(sdp, CHN_INPUT_AVAILABLE);
if (chIQPutI(&sdp->iqueue, b) < Q_OK)
chnAddFlagsI(sdp, SD_OVERRUN_ERROR);
}
/**
* @brief Handles outgoing data.
* @details Must be called from the output interrupt service routine in order
* to get the next byte to be transmitted.
* @note In order to gain some performance it is suggested to not use
* this function directly but copy this code directly into the
* interrupt service routine.
*
* @param[in] sdp pointer to a @p SerialDriver structure
* @return The byte value read from the driver's output queue.
* @retval Q_EMPTY if the queue is empty (the lower driver usually
* disables the interrupt source when this happens).
*
* @iclass
*/
msg_t sdRequestDataI(SerialDriver *sdp) {
msg_t b;
chDbgCheckClassI();
chDbgCheck(sdp != NULL, "sdRequestDataI");
b = chOQGetI(&sdp->oqueue);
if (b < Q_OK)
chnAddFlagsI(sdp, CHN_OUTPUT_EMPTY);
return b;
}
#endif /* HAL_USE_SERIAL */
/** @} */

View File

@ -0,0 +1,421 @@
/*
ChibiOS/RT - Copyright (C) 2006,2007,2008,2009,2010,
2011,2012,2013 Giovanni Di Sirio.
This file is part of ChibiOS/RT.
ChibiOS/RT is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
ChibiOS/RT is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
---
A special exception to the GPL can be applied should you wish to distribute
a combined work that includes ChibiOS/RT, without being obliged to provide
the source code for any proprietary components. See the file exception.txt
for full details of how and when the exception can be applied.
*/
/**
* @file serial_usb.c
* @brief Serial over USB Driver code.
*
* @addtogroup SERIAL_USB
* @{
*/
#include "ch.h"
#include "hal.h"
#if HAL_USE_SERIAL_USB || defined(__DOXYGEN__)
/*===========================================================================*/
/* Driver local definitions. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver exported variables. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver local variables and types. */
/*===========================================================================*/
/*
* Current Line Coding.
*/
static cdc_linecoding_t linecoding = {
{0x00, 0x96, 0x00, 0x00}, /* 38400. */
LC_STOP_1, LC_PARITY_NONE, 8
};
/*===========================================================================*/
/* Driver local functions. */
/*===========================================================================*/
/*
* Interface implementation.
*/
static size_t write(void *ip, const uint8_t *bp, size_t n) {
return chOQWriteTimeout(&((SerialUSBDriver *)ip)->oqueue, bp,
n, TIME_INFINITE);
}
static size_t read(void *ip, uint8_t *bp, size_t n) {
return chIQReadTimeout(&((SerialUSBDriver *)ip)->iqueue, bp,
n, TIME_INFINITE);
}
static msg_t put(void *ip, uint8_t b) {
return chOQPutTimeout(&((SerialUSBDriver *)ip)->oqueue, b, TIME_INFINITE);
}
static msg_t get(void *ip) {
return chIQGetTimeout(&((SerialUSBDriver *)ip)->iqueue, TIME_INFINITE);
}
static msg_t putt(void *ip, uint8_t b, systime_t timeout) {
return chOQPutTimeout(&((SerialUSBDriver *)ip)->oqueue, b, timeout);
}
static msg_t gett(void *ip, systime_t timeout) {
return chIQGetTimeout(&((SerialUSBDriver *)ip)->iqueue, timeout);
}
static size_t writet(void *ip, const uint8_t *bp, size_t n, systime_t time) {
return chOQWriteTimeout(&((SerialUSBDriver *)ip)->oqueue, bp, n, time);
}
static size_t readt(void *ip, uint8_t *bp, size_t n, systime_t time) {
return chIQReadTimeout(&((SerialUSBDriver *)ip)->iqueue, bp, n, time);
}
static const struct SerialUSBDriverVMT vmt = {
write, read, put, get,
putt, gett, writet, readt
};
/**
* @brief Notification of data removed from the input queue.
*/
static void inotify(GenericQueue *qp) {
size_t n, maxsize;
SerialUSBDriver *sdup = chQGetLink(qp);
/* If the USB driver is not in the appropriate state then transactions
must not be started.*/
if ((usbGetDriverStateI(sdup->config->usbp) != USB_ACTIVE) ||
(sdup->state != SDU_READY))
return;
/* If there is in the queue enough space to hold at least one packet and
a transaction is not yet started then a new transaction is started for
the available space.*/
maxsize = sdup->config->usbp->epc[sdup->config->bulk_out]->out_maxsize;
if (!usbGetReceiveStatusI(sdup->config->usbp, sdup->config->bulk_out) &&
((n = chIQGetEmptyI(&sdup->iqueue)) >= maxsize)) {
chSysUnlock();
n = (n / maxsize) * maxsize;
usbPrepareQueuedReceive(sdup->config->usbp,
sdup->config->bulk_out,
&sdup->iqueue, n);
chSysLock();
usbStartReceiveI(sdup->config->usbp, sdup->config->bulk_out);
}
}
/**
* @brief Notification of data inserted into the output queue.
*/
static void onotify(GenericQueue *qp) {
size_t n;
SerialUSBDriver *sdup = chQGetLink(qp);
/* If the USB driver is not in the appropriate state then transactions
must not be started.*/
if ((usbGetDriverStateI(sdup->config->usbp) != USB_ACTIVE) ||
(sdup->state != SDU_READY))
return;
/* If there is not an ongoing transaction and the output queue contains
data then a new transaction is started.*/
if (!usbGetTransmitStatusI(sdup->config->usbp, sdup->config->bulk_in) &&
((n = chOQGetFullI(&sdup->oqueue)) > 0)) {
chSysUnlock();
usbPrepareQueuedTransmit(sdup->config->usbp,
sdup->config->bulk_in,
&sdup->oqueue, n);
chSysLock();
usbStartTransmitI(sdup->config->usbp, sdup->config->bulk_in);
}
}
/*===========================================================================*/
/* Driver exported functions. */
/*===========================================================================*/
/**
* @brief Serial Driver initialization.
* @note This function is implicitly invoked by @p halInit(), there is
* no need to explicitly initialize the driver.
*
* @init
*/
void sduInit(void) {
}
/**
* @brief Initializes a generic full duplex driver object.
* @details The HW dependent part of the initialization has to be performed
* outside, usually in the hardware initialization code.
*
* @param[out] sdup pointer to a @p SerialUSBDriver structure
*
* @init
*/
void sduObjectInit(SerialUSBDriver *sdup) {
sdup->vmt = &vmt;
chEvtInit(&sdup->event);
sdup->state = SDU_STOP;
chIQInit(&sdup->iqueue, sdup->ib, SERIAL_USB_BUFFERS_SIZE, inotify, sdup);
chOQInit(&sdup->oqueue, sdup->ob, SERIAL_USB_BUFFERS_SIZE, onotify, sdup);
}
/**
* @brief Configures and starts the driver.
*
* @param[in] sdup pointer to a @p SerialUSBDriver object
* @param[in] config the serial over USB driver configuration
*
* @api
*/
void sduStart(SerialUSBDriver *sdup, const SerialUSBConfig *config) {
USBDriver *usbp = config->usbp;
chDbgCheck(sdup != NULL, "sduStart");
chSysLock();
chDbgAssert((sdup->state == SDU_STOP) || (sdup->state == SDU_READY),
"sduStart(), #1",
"invalid state");
usbp->in_params[config->bulk_in - 1] = sdup;
usbp->out_params[config->bulk_out - 1] = sdup;
usbp->in_params[config->int_in - 1] = sdup;
sdup->config = config;
sdup->state = SDU_READY;
chSysUnlock();
}
/**
* @brief Stops the driver.
* @details Any thread waiting on the driver's queues will be awakened with
* the message @p Q_RESET.
*
* @param[in] sdup pointer to a @p SerialUSBDriver object
*
* @api
*/
void sduStop(SerialUSBDriver *sdup) {
USBDriver *usbp = sdup->config->usbp;
chDbgCheck(sdup != NULL, "sdStop");
chSysLock();
chDbgAssert((sdup->state == SDU_STOP) || (sdup->state == SDU_READY),
"sduStop(), #1",
"invalid state");
/* Driver in stopped state.*/
usbp->in_params[sdup->config->bulk_in - 1] = NULL;
usbp->out_params[sdup->config->bulk_out - 1] = NULL;
usbp->in_params[sdup->config->int_in - 1] = NULL;
sdup->state = SDU_STOP;
/* Queues reset in order to signal the driver stop to the application.*/
chnAddFlagsI(sdup, CHN_DISCONNECTED);
chIQResetI(&sdup->iqueue);
chOQResetI(&sdup->oqueue);
chSchRescheduleS();
chSysUnlock();
}
/**
* @brief USB device configured handler.
*
* @param[in] sdup pointer to a @p SerialUSBDriver object
*
* @iclass
*/
void sduConfigureHookI(SerialUSBDriver *sdup) {
USBDriver *usbp = sdup->config->usbp;
chIQResetI(&sdup->iqueue);
chOQResetI(&sdup->oqueue);
chnAddFlagsI(sdup, CHN_CONNECTED);
/* Starts the first OUT transaction immediately.*/
usbPrepareQueuedReceive(usbp, sdup->config->bulk_out, &sdup->iqueue,
usbp->epc[sdup->config->bulk_out]->out_maxsize);
usbStartReceiveI(usbp, sdup->config->bulk_out);
}
/**
* @brief Default requests hook.
* @details Applications wanting to use the Serial over USB driver can use
* this function as requests hook in the USB configuration.
* The following requests are emulated:
* - CDC_GET_LINE_CODING.
* - CDC_SET_LINE_CODING.
* - CDC_SET_CONTROL_LINE_STATE.
* .
*
* @param[in] usbp pointer to the @p USBDriver object
* @return The hook status.
* @retval TRUE Message handled internally.
* @retval FALSE Message not handled.
*/
bool_t sduRequestsHook(USBDriver *usbp) {
if ((usbp->setup[0] & USB_RTYPE_TYPE_MASK) == USB_RTYPE_TYPE_CLASS) {
switch (usbp->setup[1]) {
case CDC_GET_LINE_CODING:
usbSetupTransfer(usbp, (uint8_t *)&linecoding, sizeof(linecoding), NULL);
return TRUE;
case CDC_SET_LINE_CODING:
usbSetupTransfer(usbp, (uint8_t *)&linecoding, sizeof(linecoding), NULL);
return TRUE;
case CDC_SET_CONTROL_LINE_STATE:
/* Nothing to do, there are no control lines.*/
usbSetupTransfer(usbp, NULL, 0, NULL);
return TRUE;
default:
return FALSE;
}
}
return FALSE;
}
/**
* @brief Default data transmitted callback.
* @details The application must use this function as callback for the IN
* data endpoint.
*
* @param[in] usbp pointer to the @p USBDriver object
* @param[in] ep endpoint number
*/
void sduDataTransmitted(USBDriver *usbp, usbep_t ep) {
size_t n;
SerialUSBDriver *sdup = usbp->in_params[ep - 1];
if (sdup == NULL)
return;
chSysLockFromIsr();
chnAddFlagsI(sdup, CHN_OUTPUT_EMPTY);
if ((n = chOQGetFullI(&sdup->oqueue)) > 0) {
/* The endpoint cannot be busy, we are in the context of the callback,
so it is safe to transmit without a check.*/
chSysUnlockFromIsr();
usbPrepareQueuedTransmit(usbp, ep, &sdup->oqueue, n);
chSysLockFromIsr();
usbStartTransmitI(usbp, ep);
}
else if ((usbp->epc[ep]->in_state->txsize > 0) &&
!(usbp->epc[ep]->in_state->txsize &
(usbp->epc[ep]->in_maxsize - 1))) {
/* Transmit zero sized packet in case the last one has maximum allowed
size. Otherwise the recipient may expect more data coming soon and
not return buffered data to app. See section 5.8.3 Bulk Transfer
Packet Size Constraints of the USB Specification document.*/
chSysUnlockFromIsr();
usbPrepareQueuedTransmit(usbp, ep, &sdup->oqueue, 0);
chSysLockFromIsr();
usbStartTransmitI(usbp, ep);
}
chSysUnlockFromIsr();
}
/**
* @brief Default data received callback.
* @details The application must use this function as callback for the OUT
* data endpoint.
*
* @param[in] usbp pointer to the @p USBDriver object
* @param[in] ep endpoint number
*/
void sduDataReceived(USBDriver *usbp, usbep_t ep) {
size_t n, maxsize;
SerialUSBDriver *sdup = usbp->out_params[ep - 1];
if (sdup == NULL)
return;
chSysLockFromIsr();
chnAddFlagsI(sdup, CHN_INPUT_AVAILABLE);
/* Writes to the input queue can only happen when there is enough space
to hold at least one packet.*/
maxsize = usbp->epc[ep]->out_maxsize;
if ((n = chIQGetEmptyI(&sdup->iqueue)) >= maxsize) {
/* The endpoint cannot be busy, we are in the context of the callback,
so a packet is in the buffer for sure.*/
chSysUnlockFromIsr();
n = (n / maxsize) * maxsize;
usbPrepareQueuedReceive(usbp, ep, &sdup->iqueue, n);
chSysLockFromIsr();
usbStartReceiveI(usbp, ep);
}
chSysUnlockFromIsr();
}
/**
* @brief Default data received callback.
* @details The application must use this function as callback for the IN
* interrupt endpoint.
*
* @param[in] usbp pointer to the @p USBDriver object
* @param[in] ep endpoint number
*/
void sduInterruptTransmitted(USBDriver *usbp, usbep_t ep) {
(void)usbp;
(void)ep;
}
#endif /* HAL_USE_SERIAL */
/** @} */

View File

@ -0,0 +1,447 @@
/*
ChibiOS/RT - Copyright (C) 2006,2007,2008,2009,2010,
2011,2012,2013 Giovanni Di Sirio.
This file is part of ChibiOS/RT.
ChibiOS/RT is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
ChibiOS/RT is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
---
A special exception to the GPL can be applied should you wish to distribute
a combined work that includes ChibiOS/RT, without being obliged to provide
the source code for any proprietary components. See the file exception.txt
for full details of how and when the exception can be applied.
*/
/**
* @file spi.c
* @brief SPI Driver code.
*
* @addtogroup SPI
* @{
*/
#include "ch.h"
#include "hal.h"
#if HAL_USE_SPI || defined(__DOXYGEN__)
/*===========================================================================*/
/* Driver local definitions. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver exported variables. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver local variables and types. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver local functions. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver exported functions. */
/*===========================================================================*/
/**
* @brief SPI Driver initialization.
* @note This function is implicitly invoked by @p halInit(), there is
* no need to explicitly initialize the driver.
*
* @init
*/
void spiInit(void) {
spi_lld_init();
}
/**
* @brief Initializes the standard part of a @p SPIDriver structure.
*
* @param[out] spip pointer to the @p SPIDriver object
*
* @init
*/
void spiObjectInit(SPIDriver *spip) {
spip->state = SPI_STOP;
spip->config = NULL;
#if SPI_USE_WAIT
spip->thread = NULL;
#endif /* SPI_USE_WAIT */
#if SPI_USE_MUTUAL_EXCLUSION
#if CH_USE_MUTEXES
chMtxInit(&spip->mutex);
#else
chSemInit(&spip->semaphore, 1);
#endif
#endif /* SPI_USE_MUTUAL_EXCLUSION */
#if defined(SPI_DRIVER_EXT_INIT_HOOK)
SPI_DRIVER_EXT_INIT_HOOK(spip);
#endif
}
/**
* @brief Configures and activates the SPI peripheral.
*
* @param[in] spip pointer to the @p SPIDriver object
* @param[in] config pointer to the @p SPIConfig object
*
* @api
*/
void spiStart(SPIDriver *spip, const SPIConfig *config) {
chDbgCheck((spip != NULL) && (config != NULL), "spiStart");
chSysLock();
chDbgAssert((spip->state == SPI_STOP) || (spip->state == SPI_READY),
"spiStart(), #1", "invalid state");
spip->config = config;
spi_lld_start(spip);
spip->state = SPI_READY;
chSysUnlock();
}
/**
* @brief Deactivates the SPI peripheral.
* @note Deactivating the peripheral also enforces a release of the slave
* select line.
*
* @param[in] spip pointer to the @p SPIDriver object
*
* @api
*/
void spiStop(SPIDriver *spip) {
chDbgCheck(spip != NULL, "spiStop");
chSysLock();
chDbgAssert((spip->state == SPI_STOP) || (spip->state == SPI_READY),
"spiStop(), #1", "invalid state");
spi_lld_unselect(spip);
spi_lld_stop(spip);
spip->state = SPI_STOP;
chSysUnlock();
}
/**
* @brief Asserts the slave select signal and prepares for transfers.
*
* @param[in] spip pointer to the @p SPIDriver object
*
* @api
*/
void spiSelect(SPIDriver *spip) {
chDbgCheck(spip != NULL, "spiSelect");
chSysLock();
chDbgAssert(spip->state == SPI_READY, "spiSelect(), #1", "not ready");
spiSelectI(spip);
chSysUnlock();
}
/**
* @brief Deasserts the slave select signal.
* @details The previously selected peripheral is unselected.
*
* @param[in] spip pointer to the @p SPIDriver object
*
* @api
*/
void spiUnselect(SPIDriver *spip) {
chDbgCheck(spip != NULL, "spiUnselect");
chSysLock();
chDbgAssert(spip->state == SPI_READY, "spiUnselect(), #1", "not ready");
spiUnselectI(spip);
chSysUnlock();
}
/**
* @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.
* @pre A slave must have been selected using @p spiSelect() or
* @p spiSelectI().
* @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
*
* @api
*/
void spiStartIgnore(SPIDriver *spip, size_t n) {
chDbgCheck((spip != NULL) && (n > 0), "spiStartIgnore");
chSysLock();
chDbgAssert(spip->state == SPI_READY, "spiStartIgnore(), #1", "not ready");
spiStartIgnoreI(spip, n);
chSysUnlock();
}
/**
* @brief Exchanges data on the SPI bus.
* @details This asynchronous function starts a simultaneous transmit/receive
* operation.
* @pre A slave must have been selected using @p spiSelect() or
* @p spiSelectI().
* @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
*
* @api
*/
void spiStartExchange(SPIDriver *spip, size_t n,
const void *txbuf, void *rxbuf) {
chDbgCheck((spip != NULL) && (n > 0) && (rxbuf != NULL) && (txbuf != NULL),
"spiStartExchange");
chSysLock();
chDbgAssert(spip->state == SPI_READY, "spiStartExchange(), #1", "not ready");
spiStartExchangeI(spip, n, txbuf, rxbuf);
chSysUnlock();
}
/**
* @brief Sends data over the SPI bus.
* @details This asynchronous function starts a transmit operation.
* @pre A slave must have been selected using @p spiSelect() or
* @p spiSelectI().
* @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
*
* @api
*/
void spiStartSend(SPIDriver *spip, size_t n, const void *txbuf) {
chDbgCheck((spip != NULL) && (n > 0) && (txbuf != NULL),
"spiStartSend");
chSysLock();
chDbgAssert(spip->state == SPI_READY, "spiStartSend(), #1", "not ready");
spiStartSendI(spip, n, txbuf);
chSysUnlock();
}
/**
* @brief Receives data from the SPI bus.
* @details This asynchronous function starts a receive operation.
* @pre A slave must have been selected using @p spiSelect() or
* @p spiSelectI().
* @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
*
* @api
*/
void spiStartReceive(SPIDriver *spip, size_t n, void *rxbuf) {
chDbgCheck((spip != NULL) && (n > 0) && (rxbuf != NULL),
"spiStartReceive");
chSysLock();
chDbgAssert(spip->state == SPI_READY, "spiStartReceive(), #1", "not ready");
spiStartReceiveI(spip, n, rxbuf);
chSysUnlock();
}
#if SPI_USE_WAIT || defined(__DOXYGEN__)
/**
* @brief Ignores data on the SPI bus.
* @details This synchronous function performs the transmission of a series of
* idle words on the SPI bus and ignores the received data.
* @pre In order to use this function the option @p SPI_USE_WAIT must be
* enabled.
* @pre In order to use this function the driver must have been configured
* without callbacks (@p end_cb = @p NULL).
*
* @param[in] spip pointer to the @p SPIDriver object
* @param[in] n number of words to be ignored
*
* @api
*/
void spiIgnore(SPIDriver *spip, size_t n) {
chDbgCheck((spip != NULL) && (n > 0), "spiIgnoreWait");
chSysLock();
chDbgAssert(spip->state == SPI_READY, "spiIgnore(), #1", "not ready");
chDbgAssert(spip->config->end_cb == NULL, "spiIgnore(), #2", "has callback");
spiStartIgnoreI(spip, n);
_spi_wait_s(spip);
chSysUnlock();
}
/**
* @brief Exchanges data on the SPI bus.
* @details This synchronous function performs a simultaneous transmit/receive
* operation.
* @pre In order to use this function the option @p SPI_USE_WAIT must be
* enabled.
* @pre In order to use this function the driver must have been configured
* without callbacks (@p end_cb = @p NULL).
* @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
*
* @api
*/
void spiExchange(SPIDriver *spip, size_t n,
const void *txbuf, void *rxbuf) {
chDbgCheck((spip != NULL) && (n > 0) && (rxbuf != NULL) && (txbuf != NULL),
"spiExchange");
chSysLock();
chDbgAssert(spip->state == SPI_READY, "spiExchange(), #1", "not ready");
chDbgAssert(spip->config->end_cb == NULL,
"spiExchange(), #2", "has callback");
spiStartExchangeI(spip, n, txbuf, rxbuf);
_spi_wait_s(spip);
chSysUnlock();
}
/**
* @brief Sends data over the SPI bus.
* @details This synchronous function performs a transmit operation.
* @pre In order to use this function the option @p SPI_USE_WAIT must be
* enabled.
* @pre In order to use this function the driver must have been configured
* without callbacks (@p end_cb = @p NULL).
* @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
*
* @api
*/
void spiSend(SPIDriver *spip, size_t n, const void *txbuf) {
chDbgCheck((spip != NULL) && (n > 0) && (txbuf != NULL), "spiSend");
chSysLock();
chDbgAssert(spip->state == SPI_READY, "spiSend(), #1", "not ready");
chDbgAssert(spip->config->end_cb == NULL, "spiSend(), #2", "has callback");
spiStartSendI(spip, n, txbuf);
_spi_wait_s(spip);
chSysUnlock();
}
/**
* @brief Receives data from the SPI bus.
* @details This synchronous function performs a receive operation.
* @pre In order to use this function the option @p SPI_USE_WAIT must be
* enabled.
* @pre In order to use this function the driver must have been configured
* without callbacks (@p end_cb = @p NULL).
* @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
*
* @api
*/
void spiReceive(SPIDriver *spip, size_t n, void *rxbuf) {
chDbgCheck((spip != NULL) && (n > 0) && (rxbuf != NULL),
"spiReceive");
chSysLock();
chDbgAssert(spip->state == SPI_READY, "spiReceive(), #1", "not ready");
chDbgAssert(spip->config->end_cb == NULL,
"spiReceive(), #2", "has callback");
spiStartReceiveI(spip, n, rxbuf);
_spi_wait_s(spip);
chSysUnlock();
}
#endif /* SPI_USE_WAIT */
#if SPI_USE_MUTUAL_EXCLUSION || defined(__DOXYGEN__)
/**
* @brief Gains exclusive access to the SPI bus.
* @details This function tries to gain ownership to the SPI bus, if the bus
* is already being used then the invoking thread is queued.
* @pre In order to use this function the option @p SPI_USE_MUTUAL_EXCLUSION
* must be enabled.
*
* @param[in] spip pointer to the @p SPIDriver object
*
* @api
*/
void spiAcquireBus(SPIDriver *spip) {
chDbgCheck(spip != NULL, "spiAcquireBus");
#if CH_USE_MUTEXES
chMtxLock(&spip->mutex);
#elif CH_USE_SEMAPHORES
chSemWait(&spip->semaphore);
#endif
}
/**
* @brief Releases exclusive access to the SPI bus.
* @pre In order to use this function the option @p SPI_USE_MUTUAL_EXCLUSION
* must be enabled.
*
* @param[in] spip pointer to the @p SPIDriver object
*
* @api
*/
void spiReleaseBus(SPIDriver *spip) {
chDbgCheck(spip != NULL, "spiReleaseBus");
#if CH_USE_MUTEXES
(void)spip;
chMtxUnlock();
#elif CH_USE_SEMAPHORES
chSemSignal(&spip->semaphore);
#endif
}
#endif /* SPI_USE_MUTUAL_EXCLUSION */
#endif /* HAL_USE_SPI */
/** @} */

View File

@ -0,0 +1,135 @@
/*
ChibiOS/RT - Copyright (C) 2006,2007,2008,2009,2010,
2011,2012,2013 Giovanni Di Sirio.
This file is part of ChibiOS/RT.
ChibiOS/RT is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
ChibiOS/RT is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
---
A special exception to the GPL can be applied should you wish to distribute
a combined work that includes ChibiOS/RT, without being obliged to provide
the source code for any proprietary components. See the file exception.txt
for full details of how and when the exception can be applied.
*/
/**
* @file tm.c
* @brief Time Measurement driver code.
*
* @addtogroup TM
* @{
*/
#include "ch.h"
#include "hal.h"
#if HAL_USE_TM || defined(__DOXYGEN__)
/*===========================================================================*/
/* Driver local definitions. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver exported variables. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver local variables and types. */
/*===========================================================================*/
/**
* @brief Subsystem calibration value.
*/
static halrtcnt_t measurement_offset;
/*===========================================================================*/
/* Driver local functions. */
/*===========================================================================*/
/**
* @brief Starts a measurement.
*
* @param[in,out] tmp pointer to a @p TimeMeasurement structure
*
* @notapi
*/
static void tm_start(TimeMeasurement *tmp) {
tmp->last = halGetCounterValue();
}
/**
* @brief Stops a measurement.
*
* @param[in,out] tmp pointer to a @p TimeMeasurement structure
*
* @notapi
*/
static void tm_stop(TimeMeasurement *tmp) {
halrtcnt_t now = halGetCounterValue();
tmp->last = now - tmp->last - measurement_offset;
if (tmp->last > tmp->worst)
tmp->worst = tmp->last;
else if (tmp->last < tmp->best)
tmp->best = tmp->last;
}
/*===========================================================================*/
/* Driver interrupt handlers. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver exported functions. */
/*===========================================================================*/
/**
* @brief Initializes the Time Measurement unit.
*
* @init
*/
void tmInit(void) {
TimeMeasurement tm;
/* Time Measurement subsystem calibration, it does a null measurement
and calculates the call overhead which is subtracted to real
measurements.*/
measurement_offset = 0;
tmObjectInit(&tm);
tmStartMeasurement(&tm);
tmStopMeasurement(&tm);
measurement_offset = tm.last;
}
/**
* @brief Initializes a @p TimeMeasurement object.
*
* @param[out] tmp pointer to a @p TimeMeasurement structure
*
* @init
*/
void tmObjectInit(TimeMeasurement *tmp) {
tmp->start = tm_start;
tmp->stop = tm_stop;
tmp->last = (halrtcnt_t)0;
tmp->worst = (halrtcnt_t)0;
tmp->best = (halrtcnt_t)-1;
}
#endif /* HAL_USE_TM */
/** @} */

View File

@ -0,0 +1,360 @@
/*
ChibiOS/RT - Copyright (C) 2006,2007,2008,2009,2010,
2011,2012,2013 Giovanni Di Sirio.
This file is part of ChibiOS/RT.
ChibiOS/RT is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
ChibiOS/RT is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
---
A special exception to the GPL can be applied should you wish to distribute
a combined work that includes ChibiOS/RT, without being obliged to provide
the source code for any proprietary components. See the file exception.txt
for full details of how and when the exception can be applied.
*/
/**
* @file uart.c
* @brief UART Driver code.
*
* @addtogroup UART
* @{
*/
#include "ch.h"
#include "hal.h"
#if HAL_USE_UART || defined(__DOXYGEN__)
/*===========================================================================*/
/* Driver local definitions. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver exported variables. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver local variables and types. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver local functions. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver exported functions. */
/*===========================================================================*/
/**
* @brief UART Driver initialization.
* @note This function is implicitly invoked by @p halInit(), there is
* no need to explicitly initialize the driver.
*
* @init
*/
void uartInit(void) {
uart_lld_init();
}
/**
* @brief Initializes the standard part of a @p UARTDriver structure.
*
* @param[out] uartp pointer to the @p UARTDriver object
*
* @init
*/
void uartObjectInit(UARTDriver *uartp) {
uartp->state = UART_STOP;
uartp->txstate = UART_TX_IDLE;
uartp->rxstate = UART_RX_IDLE;
uartp->config = NULL;
/* Optional, user-defined initializer.*/
#if defined(UART_DRIVER_EXT_INIT_HOOK)
UART_DRIVER_EXT_INIT_HOOK(uartp);
#endif
}
/**
* @brief Configures and activates the UART peripheral.
*
* @param[in] uartp pointer to the @p UARTDriver object
* @param[in] config pointer to the @p UARTConfig object
*
* @api
*/
void uartStart(UARTDriver *uartp, const UARTConfig *config) {
chDbgCheck((uartp != NULL) && (config != NULL), "uartStart");
chSysLock();
chDbgAssert((uartp->state == UART_STOP) || (uartp->state == UART_READY),
"uartStart(), #1", "invalid state");
uartp->config = config;
uart_lld_start(uartp);
uartp->state = UART_READY;
chSysUnlock();
}
/**
* @brief Deactivates the UART peripheral.
*
* @param[in] uartp pointer to the @p UARTDriver object
*
* @api
*/
void uartStop(UARTDriver *uartp) {
chDbgCheck(uartp != NULL, "uartStop");
chSysLock();
chDbgAssert((uartp->state == UART_STOP) || (uartp->state == UART_READY),
"uartStop(), #1", "invalid state");
uart_lld_stop(uartp);
uartp->state = UART_STOP;
uartp->txstate = UART_TX_IDLE;
uartp->rxstate = UART_RX_IDLE;
chSysUnlock();
}
/**
* @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
*
* @api
*/
void uartStartSend(UARTDriver *uartp, size_t n, const void *txbuf) {
chDbgCheck((uartp != NULL) && (n > 0) && (txbuf != NULL),
"uartStartSend");
chSysLock();
chDbgAssert(uartp->state == UART_READY,
"uartStartSend(), #1", "is active");
chDbgAssert(uartp->txstate != UART_TX_ACTIVE,
"uartStartSend(), #2", "tx active");
uart_lld_start_send(uartp, n, txbuf);
uartp->txstate = UART_TX_ACTIVE;
chSysUnlock();
}
/**
* @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.
* @note This function has to be invoked from a lock zone.
*
* @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
*
* @iclass
*/
void uartStartSendI(UARTDriver *uartp, size_t n, const void *txbuf) {
chDbgCheckClassI();
chDbgCheck((uartp != NULL) && (n > 0) && (txbuf != NULL),
"uartStartSendI");
chDbgAssert(uartp->state == UART_READY,
"uartStartSendI(), #1", "is active");
chDbgAssert(uartp->txstate != UART_TX_ACTIVE,
"uartStartSendI(), #2", "tx active");
uart_lld_start_send(uartp, n, txbuf);
uartp->txstate = UART_TX_ACTIVE;
}
/**
* @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.
* @retval 0 There was no transmit operation in progress.
*
* @api
*/
size_t uartStopSend(UARTDriver *uartp) {
size_t n;
chDbgCheck(uartp != NULL, "uartStopSend");
chSysLock();
chDbgAssert(uartp->state == UART_READY, "uartStopSend(), #1", "not active");
if (uartp->txstate == UART_TX_ACTIVE) {
n = uart_lld_stop_send(uartp);
uartp->txstate = UART_TX_IDLE;
}
else
n = 0;
chSysUnlock();
return n;
}
/**
* @brief Stops any ongoing transmission.
* @note Stopping a transmission also suppresses the transmission callbacks.
* @note This function has to be invoked from a lock zone.
*
* @param[in] uartp pointer to the @p UARTDriver object
*
* @return The number of data frames not transmitted by the
* stopped transmit operation.
* @retval 0 There was no transmit operation in progress.
*
* @iclass
*/
size_t uartStopSendI(UARTDriver *uartp) {
chDbgCheckClassI();
chDbgCheck(uartp != NULL, "uartStopSendI");
chDbgAssert(uartp->state == UART_READY, "uartStopSendI(), #1", "not active");
if (uartp->txstate == UART_TX_ACTIVE) {
size_t n = uart_lld_stop_send(uartp);
uartp->txstate = UART_TX_IDLE;
return n;
}
return 0;
}
/**
* @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[in] rxbuf the pointer to the receive buffer
*
* @api
*/
void uartStartReceive(UARTDriver *uartp, size_t n, void *rxbuf) {
chDbgCheck((uartp != NULL) && (n > 0) && (rxbuf != NULL),
"uartStartReceive");
chSysLock();
chDbgAssert(uartp->state == UART_READY,
"uartStartReceive(), #1", "is active");
chDbgAssert(uartp->rxstate != UART_RX_ACTIVE,
"uartStartReceive(), #2", "rx active");
uart_lld_start_receive(uartp, n, rxbuf);
uartp->rxstate = UART_RX_ACTIVE;
chSysUnlock();
}
/**
* @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.
* @note This function has to be invoked from a lock zone.
*
* @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
*
* @iclass
*/
void uartStartReceiveI(UARTDriver *uartp, size_t n, void *rxbuf) {
chDbgCheckClassI();
chDbgCheck((uartp != NULL) && (n > 0) && (rxbuf != NULL),
"uartStartReceiveI");
chDbgAssert(uartp->state == UART_READY,
"uartStartReceiveI(), #1", "is active");
chDbgAssert(uartp->rxstate != UART_RX_ACTIVE,
"uartStartReceiveI(), #2", "rx active");
uart_lld_start_receive(uartp, n, rxbuf);
uartp->rxstate = UART_RX_ACTIVE;
}
/**
* @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.
* @retval 0 There was no receive operation in progress.
*
* @api
*/
size_t uartStopReceive(UARTDriver *uartp) {
size_t n;
chDbgCheck(uartp != NULL, "uartStopReceive");
chSysLock();
chDbgAssert(uartp->state == UART_READY,
"uartStopReceive(), #1", "not active");
if (uartp->rxstate == UART_RX_ACTIVE) {
n = uart_lld_stop_receive(uartp);
uartp->rxstate = UART_RX_IDLE;
}
else
n = 0;
chSysUnlock();
return n;
}
/**
* @brief Stops any ongoing receive operation.
* @note Stopping a receive operation also suppresses the receive callbacks.
* @note This function has to be invoked from a lock zone.
*
* @param[in] uartp pointer to the @p UARTDriver object
*
* @return The number of data frames not received by the
* stopped receive operation.
* @retval 0 There was no receive operation in progress.
*
* @iclass
*/
size_t uartStopReceiveI(UARTDriver *uartp) {
chDbgCheckClassI();
chDbgCheck(uartp != NULL, "uartStopReceiveI");
chDbgAssert(uartp->state == UART_READY,
"uartStopReceiveI(), #1", "not active");
if (uartp->rxstate == UART_RX_ACTIVE) {
size_t n = uart_lld_stop_receive(uartp);
uartp->rxstate = UART_RX_IDLE;
return n;
}
return 0;
}
#endif /* HAL_USE_UART */
/** @} */

View File

@ -0,0 +1,810 @@
/*
ChibiOS/RT - Copyright (C) 2006,2007,2008,2009,2010,
2011,2012,2013 Giovanni Di Sirio.
This file is part of ChibiOS/RT.
ChibiOS/RT is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
ChibiOS/RT is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
---
A special exception to the GPL can be applied should you wish to distribute
a combined work that includes ChibiOS/RT, without being obliged to provide
the source code for any proprietary components. See the file exception.txt
for full details of how and when the exception can be applied.
*/
/**
* @file usb.c
* @brief USB Driver code.
*
* @addtogroup USB
* @{
*/
#include <string.h>
#include "ch.h"
#include "hal.h"
#include "usb.h"
#if HAL_USE_USB || defined(__DOXYGEN__)
/*===========================================================================*/
/* Driver local definitions. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver exported variables. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver local variables and types. */
/*===========================================================================*/
static const uint8_t zero_status[] = {0x00, 0x00};
static const uint8_t active_status[] ={0x00, 0x00};
static const uint8_t halted_status[] = {0x01, 0x00};
/*===========================================================================*/
/* Driver local functions. */
/*===========================================================================*/
/**
* @brief SET ADDRESS transaction callback.
*
* @param[in] usbp pointer to the @p USBDriver object
*/
static void set_address(USBDriver *usbp) {
usbp->address = usbp->setup[2];
usb_lld_set_address(usbp);
_usb_isr_invoke_event_cb(usbp, USB_EVENT_ADDRESS);
usbp->state = USB_SELECTED;
}
/**
* @brief Standard requests handler.
* @details This is the standard requests default handler, most standard
* requests are handled here, the user can override the standard
* handling using the @p requests_hook_cb hook in the
* @p USBConfig structure.
*
* @param[in] usbp pointer to the @p USBDriver object
* @return The request handling exit code.
* @retval FALSE Request not recognized by the handler or error.
* @retval TRUE Request handled.
*/
static bool_t default_handler(USBDriver *usbp) {
const USBDescriptor *dp;
/* Decoding the request.*/
switch (((usbp->setup[0] & (USB_RTYPE_RECIPIENT_MASK |
USB_RTYPE_TYPE_MASK)) |
(usbp->setup[1] << 8))) {
case USB_RTYPE_RECIPIENT_DEVICE | (USB_REQ_GET_STATUS << 8):
/* Just returns the current status word.*/
usbSetupTransfer(usbp, (uint8_t *)&usbp->status, 2, NULL);
return TRUE;
case USB_RTYPE_RECIPIENT_DEVICE | (USB_REQ_CLEAR_FEATURE << 8):
/* Only the DEVICE_REMOTE_WAKEUP is handled here, any other feature
number is handled as an error.*/
if (usbp->setup[2] == USB_FEATURE_DEVICE_REMOTE_WAKEUP) {
usbp->status &= ~2;
usbSetupTransfer(usbp, NULL, 0, NULL);
return TRUE;
}
return FALSE;
case USB_RTYPE_RECIPIENT_DEVICE | (USB_REQ_SET_FEATURE << 8):
/* Only the DEVICE_REMOTE_WAKEUP is handled here, any other feature
number is handled as an error.*/
if (usbp->setup[2] == USB_FEATURE_DEVICE_REMOTE_WAKEUP) {
usbp->status |= 2;
usbSetupTransfer(usbp, NULL, 0, NULL);
return TRUE;
}
return FALSE;
case USB_RTYPE_RECIPIENT_DEVICE | (USB_REQ_SET_ADDRESS << 8):
/* The SET_ADDRESS handling can be performed here or postponed after
the status packed depending on the USB_SET_ADDRESS_MODE low
driver setting.*/
#if USB_SET_ADDRESS_MODE == USB_EARLY_SET_ADDRESS
if ((usbp->setup[0] == USB_RTYPE_RECIPIENT_DEVICE) &&
(usbp->setup[1] == USB_REQ_SET_ADDRESS))
set_address(usbp);
usbSetupTransfer(usbp, NULL, 0, NULL);
#else
usbSetupTransfer(usbp, NULL, 0, set_address);
#endif
return TRUE;
case USB_RTYPE_RECIPIENT_DEVICE | (USB_REQ_GET_DESCRIPTOR << 8):
/* Handling descriptor requests from the host.*/
dp = usbp->config->get_descriptor_cb(
usbp, usbp->setup[3], usbp->setup[2],
usbFetchWord(&usbp->setup[4]));
if (dp == NULL)
return FALSE;
usbSetupTransfer(usbp, (uint8_t *)dp->ud_string, dp->ud_size, NULL);
return TRUE;
case USB_RTYPE_RECIPIENT_DEVICE | (USB_REQ_GET_CONFIGURATION << 8):
/* Returning the last selected configuration.*/
usbSetupTransfer(usbp, &usbp->configuration, 1, NULL);
return TRUE;
case USB_RTYPE_RECIPIENT_DEVICE | (USB_REQ_SET_CONFIGURATION << 8):
/* Handling configuration selection from the host.*/
usbp->configuration = usbp->setup[2];
if (usbp->configuration == 0)
usbp->state = USB_SELECTED;
else
usbp->state = USB_ACTIVE;
_usb_isr_invoke_event_cb(usbp, USB_EVENT_CONFIGURED);
usbSetupTransfer(usbp, NULL, 0, NULL);
return TRUE;
case USB_RTYPE_RECIPIENT_INTERFACE | (USB_REQ_GET_STATUS << 8):
case USB_RTYPE_RECIPIENT_ENDPOINT | (USB_REQ_SYNCH_FRAME << 8):
/* Just sending two zero bytes, the application can change the behavior
using a hook..*/
usbSetupTransfer(usbp, (uint8_t *)zero_status, 2, NULL);
return TRUE;
case USB_RTYPE_RECIPIENT_ENDPOINT | (USB_REQ_GET_STATUS << 8):
/* Sending the EP status.*/
if (usbp->setup[4] & 0x80) {
switch (usb_lld_get_status_in(usbp, usbp->setup[4] & 0x0F)) {
case EP_STATUS_STALLED:
usbSetupTransfer(usbp, (uint8_t *)halted_status, 2, NULL);
return TRUE;
case EP_STATUS_ACTIVE:
usbSetupTransfer(usbp, (uint8_t *)active_status, 2, NULL);
return TRUE;
default:
return FALSE;
}
}
else {
switch (usb_lld_get_status_out(usbp, usbp->setup[4] & 0x0F)) {
case EP_STATUS_STALLED:
usbSetupTransfer(usbp, (uint8_t *)halted_status, 2, NULL);
return TRUE;
case EP_STATUS_ACTIVE:
usbSetupTransfer(usbp, (uint8_t *)active_status, 2, NULL);
return TRUE;
default:
return FALSE;
}
}
case USB_RTYPE_RECIPIENT_ENDPOINT | (USB_REQ_CLEAR_FEATURE << 8):
/* Only ENDPOINT_HALT is handled as feature.*/
if (usbp->setup[2] != USB_FEATURE_ENDPOINT_HALT)
return FALSE;
/* Clearing the EP status, not valid for EP0, it is ignored in that case.*/
if ((usbp->setup[4] & 0x0F) > 0) {
if (usbp->setup[4] & 0x80)
usb_lld_clear_in(usbp, usbp->setup[4] & 0x0F);
else
usb_lld_clear_out(usbp, usbp->setup[4] & 0x0F);
}
usbSetupTransfer(usbp, NULL, 0, NULL);
return TRUE;
case USB_RTYPE_RECIPIENT_ENDPOINT | (USB_REQ_SET_FEATURE << 8):
/* Only ENDPOINT_HALT is handled as feature.*/
if (usbp->setup[2] != USB_FEATURE_ENDPOINT_HALT)
return FALSE;
/* Stalling the EP, not valid for EP0, it is ignored in that case.*/
if ((usbp->setup[4] & 0x0F) > 0) {
if (usbp->setup[4] & 0x80)
usb_lld_stall_in(usbp, usbp->setup[4] & 0x0F);
else
usb_lld_stall_out(usbp, usbp->setup[4] & 0x0F);
}
usbSetupTransfer(usbp, NULL, 0, NULL);
return TRUE;
case USB_RTYPE_RECIPIENT_DEVICE | (USB_REQ_SET_DESCRIPTOR << 8):
case USB_RTYPE_RECIPIENT_INTERFACE | (USB_REQ_CLEAR_FEATURE << 8):
case USB_RTYPE_RECIPIENT_INTERFACE | (USB_REQ_SET_FEATURE << 8):
case USB_RTYPE_RECIPIENT_INTERFACE | (USB_REQ_GET_INTERFACE << 8):
case USB_RTYPE_RECIPIENT_INTERFACE | (USB_REQ_SET_INTERFACE << 8):
/* All the above requests are not handled here, if you need them then
use the hook mechanism and provide handling.*/
default:
return FALSE;
}
}
/*===========================================================================*/
/* Driver exported functions. */
/*===========================================================================*/
/**
* @brief USB Driver initialization.
* @note This function is implicitly invoked by @p halInit(), there is
* no need to explicitly initialize the driver.
*
* @init
*/
void usbInit(void) {
usb_lld_init();
}
/**
* @brief Initializes the standard part of a @p USBDriver structure.
*
* @param[out] usbp pointer to the @p USBDriver object
*
* @init
*/
void usbObjectInit(USBDriver *usbp) {
unsigned i;
usbp->state = USB_STOP;
usbp->config = NULL;
for (i = 0; i < USB_MAX_ENDPOINTS; i++) {
usbp->in_params[i] = NULL;
usbp->out_params[i] = NULL;
}
usbp->transmitting = 0;
usbp->receiving = 0;
}
/**
* @brief Configures and activates the USB peripheral.
*
* @param[in] usbp pointer to the @p USBDriver object
* @param[in] config pointer to the @p USBConfig object
*
* @api
*/
void usbStart(USBDriver *usbp, const USBConfig *config) {
unsigned i;
chDbgCheck((usbp != NULL) && (config != NULL), "usbStart");
chSysLock();
chDbgAssert((usbp->state == USB_STOP) || (usbp->state == USB_READY),
"usbStart(), #1", "invalid state");
usbp->config = config;
for (i = 0; i <= USB_MAX_ENDPOINTS; i++)
usbp->epc[i] = NULL;
usb_lld_start(usbp);
usbp->state = USB_READY;
chSysUnlock();
}
/**
* @brief Deactivates the USB peripheral.
*
* @param[in] usbp pointer to the @p USBDriver object
*
* @api
*/
void usbStop(USBDriver *usbp) {
chDbgCheck(usbp != NULL, "usbStop");
chSysLock();
chDbgAssert((usbp->state == USB_STOP) || (usbp->state == USB_READY) ||
(usbp->state == USB_SELECTED) || (usbp->state == USB_ACTIVE),
"usbStop(), #1", "invalid state");
usb_lld_stop(usbp);
usbp->state = USB_STOP;
chSysUnlock();
}
/**
* @brief Enables an endpoint.
* @details This function enables an endpoint, both IN and/or OUT directions
* depending on the configuration structure.
* @note This function must be invoked in response of a SET_CONFIGURATION
* or SET_INTERFACE message.
*
* @param[in] usbp pointer to the @p USBDriver object
* @param[in] ep endpoint number
* @param[in] epcp the endpoint configuration
*
* @iclass
*/
void usbInitEndpointI(USBDriver *usbp, usbep_t ep,
const USBEndpointConfig *epcp) {
chDbgCheckClassI();
chDbgCheck((usbp != NULL) && (epcp != NULL), "usbInitEndpointI");
chDbgAssert(usbp->state == USB_ACTIVE,
"usbEnableEndpointI(), #1", "invalid state");
chDbgAssert(usbp->epc[ep] == NULL,
"usbEnableEndpointI(), #2", "already initialized");
/* Logically enabling the endpoint in the USBDriver structure.*/
if (epcp->in_state != NULL)
memset(epcp->in_state, 0, sizeof(USBInEndpointState));
if (epcp->out_state != NULL)
memset(epcp->out_state, 0, sizeof(USBOutEndpointState));
usbp->epc[ep] = epcp;
/* Low level endpoint activation.*/
usb_lld_init_endpoint(usbp, ep);
}
/**
* @brief Disables all the active endpoints.
* @details This function disables all the active endpoints except the
* endpoint zero.
* @note This function must be invoked in response of a SET_CONFIGURATION
* message with configuration number zero.
*
* @param[in] usbp pointer to the @p USBDriver object
*
* @iclass
*/
void usbDisableEndpointsI(USBDriver *usbp) {
unsigned i;
chDbgCheckClassI();
chDbgCheck(usbp != NULL, "usbDisableEndpointsI");
chDbgAssert(usbp->state == USB_SELECTED,
"usbDisableEndpointsI(), #1", "invalid state");
usbp->transmitting &= ~1;
usbp->receiving &= ~1;
for (i = 1; i <= USB_MAX_ENDPOINTS; i++)
usbp->epc[i] = NULL;
/* Low level endpoints deactivation.*/
usb_lld_disable_endpoints(usbp);
}
/**
* @brief Prepares for a receive transaction on an OUT endpoint.
* @post The endpoint is ready for @p usbStartReceiveI().
* @note This function can be called both in ISR and thread context.
*
* @param[in] usbp pointer to the @p USBDriver object
* @param[in] ep endpoint number
* @param[out] buf buffer where to copy the received data
* @param[in] n transaction size
*
* @special
*/
void usbPrepareReceive(USBDriver *usbp, usbep_t ep, uint8_t *buf, size_t n) {
USBOutEndpointState *osp = usbp->epc[ep]->out_state;
osp->rxqueued = FALSE;
osp->mode.linear.rxbuf = buf;
osp->rxsize = n;
osp->rxcnt = 0;
usb_lld_prepare_receive(usbp, ep);
}
/**
* @brief Prepares for a transmit transaction on an IN endpoint.
* @post The endpoint is ready for @p usbStartTransmitI().
* @note This function can be called both in ISR and thread context.
* @note The queue must contain at least the amount of data specified
* as transaction size.
*
* @param[in] usbp pointer to the @p USBDriver object
* @param[in] ep endpoint number
* @param[in] buf buffer where to fetch the data to be transmitted
* @param[in] n transaction size
*
* @special
*/
void usbPrepareTransmit(USBDriver *usbp, usbep_t ep,
const uint8_t *buf, size_t n) {
USBInEndpointState *isp = usbp->epc[ep]->in_state;
isp->txqueued = FALSE;
isp->mode.linear.txbuf = buf;
isp->txsize = n;
isp->txcnt = 0;
usb_lld_prepare_transmit(usbp, ep);
}
/**
* @brief Prepares for a receive transaction on an OUT endpoint.
* @post The endpoint is ready for @p usbStartReceiveI().
* @note This function can be called both in ISR and thread context.
* @note The queue must have enough free space to accommodate the
* specified transaction size rounded to the next packet size
* boundary. For example if the transaction size is 1 and the
* packet size is 64 then the queue must have space for at least
* 64 bytes.
*
* @param[in] usbp pointer to the @p USBDriver object
* @param[in] ep endpoint number
* @param[in] iqp input queue to be filled with incoming data
* @param[in] n transaction size
*
* @special
*/
void usbPrepareQueuedReceive(USBDriver *usbp, usbep_t ep,
InputQueue *iqp, size_t n) {
USBOutEndpointState *osp = usbp->epc[ep]->out_state;
osp->rxqueued = TRUE;
osp->mode.queue.rxqueue = iqp;
osp->rxsize = n;
osp->rxcnt = 0;
usb_lld_prepare_receive(usbp, ep);
}
/**
* @brief Prepares for a transmit transaction on an IN endpoint.
* @post The endpoint is ready for @p usbStartTransmitI().
* @note This function can be called both in ISR and thread context.
* @note The transmit transaction size is equal to the data contained
* in the queue.
*
* @param[in] usbp pointer to the @p USBDriver object
* @param[in] ep endpoint number
* @param[in] oqp output queue to be fetched for outgoing data
* @param[in] n transaction size
*
* @special
*/
void usbPrepareQueuedTransmit(USBDriver *usbp, usbep_t ep,
OutputQueue *oqp, size_t n) {
USBInEndpointState *isp = usbp->epc[ep]->in_state;
isp->txqueued = TRUE;
isp->mode.queue.txqueue = oqp;
isp->txsize = n;
isp->txcnt = 0;
usb_lld_prepare_transmit(usbp, ep);
}
/**
* @brief Starts a receive transaction on an OUT endpoint.
* @post The endpoint callback is invoked when the transfer has been
* completed.
*
* @param[in] usbp pointer to the @p USBDriver object
* @param[in] ep endpoint number
*
* @return The operation status.
* @retval FALSE Operation started successfully.
* @retval TRUE Endpoint busy, operation not started.
*
* @iclass
*/
bool_t usbStartReceiveI(USBDriver *usbp, usbep_t ep) {
chDbgCheckClassI();
chDbgCheck(usbp != NULL, "usbStartReceiveI");
if (usbGetReceiveStatusI(usbp, ep))
return TRUE;
usbp->receiving |= (1 << ep);
usb_lld_start_out(usbp, ep);
return FALSE;
}
/**
* @brief Starts a transmit transaction on an IN endpoint.
* @post The endpoint callback is invoked when the transfer has been
* completed.
*
* @param[in] usbp pointer to the @p USBDriver object
* @param[in] ep endpoint number
*
* @return The operation status.
* @retval FALSE Operation started successfully.
* @retval TRUE Endpoint busy, operation not started.
*
* @iclass
*/
bool_t usbStartTransmitI(USBDriver *usbp, usbep_t ep) {
chDbgCheckClassI();
chDbgCheck(usbp != NULL, "usbStartTransmitI");
if (usbGetTransmitStatusI(usbp, ep))
return TRUE;
usbp->transmitting |= (1 << ep);
usb_lld_start_in(usbp, ep);
return FALSE;
}
/**
* @brief Stalls an OUT endpoint.
*
* @param[in] usbp pointer to the @p USBDriver object
* @param[in] ep endpoint number
*
* @return The operation status.
* @retval FALSE Endpoint stalled.
* @retval TRUE Endpoint busy, not stalled.
*
* @iclass
*/
bool_t usbStallReceiveI(USBDriver *usbp, usbep_t ep) {
chDbgCheckClassI();
chDbgCheck(usbp != NULL, "usbStallReceiveI");
if (usbGetReceiveStatusI(usbp, ep))
return TRUE;
usb_lld_stall_out(usbp, ep);
return FALSE;
}
/**
* @brief Stalls an IN endpoint.
*
* @param[in] usbp pointer to the @p USBDriver object
* @param[in] ep endpoint number
*
* @return The operation status.
* @retval FALSE Endpoint stalled.
* @retval TRUE Endpoint busy, not stalled.
*
* @iclass
*/
bool_t usbStallTransmitI(USBDriver *usbp, usbep_t ep) {
chDbgCheckClassI();
chDbgCheck(usbp != NULL, "usbStallTransmitI");
if (usbGetTransmitStatusI(usbp, ep))
return TRUE;
usb_lld_stall_in(usbp, ep);
return FALSE;
}
/**
* @brief USB reset routine.
* @details This function must be invoked when an USB bus reset condition is
* detected.
*
* @param[in] usbp pointer to the @p USBDriver object
*
* @notapi
*/
void _usb_reset(USBDriver *usbp) {
unsigned i;
usbp->state = USB_READY;
usbp->status = 0;
usbp->address = 0;
usbp->configuration = 0;
usbp->transmitting = 0;
usbp->receiving = 0;
/* Invalidates all endpoints into the USBDriver structure.*/
for (i = 0; i <= USB_MAX_ENDPOINTS; i++)
usbp->epc[i] = NULL;
/* EP0 state machine initialization.*/
usbp->ep0state = USB_EP0_WAITING_SETUP;
/* Low level reset.*/
usb_lld_reset(usbp);
}
/**
* @brief Default EP0 SETUP callback.
* @details This function is used by the low level driver as default handler
* for EP0 SETUP events.
*
* @param[in] usbp pointer to the @p USBDriver object
* @param[in] ep endpoint number, always zero
*
* @notapi
*/
void _usb_ep0setup(USBDriver *usbp, usbep_t ep) {
size_t max;
usbp->ep0state = USB_EP0_WAITING_SETUP;
usbReadSetup(usbp, ep, usbp->setup);
/* First verify if the application has an handler installed for this
request.*/
if (!(usbp->config->requests_hook_cb) ||
!(usbp->config->requests_hook_cb(usbp))) {
/* Invoking the default handler, if this fails then stalls the
endpoint zero as error.*/
if (((usbp->setup[0] & USB_RTYPE_TYPE_MASK) != USB_RTYPE_TYPE_STD) ||
!default_handler(usbp)) {
/* Error response, the state machine goes into an error state, the low
level layer will have to reset it to USB_EP0_WAITING_SETUP after
receiving a SETUP packet.*/
usb_lld_stall_in(usbp, 0);
usb_lld_stall_out(usbp, 0);
_usb_isr_invoke_event_cb(usbp, USB_EVENT_STALLED);
usbp->ep0state = USB_EP0_ERROR;
return;
}
}
#if (USB_SET_ADDRESS_ACK_HANDLING == USB_SET_ADDRESS_ACK_HW)
if (usbp->setup[1] == USB_REQ_SET_ADDRESS) {
/* Zero-length packet sent by hardware */
return;
}
#endif
/* Transfer preparation. The request handler must have populated
correctly the fields ep0next, ep0n and ep0endcb using the macro
usbSetupTransfer().*/
max = usbFetchWord(&usbp->setup[6]);
/* The transfer size cannot exceed the specified amount.*/
if (usbp->ep0n > max)
usbp->ep0n = max;
if ((usbp->setup[0] & USB_RTYPE_DIR_MASK) == USB_RTYPE_DIR_DEV2HOST) {
/* IN phase.*/
if (usbp->ep0n > 0) {
/* Starts the transmit phase.*/
usbp->ep0state = USB_EP0_TX;
usbPrepareTransmit(usbp, 0, usbp->ep0next, usbp->ep0n);
chSysLockFromIsr();
usbStartTransmitI(usbp, 0);
chSysUnlockFromIsr();
}
else {
/* No transmission phase, directly receiving the zero sized status
packet.*/
usbp->ep0state = USB_EP0_WAITING_STS;
#if (USB_EP0_STATUS_STAGE == USB_EP0_STATUS_STAGE_SW)
usbPrepareReceive(usbp, 0, NULL, 0);
chSysLockFromIsr();
usbStartReceiveI(usbp, 0);
chSysUnlockFromIsr();
#else
usb_lld_end_setup(usbp, ep);
#endif
}
}
else {
/* OUT phase.*/
if (usbp->ep0n > 0) {
/* Starts the receive phase.*/
usbp->ep0state = USB_EP0_RX;
usbPrepareReceive(usbp, 0, usbp->ep0next, usbp->ep0n);
chSysLockFromIsr();
usbStartReceiveI(usbp, 0);
chSysUnlockFromIsr();
}
else {
/* No receive phase, directly sending the zero sized status
packet.*/
usbp->ep0state = USB_EP0_SENDING_STS;
#if (USB_EP0_STATUS_STAGE == USB_EP0_STATUS_STAGE_SW)
usbPrepareTransmit(usbp, 0, NULL, 0);
chSysLockFromIsr();
usbStartTransmitI(usbp, 0);
chSysUnlockFromIsr();
#else
usb_lld_end_setup(usbp, ep);
#endif
}
}
}
/**
* @brief Default EP0 IN callback.
* @details This function is used by the low level driver as default handler
* for EP0 IN events.
*
* @param[in] usbp pointer to the @p USBDriver object
* @param[in] ep endpoint number, always zero
*
* @notapi
*/
void _usb_ep0in(USBDriver *usbp, usbep_t ep) {
size_t max;
(void)ep;
switch (usbp->ep0state) {
case USB_EP0_TX:
max = usbFetchWord(&usbp->setup[6]);
/* If the transmitted size is less than the requested size and it is a
multiple of the maximum packet size then a zero size packet must be
transmitted.*/
if ((usbp->ep0n < max) && ((usbp->ep0n % usbp->epc[0]->in_maxsize) == 0)) {
usbPrepareTransmit(usbp, 0, NULL, 0);
chSysLockFromIsr();
usbStartTransmitI(usbp, 0);
chSysUnlockFromIsr();
usbp->ep0state = USB_EP0_WAITING_TX0;
return;
}
/* Falls into, it is intentional.*/
case USB_EP0_WAITING_TX0:
/* Transmit phase over, receiving the zero sized status packet.*/
usbp->ep0state = USB_EP0_WAITING_STS;
#if (USB_EP0_STATUS_STAGE == USB_EP0_STATUS_STAGE_SW)
usbPrepareReceive(usbp, 0, NULL, 0);
chSysLockFromIsr();
usbStartReceiveI(usbp, 0);
chSysUnlockFromIsr();
#else
usb_lld_end_setup(usbp, ep);
#endif
return;
case USB_EP0_SENDING_STS:
/* Status packet sent, invoking the callback if defined.*/
if (usbp->ep0endcb != NULL)
usbp->ep0endcb(usbp);
usbp->ep0state = USB_EP0_WAITING_SETUP;
return;
default:
;
}
/* Error response, the state machine goes into an error state, the low
level layer will have to reset it to USB_EP0_WAITING_SETUP after
receiving a SETUP packet.*/
usb_lld_stall_in(usbp, 0);
usb_lld_stall_out(usbp, 0);
_usb_isr_invoke_event_cb(usbp, USB_EVENT_STALLED);
usbp->ep0state = USB_EP0_ERROR;
}
/**
* @brief Default EP0 OUT callback.
* @details This function is used by the low level driver as default handler
* for EP0 OUT events.
*
* @param[in] usbp pointer to the @p USBDriver object
* @param[in] ep endpoint number, always zero
*
* @notapi
*/
void _usb_ep0out(USBDriver *usbp, usbep_t ep) {
(void)ep;
switch (usbp->ep0state) {
case USB_EP0_RX:
/* Receive phase over, sending the zero sized status packet.*/
usbp->ep0state = USB_EP0_SENDING_STS;
#if (USB_EP0_STATUS_STAGE == USB_EP0_STATUS_STAGE_SW)
usbPrepareTransmit(usbp, 0, NULL, 0);
chSysLockFromIsr();
usbStartTransmitI(usbp, 0);
chSysUnlockFromIsr();
#else
usb_lld_end_setup(usbp, ep);
#endif
return;
case USB_EP0_WAITING_STS:
/* Status packet received, it must be zero sized, invoking the callback
if defined.*/
#if (USB_EP0_STATUS_STAGE == USB_EP0_STATUS_STAGE_SW)
if (usbGetReceiveTransactionSizeI(usbp, 0) != 0)
break;
#endif
if (usbp->ep0endcb != NULL)
usbp->ep0endcb(usbp);
usbp->ep0state = USB_EP0_WAITING_SETUP;
return;
default:
;
}
/* Error response, the state machine goes into an error state, the low
level layer will have to reset it to USB_EP0_WAITING_SETUP after
receiving a SETUP packet.*/
usb_lld_stall_in(usbp, 0);
usb_lld_stall_out(usbp, 0);
_usb_isr_invoke_event_cb(usbp, USB_EVENT_STALLED);
usbp->ep0state = USB_EP0_ERROR;
}
#endif /* HAL_USE_USB */
/** @} */