Improved DAC driver, updated STM32 DACv1.

git-svn-id: svn://svn.code.sf.net/p/chibios/svn/trunk@16315 27425a3e-05d8-49a3-a47f-9c15f0e5edd8
This commit is contained in:
Giovanni Di Sirio 2023-07-09 08:34:39 +00:00
parent f77d00b0c9
commit 665b0d48b4
5 changed files with 2309 additions and 2207 deletions

View File

@ -40,11 +40,14 @@
* @{
*/
/**
* @brief Enables synchronous APIs.
* @note Disabling this option saves both code and data space.
* @brief Support for thread synchronization API.
*/
#if !defined(DAC_USE_SYNCHRONIZATION) || defined(__DOXYGEN__)
#if !defined(DAC_USE_WAIT) || defined(__DOXYGEN__)
#define DAC_USE_WAIT TRUE
#define DAC_USE_SYNCHRONIZATION FALSE
#else
#define DAC_USE_SYNCHRONIZATION DAC_USE_WAIT
#endif
#endif
/**
@ -163,12 +166,12 @@ struct hal_dac_driver {
* @brief Current configuration data.
*/
const DACConfig *config;
#if (DAC_USE_WAIT == TRUE) || defined(__DOXYGEN__)
#if (DAC_USE_SYNCHRONIZATION == TRUE) || defined(__DOXYGEN__)
/**
* @brief Waiting thread.
*/
thread_reference_t thread;
#endif /* DAC_USE_WAIT */
#endif /* DAC_USE_SYNCHRONIZATION */
#if (DAC_USE_MUTUAL_EXCLUSION == TRUE) || defined(__DOXYGEN__)
/**
* @brief Mutex protecting the bus.
@ -205,7 +208,7 @@ struct hal_dac_driver {
*/
#define dacIsBufferComplete(dacp) ((bool)((dacp)->state == DAC_COMPLETE))
#if (DAC_USE_WAIT == TRUE) || defined(__DOXYGEN__)
#if (DAC_USE_SYNCHRONIZATION == TRUE) || defined(__DOXYGEN__)
/**
* @brief Waits for operation completion.
* @details This function waits for the driver to complete the current
@ -264,13 +267,13 @@ struct hal_dac_driver {
osalSysUnlockFromISR(); \
}
#else /* !DAC_USE_WAIT */
#else /* !DAC_USE_SYNCHRONIZATION */
#define _dac_wait_s(dacp)
#define _dac_reset_i(dacp)
#define _dac_reset_s(dacp)
#define _dac_wakeup_isr(dacp)
#define _dac_timeout_isr(dacp)
#endif /* !DAC_USE_WAIT */
#endif /* !DAC_USE_SYNCHRONIZATION */
/**
* @brief Common ISR code, half buffer event.
@ -310,13 +313,14 @@ struct hal_dac_driver {
if ((dacp)->state == DAC_COMPLETE) \
(dacp)->state = DAC_ACTIVE; \
} \
_dac_wakeup_isr(dacp); \
}
/**
* @brief Common ISR code, error event.
* @details This code handles the portable part of the ISR code:
* - Callback invocation.
* - Waiting thread timeout signaling, if any.
* - Waiting thread timeout signalling, if any.
* - Driver state transitions.
* .
* @note This macro is meant to be used in the low level drivers
@ -360,10 +364,12 @@ extern "C" {
dacsample_t *samples, size_t depth);
void dacStopConversion(DACDriver *dacp);
void dacStopConversionI(DACDriver *dacp);
#if DAC_USE_WAIT
#if DAC_USE_SYNCHRONIZATION
msg_t dacConvert(DACDriver *dacp, const DACConversionGroup *grpp,
dacsample_t *samples, size_t depth);
#endif
msg_t dacSynchronizeS(DACDriver *dacp, sysinterval_t timeout);
msg_t dacSynchronize(DACDriver *dacp, sysinterval_t timeout);
#endif /* DAC_USE_SYNCHRONIZATION */
#if DAC_USE_MUTUAL_EXCLUSION
void dacAcquireBus(DACDriver *dacp);
void dacReleaseBus(DACDriver *dacp);

View File

@ -68,6 +68,9 @@
STM32_DAC4_CH2_DMA_CHN)
#define CHANNEL_DATA_OFFSET 3U
#define CHANNEL_REGISTER_SHIFT 16U
#define CHANNEL_REGISTER_MASK1 0xFFFF0000U
#define CHANNEL_REGISTER_MASK2 0x0000FFFFU
/*===========================================================================*/
/* Driver exported variables. */
@ -122,7 +125,7 @@ static const dacparams_t dac1_ch1_params = {
.dac = DAC1,
.dataoffset = 0U,
.regshift = 0U,
.regmask = 0xFFFF0000U,
.regmask = CHANNEL_REGISTER_MASK1,
.dmastream = STM32_DAC_DAC1_CH1_DMA_STREAM,
#if STM32_DMA_SUPPORTS_DMAMUX
.peripheral = STM32_DMAMUX1_DAC1_CH1,
@ -140,8 +143,8 @@ static const dacparams_t dac1_ch1_params = {
static const dacparams_t dac1_ch2_params = {
.dac = DAC1,
.dataoffset = CHANNEL_DATA_OFFSET,
.regshift = 16U,
.regmask = 0x0000FFFFU,
.regshift = CHANNEL_REGISTER_SHIFT,
.regmask = CHANNEL_REGISTER_MASK2,
.dmastream = STM32_DAC_DAC1_CH2_DMA_STREAM,
#if STM32_DMA_SUPPORTS_DMAMUX
.peripheral = STM32_DMAMUX1_DAC1_CH2,
@ -160,7 +163,7 @@ static const dacparams_t dac2_ch1_params = {
.dac = DAC2,
.dataoffset = 0U,
.regshift = 0U,
.regmask = 0xFFFF0000U,
.regmask = CHANNEL_REGISTER_MASK1,
.dmastream = STM32_DAC_DAC2_CH1_DMA_STREAM,
#if STM32_DMA_SUPPORTS_DMAMUX
.peripheral = STM32_DMAMUX1_DAC2_CH1,
@ -178,8 +181,8 @@ static const dacparams_t dac2_ch1_params = {
static const dacparams_t dac2_ch2_params = {
.dac = DAC2,
.dataoffset = CHANNEL_DATA_OFFSET,
.regshift = 16U,
.regmask = 0x0000FFFFU,
.regshift = CHANNEL_REGISTER_SHIFT,
.regmask = CHANNEL_REGISTER_MASK2,
.dmastream = STM32_DAC_DAC2_CH2_DMA_STREAM,
#if STM32_DMA_SUPPORTS_DMAMUX
.peripheral = STM32_DMAMUX1_DAC2_CH2,
@ -198,7 +201,7 @@ static const dacparams_t dac3_ch1_params = {
.dac = DAC3,
.dataoffset = 0U,
.regshift = 0U,
.regmask = 0xFFFF0000U,
.regmask = CHANNEL_REGISTER_MASK1,
.dmastream = STM32_DAC_DAC3_CH1_DMA_STREAM,
#if STM32_DMA_SUPPORTS_DMAMUX
.peripheral = STM32_DMAMUX1_DAC3_CH1,
@ -216,8 +219,8 @@ static const dacparams_t dac3_ch1_params = {
static const dacparams_t dac3_ch2_params = {
.dac = DAC3,
.dataoffset = CHANNEL_DATA_OFFSET,
.regshift = 16U,
.regmask = 0x0000FFFFU,
.regshift = CHANNEL_REGISTER_SHIFT,
.regmask = CHANNEL_REGISTER_MASK2,
.dmastream = STM32_DAC_DAC3_CH2_DMA_STREAM,
#if STM32_DMA_SUPPORTS_DMAMUX
.peripheral = STM32_DMAMUX1_DAC3_CH2,
@ -236,7 +239,7 @@ static const dacparams_t dac4_ch1_params = {
.dac = DAC4,
.dataoffset = 0U,
.regshift = 0U,
.regmask = 0xFFFF0000U,
.regmask = CHANNEL_REGISTER_MASK1,
.dmastream = STM32_DAC_DAC4_CH1_DMA_STREAM,
#if STM32_DMA_SUPPORTS_DMAMUX
.peripheral = STM32_DMAMUX1_DAC4_CH1,
@ -254,8 +257,8 @@ static const dacparams_t dac4_ch1_params = {
static const dacparams_t dac4_ch2_params = {
.dac = DAC4,
.dataoffset = CHANNEL_DATA_OFFSET,
.regshift = 16U,
.regmask = 0x0000FFFFU,
.regshift = CHANNEL_REGISTER_SHIFT,
.regmask = CHANNEL_REGISTER_MASK2,
.dmastream = STM32_DAC_DAC4_CH2_DMA_STREAM,
#if STM32_DMA_SUPPORTS_DMAMUX
.peripheral = STM32_DMAMUX1_DAC4_CH2,
@ -429,30 +432,41 @@ void dac_lld_start(DACDriver *dacp) {
}
#endif
/* Enabling DAC in SW triggering mode initially, initializing data to
zero.*/
#if STM32_DAC_DUAL_MODE == FALSE
/* Enabling DAC in SW triggering mode initially, initializing data to
configuration default.*/
{
uint32_t cr;
uint32_t reg;
cr = dacp->params->dac->CR;
cr &= dacp->params->regmask;
cr |= (DAC_CR_EN1 | dacp->config->cr) << dacp->params->regshift;
dacp->params->dac->CR = cr;
dac_lld_put_channel(dacp, channel, dacp->config->init);
/* Operating in SINGLE mode with one channel to set. Set registers for
specified channel from configuration. Lower half word of
configuration specifies configuration for any channel.*/
reg = dacp->params->dac->MCR & dacp->params->regmask;
dacp->params->dac->MCR = reg |
((dacp->config->mcr & ~dacp->params->regmask) << dacp->params->regshift);
/* Enable and initialise the channel.*/
reg = dacp->params->dac->CR;
reg &= dacp->params->regmask;
reg |= (DAC_CR_EN1 | dacp->config->cr) << dacp->params->regshift;
dacp->params->dac->CR = reg;
dac_lld_put_channel(dacp, channel, (dacsample_t)dacp->config->init);
}
#else
if ((dacp->config->datamode == DAC_DHRM_12BIT_RIGHT_DUAL) ||
(dacp->config->datamode == DAC_DHRM_12BIT_LEFT_DUAL) ||
(dacp->config->datamode == DAC_DHRM_8BIT_RIGHT_DUAL)) {
dacp->params->dac->CR = DAC_CR_EN2 | (dacp->config->cr << 16) | DAC_CR_EN1 | dacp->config->cr;
dac_lld_put_channel(dacp, 1U, dacp->config->init);
}
else {
dacp->params->dac->CR = DAC_CR_EN1 | dacp->config->cr;
}
dac_lld_put_channel(dacp, channel, dacp->config->init);
#endif
#else /* STM32_DAC_DUAL_MODE != FALSE */
/* Operating in DUAL mode with two channels to setup. Set registers for
both channels from configuration. Lower and upper half words specify
configuration for channels CH1 & CH2 respectively.*/
(void)channel;
dacp->params->dac->MCR = dacp->config->mcr;
/* Enable and initialise both CH1 and CH2. Mask out DMA and calibrate.*/
reg = dacp->config->cr;
reg &= ~(DAC_CR_DMAEN1 | DAC_CR_DMAEN2 | DAC_CR_CEN1 | DAC_CR_CEN2);
dacp->params->dac->CR = DAC_CR_EN2 | DAC_CR_EN1 | reg;
dac_lld_put_channel(dacp, 0U, (dacsample_t)dacp->config->init);
dac_lld_put_channel(dacp, 1U, (dacsample_t)(dacp->config->init >>
(sizeof(dacsample_t) * 8)));
#endif /* STM32_DAC_DUAL_MODE == FALSE */
}
}
@ -555,6 +569,8 @@ void dac_lld_stop(DACDriver *dacp) {
/**
* @brief Outputs a value directly on a DAC channel.
* @note While a group is active in DUAL mode on CH1 only then CH2
* is available for normal output (put) operations.
*
* @param[in] dacp pointer to the @p DACDriver object
* @param[in] channel DAC channel number
@ -566,6 +582,13 @@ void dac_lld_put_channel(DACDriver *dacp,
dacchannel_t channel,
dacsample_t sample) {
#if STM32_DAC_DUAL_MODE
if (dacp->grpp != NULL) {
osalDbgAssert(dacp->grpp->num_channels == 1 && channel == 1,
"channel busy");
}
#endif /* STM32_DAC_DUAL_MODE */
switch (dacp->config->datamode) {
case DAC_DHRM_12BIT_RIGHT:
#if STM32_DAC_DUAL_MODE
@ -638,6 +661,11 @@ void dac_lld_put_channel(DACDriver *dacp,
* as a single 16 bits sample and packed into a single dacsample_t
* element. The num_channels must be set to one in the group
* conversion configuration structure.
* @note If using DUAL mode with a single channel conversion then CH2
* is enabled for manual (put_channel) for non DMA triggered use. The
* the data format for put operations is specified in the upper half
* word of the 'datamode' field. The CR setting is in the upper half
* word of the 'cr' field of the configuration.
*
* @param[in] dacp pointer to the @p DACDriver object
*
@ -744,15 +772,18 @@ void dac_lld_start_conversion(DACDriver *dacp) {
STM32_DMA_CR_HTIE | STM32_DMA_CR_TCIE);
dmaStreamEnable(dacp->dma);
/* DAC configuration.*/
/* DAC configuration. Mask out DMA and calibration.*/
cr = dacp->params->dac->CR;
cr &= ~(DAC_CR_CEN1 | DAC_CR_CEN2 | DAC_CR_DMAEN2);
#if STM32_DAC_DUAL_MODE == FALSE
/* Start the DMA on the single channel.*/
cr &= dacp->params->regmask;
cr |= (DAC_CR_DMAEN1 | (dacp->grpp->trigger << DAC_CR_TSEL1_Pos) | DAC_CR_TEN1 | DAC_CR_EN1 | dacp->config->cr) << dacp->params->regshift;
cr |= (DAC_CR_DMAEN1 | (dacp->grpp->trigger << DAC_CR_TSEL1_Pos) |
DAC_CR_TEN1 | DAC_CR_EN1 | dacp->config->cr) << dacp->params->regshift;
#else
cr = DAC_CR_DMAEN1 | (dacp->grpp->trigger << DAC_CR_TSEL1_Pos) | DAC_CR_TEN1 | DAC_CR_EN1 | dacp->config->cr
| (dacp->grpp->trigger << DAC_CR_TSEL2_Pos) | DAC_CR_TEN2 | DAC_CR_EN2 | (dacp->config->cr << 16);
/* Enable the DMA operation on CH1.*/
cr = DAC_CR_DMAEN1 | (dacp->grpp->trigger << DAC_CR_TSEL1_Pos) |
DAC_CR_TEN1 | DAC_CR_EN1 | dacp->config->cr;
#endif
dacp->params->dac->CR = cr;
@ -760,9 +791,9 @@ void dac_lld_start_conversion(DACDriver *dacp) {
/**
* @brief Stops an ongoing conversion.
* @details This function stops the currently ongoing conversion and returns
* the driver in the @p DAC_READY state. If there was no conversion
* being processed then the function does nothing.
* @details This function stops the currently ongoing conversion. The
* configuration is restored to start condition. The DOR values
* are not updated.
*
* @param[in] dacp pointer to the @p DACDriver object
*
@ -776,21 +807,19 @@ void dac_lld_stop_conversion(DACDriver *dacp) {
dmaStreamFreeI(dacp->dma);
dacp->dma = NULL;
/* Restore start configuration but leave DORx at current values.*/
cr = dacp->params->dac->CR;
#if STM32_DAC_DUAL_MODE == FALSE
uint32_t mcr;
mcr = dacp->params->dac->MCR & dacp->params->regmask;
dacp->params->dac->MCR = mcr |
((dacp->config->mcr & dacp->params->regmask) << dacp->params->regshift);
cr &= dacp->params->regmask;
cr |= (DAC_CR_EN1 | dacp->config->cr) << dacp->params->regshift;
cr |= (DAC_CR_EN1 | (dacp->config->cr & ~dacp->params->regmask)) <<
dacp->params->regshift;
#else
if ((dacp->config->datamode == DAC_DHRM_12BIT_RIGHT_DUAL) ||
(dacp->config->datamode == DAC_DHRM_12BIT_LEFT_DUAL) ||
(dacp->config->datamode == DAC_DHRM_8BIT_RIGHT_DUAL)) {
cr = DAC_CR_EN2 | (dacp->config->cr << 16) |
DAC_CR_EN1 | dacp->config->cr;
}
else {
cr = DAC_CR_EN1 | dacp->config->cr;
}
dacp->params->dac->MCR = dacp->config->mcr;
cr = dacp->config->cr | DAC_CR_EN1 | DAC_CR_EN2;
#endif
dacp->params->dac->CR = cr;

View File

@ -31,14 +31,6 @@
/* Driver constants. */
/*===========================================================================*/
/**
* @name DAC trigger modes
* @{
*/
#define DAC_TRG_MASK 7U
#define DAC_TRG(n) (n)
/** @} */
/*===========================================================================*/
/* Driver pre-compile time settings. */
/*===========================================================================*/
@ -487,7 +479,7 @@
/**
* @brief Max DAC channels.
*/
#if STM32_DAC_DUAL_MODE == FALSE
#if STM32_DAC_DUAL_MODE == TRUE
#define DAC_MAX_CHANNELS 2
#else
#define DAC_MAX_CHANNELS 1
@ -571,10 +563,6 @@ typedef enum {
#endif
} dacdhrmode_t;
/*===========================================================================*/
/* Driver macros. */
/*===========================================================================*/
/**
* @brief Low level fields of the DAC driver structure.
*/
@ -586,14 +574,18 @@ typedef enum {
/**
* @brief Low level fields of the DAC configuration structure.
* @note In DUAL mode init, cr and mcr fields hold CH1 settings in their
* lower 16 bits and CH2 settings in the upper 16 bits.
*/
#define dac_lld_config_fields \
/* Initial output on DAC channels.*/ \
dacsample_t init; \
/* Initial output on DAC channel.*/ \
uint32_t init; \
/* DAC data holding register mode.*/ \
dacdhrmode_t datamode; \
/* DAC control register lower 16 bits.*/ \
uint32_t cr
/* DAC control register.*/ \
uint32_t cr; \
/* DAC mode control register.*/ \
uint32_t mcr
/**
* @brief Low level fields of the DAC group configuration structure.
@ -604,6 +596,23 @@ typedef enum {
initialization. All other fields are handled internally.*/ \
uint32_t trigger
/*===========================================================================*/
/* Driver macros. */
/*===========================================================================*/
/**
* @name DAC trigger modes
* @{
*/
#define DAC_TRG_MASK 7U
#define DAC_TRG(n) (n)
/** @} */
/**
* @brief Shift of initialisation value for channel 2 in dual mode.
*/
#define DAC_VALUE_DUAL(n) ((n) << (sizeof(dacsample_t) * 8))
/*===========================================================================*/
/* External declarations. */
/*===========================================================================*/

View File

@ -158,7 +158,8 @@ void dacStop(DACDriver *dacp) {
void dacPutChannelX(DACDriver *dacp, dacchannel_t channel, dacsample_t sample) {
osalDbgCheck(channel < (dacchannel_t)DAC_MAX_CHANNELS);
osalDbgAssert(dacp->state == DAC_READY, "invalid state");
osalDbgAssert(dacp->state == DAC_READY || dacp->state == DAC_ACTIVE,
"invalid state");
dac_lld_put_channel(dacp, channel, sample);
}
@ -193,10 +194,10 @@ void dacStartConversion(DACDriver *dacp,
* @brief Starts a DAC conversion.
* @details Starts an asynchronous conversion operation.
* @post The callbacks associated to the conversion group will be invoked
* on buffer fill and error events.
* on buffer complete 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
* buffer depth. The samples are sequentially organised in the buffer
* with no gaps.
*
* @param[in] dacp pointer to the @p DACDriver object
@ -284,25 +285,25 @@ void dacStopConversionI(DACDriver *dacp) {
}
}
#if (DAC_USE_WAIT == TRUE) || defined(__DOXYGEN__)
#if (DAC_USE_SYNCHRONIZATION == TRUE) || defined(__DOXYGEN__)
/**
* @brief Performs a DAC 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
* buffer depth. The samples are sequentially organised in the buffer
* with no gaps.
*
* @param[in] dacp pointer to the @p DACDriver object
* @param[in] grpp pointer to a @p DACConversionGroup object
* @param[out] samples pointer to the samples buffer
* @param[in] 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 MSG_OK Conversion finished.
* @retval MSG_RESET The conversion has been stopped using
* @p acdStopConversion() or @p acdStopConversionI(),
* the result buffer may contain incorrect data.
* @p dacStopConversion() or @p dacStopConversionI().
* @retval MSG_TIMEOUT The conversion has been stopped because an hardware
* error.
*
@ -322,7 +323,63 @@ msg_t dacConvert(DACDriver *dacp,
osalSysUnlock();
return msg;
}
#endif /* DAC_USE_WAIT == TRUE */
/**
* @brief Synchronize to a conversion completion.
* @note This function can only be called by a single thread at time.
*
* @param[in] dacp pointer to the @p DACDriver object
* @param[in] timeout wait timeout
*
* @return The wait result.
* @retval MSG_OK if operation completed without errors.
* @retval MSG_TIMEOUT if synchronization request timed out.
* @retval MSG_RESET if the conversion has been stopped.
*
* @sclass
*/
msg_t dacSynchronizeS(DACDriver *dacp, sysinterval_t timeout) {
msg_t msg;
osalDbgCheckClassS();
osalDbgCheck(dacp != NULL);
osalDbgAssert((dacp->state == DAC_ACTIVE) || (dacp->state == DAC_READY),
"invalid state");
if (dacp->state == DAC_ACTIVE) {
msg = osalThreadSuspendTimeoutS(&dacp->thread, timeout);
}
else {
msg = MSG_OK;
}
return msg;
}
/**
* @brief Synchronize to a conversion completion.
* @note This function can only be called by a single thread at time.
*
* @param[in] dacp pointer to the @p DACDriver object
* @param[in] timeout wait timeout
*
* @return The wait result.
* @retval MSG_OK if operation completed without errors.
* @retval MSG_TIMEOUT if synchronization request timed out.
* @retval MSG_RESET if the conversion has been stopped.
*
* @api
*/
msg_t dacSynchronize(DACDriver *dacp, sysinterval_t timeout) {
msg_t msg;
osalSysLock();
msg = dacSynchronizeS(dacp, timeout);
osalSysUnlock();
return msg;
}
#endif /* DAC_USE_SYNCHRONIZATION == TRUE */
#if (DAC_USE_MUTUAL_EXCLUSION == TRUE) || defined(__DOXYGEN__)
/**

View File

@ -74,6 +74,7 @@
*****************************************************************************
*** Next ***
- NEW: Improved DAC driver, updated STM32 DACv1.
- NEW: STM32 RTCv2 and RTCv3 modified to not use shadow registers.
- NEW: Enhanced STM32F7xx MPU configuration in mcuconf.h.
- NEW: I2C slave support in HAL high level driver.