From 665b0d48b48f978e9de50b681580e78baef84037 Mon Sep 17 00:00:00 2001 From: Giovanni Di Sirio Date: Sun, 9 Jul 2023 08:34:39 +0000 Subject: [PATCH] Improved DAC driver, updated STM32 DACv1. git-svn-id: svn://svn.code.sf.net/p/chibios/svn/trunk@16315 27425a3e-05d8-49a3-a47f-9c15f0e5edd8 --- os/hal/include/hal_dac.h | 764 ++++----- os/hal/ports/STM32/LLD/DACv1/hal_dac_lld.c | 1631 ++++++++++---------- os/hal/ports/STM32/LLD/DACv1/hal_dac_lld.h | 1333 ++++++++-------- os/hal/src/hal_dac.c | 787 +++++----- readme.txt | 1 + 5 files changed, 2309 insertions(+), 2207 deletions(-) diff --git a/os/hal/include/hal_dac.h b/os/hal/include/hal_dac.h index 2c0fb5c0b..9e1c4041d 100644 --- a/os/hal/include/hal_dac.h +++ b/os/hal/include/hal_dac.h @@ -1,379 +1,385 @@ -/* - ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -/** - * @file hal_dac.h - * @brief DAC Driver macros and structures. - * - * @addtogroup DAC - * @{ - */ - -#ifndef HAL_DAC_H -#define HAL_DAC_H - -#if (HAL_USE_DAC == TRUE) || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Driver constants. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver pre-compile time settings. */ -/*===========================================================================*/ - -/** - * @name DAC configuration options - * @{ - */ -/** - * @brief Enables synchronous APIs. - * @note Disabling this option saves both code and data space. - */ -#if !defined(DAC_USE_WAIT) || defined(__DOXYGEN__) -#define DAC_USE_WAIT TRUE -#endif - -/** - * @brief Enables the @p dacAcquireBus() and @p dacReleaseBus() APIs. - * @note Disabling this option saves both code and data space. - */ -#if !defined(DAC_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__) -#define DAC_USE_MUTUAL_EXCLUSION TRUE -#endif -/** @} */ - -/*===========================================================================*/ -/* Derived constants and error checks. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver data structures and types. */ -/*===========================================================================*/ - -/** - * @brief Driver state machine possible states. - */ -typedef enum { - DAC_UNINIT = 0, /**< Not initialized. */ - DAC_STOP = 1, /**< Stopped. */ - DAC_READY = 2, /**< Ready. */ - DAC_ACTIVE = 3, /**< Exchanging data. */ - DAC_COMPLETE = 4, /**< Asynchronous operation complete. */ - DAC_ERROR = 5 /**< Error. */ -} dacstate_t; - -/** - * @brief Type of a structure representing an DAC driver. - */ -typedef struct hal_dac_driver DACDriver; - -/** - * @brief Type of a structure representing an DAC driver configuration. - */ -typedef struct hal_dac_config DACConfig; - -/** - * @brief Type of a DAC conversion group. - */ -typedef struct hal_dac_conversion_group DACConversionGroup; - -/* Including the low level driver header, it exports information required - for completing types.*/ -#include "hal_dac_lld.h" - -/** - * @brief DAC notification callback type. - * - * @param[in] dacp pointer to the @p DACDriver object triggering the - */ -typedef void (*daccallback_t)(DACDriver *dacp); - -/** - * @brief DAC error callback type. - * - * @param[in] dacp pointer to the @p DACDriver object triggering the - * callback - * @param[in] err DAC error code - */ -typedef void (*dacerrorcallback_t)(DACDriver *dacp, dacerror_t err); - -/** - * @brief DAC Conversion group structure. - */ -struct hal_dac_conversion_group { - /** - * @brief Number of DAC channels. - */ - uint32_t num_channels; - /** - * @brief Operation complete callback or @p NULL. - */ - daccallback_t end_cb; - /** - * @brief Error handling callback or @p NULL. - */ - dacerrorcallback_t error_cb; - /* End of the mandatory fields.*/ - dac_lld_conversion_group_fields; -}; - -/** - * @brief Driver configuration structure. - */ -struct hal_dac_config { - /* End of the mandatory fields.*/ - dac_lld_config_fields; -}; - -/** - * @brief Structure representing a DAC driver. - */ -struct hal_dac_driver { - /** - * @brief Driver state. - */ - dacstate_t state; - /** - * @brief Conversion group. - */ - const DACConversionGroup *grpp; - /** - * @brief Samples buffer pointer. - */ - dacsample_t *samples; - /** - * @brief Samples buffer size. - */ - size_t depth; - /** - * @brief Current configuration data. - */ - const DACConfig *config; -#if (DAC_USE_WAIT == TRUE) || defined(__DOXYGEN__) - /** - * @brief Waiting thread. - */ - thread_reference_t thread; -#endif /* DAC_USE_WAIT */ -#if (DAC_USE_MUTUAL_EXCLUSION == TRUE) || defined(__DOXYGEN__) - /** - * @brief Mutex protecting the bus. - */ - mutex_t mutex; -#endif /* DAC_USE_MUTUAL_EXCLUSION */ -#if defined(DAC_DRIVER_EXT_FIELDS) - DAC_DRIVER_EXT_FIELDS -#endif - /* End of the mandatory fields.*/ - dac_lld_driver_fields; -}; - -/*===========================================================================*/ -/* Driver macros. */ -/*===========================================================================*/ - -/** - * @name Low level driver helper macros - * @{ - */ -/** - * @brief Buffer state. - * @note This function is meant to be called from the DAC callback only. - * - * @param[in] dacp pointer to the @p DACDriver object - * @return The buffer state. - * @retval false if the driver filled/sent the first half of the - * buffer. - * @retval true if the driver filled/sent the second half of the - * buffer. - * - * @special - */ -#define dacIsBufferComplete(dacp) ((bool)((dacp)->state == DAC_COMPLETE)) - -#if (DAC_USE_WAIT == TRUE) || defined(__DOXYGEN__) -/** - * @brief Waits for operation completion. - * @details This function waits for the driver to complete the current - * operation. - * @pre An operation must be running while the function is invoked. - * @note No more than one thread can wait on a DAC driver using - * this function. - * - * @param[in] dacp pointer to the @p DACDriver object - * - * @notapi - */ -#define _dac_wait_s(dacp) osalThreadSuspendS(&(dacp)->thread) - -/** - * @brief Resumes a thread waiting for a conversion completion. - * - * @param[in] dacp pointer to the @p DACDriver object - * - * @notapi - */ -#define _dac_reset_i(dacp) osalThreadResumeI(&(dacp)->thread, MSG_RESET) - -/** - * @brief Resumes a thread waiting for a conversion completion. - * - * @param[in] dacp pointer to the @p DACDriver object - * - * @notapi - */ -#define _dac_reset_s(dacp) osalThreadResumeS(&(dacp)->thread, MSG_RESET) - -/** - * @brief Wakes up the waiting thread. - * - * @param[in] dacp pointer to the @p DACDriver object - * - * @notapi - */ -#define _dac_wakeup_isr(dacp) { \ - osalSysLockFromISR(); \ - osalThreadResumeI(&(dacp)->thread, MSG_OK); \ - osalSysUnlockFromISR(); \ -} - -/** - * @brief Wakes up the waiting thread with a timeout message. - * - * @param[in] dacp pointer to the @p DACDriver object - * - * @notapi - */ -#define _dac_timeout_isr(dacp) { \ - osalSysLockFromISR(); \ - osalThreadResumeI(&(dacp)->thread, MSG_TIMEOUT); \ - osalSysUnlockFromISR(); \ -} - -#else /* !DAC_USE_WAIT */ -#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 */ - -/** - * @brief Common ISR code, half buffer event. - * @details This code handles the portable part of the ISR code: - * - Callback invocation. - * . - * @note This macro is meant to be used in the low level drivers - * implementation only. - * - * @param[in] dacp pointer to the @p DACDriver object - * - * @notapi - */ -#define _dac_isr_half_code(dacp) { \ - if ((dacp)->grpp->end_cb != NULL) { \ - (dacp)->grpp->end_cb(dacp); \ - } \ -} - -/** - * @brief Common ISR code, full buffer event. - * @details This code handles the portable part of the ISR code: - * - Callback invocation. - * - Driver state transitions. - * . - * @note This macro is meant to be used in the low level drivers - * implementation only. - * - * @param[in] dacp pointer to the @p DACDriver object - * - * @notapi - */ -#define _dac_isr_full_code(dacp) { \ - if ((dacp)->grpp->end_cb) { \ - (dacp)->state = DAC_COMPLETE; \ - (dacp)->grpp->end_cb(dacp); \ - if ((dacp)->state == DAC_COMPLETE) \ - (dacp)->state = DAC_ACTIVE; \ - } \ -} - -/** - * @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. - * - Driver state transitions. - * . - * @note This macro is meant to be used in the low level drivers - * implementation only. - * - * @param[in] dacp pointer to the @p DACDriver object - * @param[in] err platform dependent error code - * - * @notapi - */ -#define _dac_isr_error_code(dacp, err) { \ - dac_lld_stop_conversion(dacp); \ - if ((dacp)->grpp->error_cb != NULL) { \ - (dacp)->state = DAC_ERROR; \ - (dacp)->grpp->error_cb(dacp, err); \ - if ((dacp)->state == DAC_ERROR) \ - (dacp)->state = DAC_READY; \ - } \ - (dacp)->grpp = NULL; \ - _dac_timeout_isr(dacp); \ -} -/** @} */ - -/*===========================================================================*/ -/* External declarations. */ -/*===========================================================================*/ - -#ifdef __cplusplus -extern "C" { -#endif - void dacInit(void); - void dacObjectInit(DACDriver *dacp); - msg_t dacStart(DACDriver *dacp, const DACConfig *config); - void dacStop(DACDriver *dacp); - void dacPutChannelX(DACDriver *dacp, - dacchannel_t channel, - dacsample_t sample); - void dacStartConversion(DACDriver *dacp, const DACConversionGroup *grpp, - dacsample_t *samples, size_t depth); - void dacStartConversionI(DACDriver *dacp, const DACConversionGroup *grpp, - dacsample_t *samples, size_t depth); - void dacStopConversion(DACDriver *dacp); - void dacStopConversionI(DACDriver *dacp); -#if DAC_USE_WAIT - msg_t dacConvert(DACDriver *dacp, const DACConversionGroup *grpp, - dacsample_t *samples, size_t depth); -#endif -#if DAC_USE_MUTUAL_EXCLUSION - void dacAcquireBus(DACDriver *dacp); - void dacReleaseBus(DACDriver *dacp); -#endif -#ifdef __cplusplus -} -#endif - -#endif /* HAL_USE_DAC == TRUE */ - -#endif /* HAL_DAC_H */ - -/** @} */ +/* + ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file hal_dac.h + * @brief DAC Driver macros and structures. + * + * @addtogroup DAC + * @{ + */ + +#ifndef HAL_DAC_H +#define HAL_DAC_H + +#if (HAL_USE_DAC == TRUE) || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver constants. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver pre-compile time settings. */ +/*===========================================================================*/ + +/** + * @name DAC configuration options + * @{ + */ +/** + * @brief Support for thread synchronization API. + */ +#if !defined(DAC_USE_SYNCHRONIZATION) || defined(__DOXYGEN__) +#if !defined(DAC_USE_WAIT) || defined(__DOXYGEN__) +#define DAC_USE_SYNCHRONIZATION FALSE +#else +#define DAC_USE_SYNCHRONIZATION DAC_USE_WAIT +#endif +#endif + +/** + * @brief Enables the @p dacAcquireBus() and @p dacReleaseBus() APIs. + * @note Disabling this option saves both code and data space. + */ +#if !defined(DAC_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__) +#define DAC_USE_MUTUAL_EXCLUSION TRUE +#endif +/** @} */ + +/*===========================================================================*/ +/* Derived constants and error checks. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver data structures and types. */ +/*===========================================================================*/ + +/** + * @brief Driver state machine possible states. + */ +typedef enum { + DAC_UNINIT = 0, /**< Not initialized. */ + DAC_STOP = 1, /**< Stopped. */ + DAC_READY = 2, /**< Ready. */ + DAC_ACTIVE = 3, /**< Exchanging data. */ + DAC_COMPLETE = 4, /**< Asynchronous operation complete. */ + DAC_ERROR = 5 /**< Error. */ +} dacstate_t; + +/** + * @brief Type of a structure representing an DAC driver. + */ +typedef struct hal_dac_driver DACDriver; + +/** + * @brief Type of a structure representing an DAC driver configuration. + */ +typedef struct hal_dac_config DACConfig; + +/** + * @brief Type of a DAC conversion group. + */ +typedef struct hal_dac_conversion_group DACConversionGroup; + +/* Including the low level driver header, it exports information required + for completing types.*/ +#include "hal_dac_lld.h" + +/** + * @brief DAC notification callback type. + * + * @param[in] dacp pointer to the @p DACDriver object triggering the + */ +typedef void (*daccallback_t)(DACDriver *dacp); + +/** + * @brief DAC error callback type. + * + * @param[in] dacp pointer to the @p DACDriver object triggering the + * callback + * @param[in] err DAC error code + */ +typedef void (*dacerrorcallback_t)(DACDriver *dacp, dacerror_t err); + +/** + * @brief DAC Conversion group structure. + */ +struct hal_dac_conversion_group { + /** + * @brief Number of DAC channels. + */ + uint32_t num_channels; + /** + * @brief Operation complete callback or @p NULL. + */ + daccallback_t end_cb; + /** + * @brief Error handling callback or @p NULL. + */ + dacerrorcallback_t error_cb; + /* End of the mandatory fields.*/ + dac_lld_conversion_group_fields; +}; + +/** + * @brief Driver configuration structure. + */ +struct hal_dac_config { + /* End of the mandatory fields.*/ + dac_lld_config_fields; +}; + +/** + * @brief Structure representing a DAC driver. + */ +struct hal_dac_driver { + /** + * @brief Driver state. + */ + dacstate_t state; + /** + * @brief Conversion group. + */ + const DACConversionGroup *grpp; + /** + * @brief Samples buffer pointer. + */ + dacsample_t *samples; + /** + * @brief Samples buffer size. + */ + size_t depth; + /** + * @brief Current configuration data. + */ + const DACConfig *config; +#if (DAC_USE_SYNCHRONIZATION == TRUE) || defined(__DOXYGEN__) + /** + * @brief Waiting thread. + */ + thread_reference_t thread; +#endif /* DAC_USE_SYNCHRONIZATION */ +#if (DAC_USE_MUTUAL_EXCLUSION == TRUE) || defined(__DOXYGEN__) + /** + * @brief Mutex protecting the bus. + */ + mutex_t mutex; +#endif /* DAC_USE_MUTUAL_EXCLUSION */ +#if defined(DAC_DRIVER_EXT_FIELDS) + DAC_DRIVER_EXT_FIELDS +#endif + /* End of the mandatory fields.*/ + dac_lld_driver_fields; +}; + +/*===========================================================================*/ +/* Driver macros. */ +/*===========================================================================*/ + +/** + * @name Low level driver helper macros + * @{ + */ +/** + * @brief Buffer state. + * @note This function is meant to be called from the DAC callback only. + * + * @param[in] dacp pointer to the @p DACDriver object + * @return The buffer state. + * @retval false if the driver filled/sent the first half of the + * buffer. + * @retval true if the driver filled/sent the second half of the + * buffer. + * + * @special + */ +#define dacIsBufferComplete(dacp) ((bool)((dacp)->state == DAC_COMPLETE)) + +#if (DAC_USE_SYNCHRONIZATION == TRUE) || defined(__DOXYGEN__) +/** + * @brief Waits for operation completion. + * @details This function waits for the driver to complete the current + * operation. + * @pre An operation must be running while the function is invoked. + * @note No more than one thread can wait on a DAC driver using + * this function. + * + * @param[in] dacp pointer to the @p DACDriver object + * + * @notapi + */ +#define _dac_wait_s(dacp) osalThreadSuspendS(&(dacp)->thread) + +/** + * @brief Resumes a thread waiting for a conversion completion. + * + * @param[in] dacp pointer to the @p DACDriver object + * + * @notapi + */ +#define _dac_reset_i(dacp) osalThreadResumeI(&(dacp)->thread, MSG_RESET) + +/** + * @brief Resumes a thread waiting for a conversion completion. + * + * @param[in] dacp pointer to the @p DACDriver object + * + * @notapi + */ +#define _dac_reset_s(dacp) osalThreadResumeS(&(dacp)->thread, MSG_RESET) + +/** + * @brief Wakes up the waiting thread. + * + * @param[in] dacp pointer to the @p DACDriver object + * + * @notapi + */ +#define _dac_wakeup_isr(dacp) { \ + osalSysLockFromISR(); \ + osalThreadResumeI(&(dacp)->thread, MSG_OK); \ + osalSysUnlockFromISR(); \ +} + +/** + * @brief Wakes up the waiting thread with a timeout message. + * + * @param[in] dacp pointer to the @p DACDriver object + * + * @notapi + */ +#define _dac_timeout_isr(dacp) { \ + osalSysLockFromISR(); \ + osalThreadResumeI(&(dacp)->thread, MSG_TIMEOUT); \ + osalSysUnlockFromISR(); \ +} + +#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_SYNCHRONIZATION */ + +/** + * @brief Common ISR code, half buffer event. + * @details This code handles the portable part of the ISR code: + * - Callback invocation. + * . + * @note This macro is meant to be used in the low level drivers + * implementation only. + * + * @param[in] dacp pointer to the @p DACDriver object + * + * @notapi + */ +#define _dac_isr_half_code(dacp) { \ + if ((dacp)->grpp->end_cb != NULL) { \ + (dacp)->grpp->end_cb(dacp); \ + } \ +} + +/** + * @brief Common ISR code, full buffer event. + * @details This code handles the portable part of the ISR code: + * - Callback invocation. + * - Driver state transitions. + * . + * @note This macro is meant to be used in the low level drivers + * implementation only. + * + * @param[in] dacp pointer to the @p DACDriver object + * + * @notapi + */ +#define _dac_isr_full_code(dacp) { \ + if ((dacp)->grpp->end_cb) { \ + (dacp)->state = DAC_COMPLETE; \ + (dacp)->grpp->end_cb(dacp); \ + 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 signalling, if any. + * - Driver state transitions. + * . + * @note This macro is meant to be used in the low level drivers + * implementation only. + * + * @param[in] dacp pointer to the @p DACDriver object + * @param[in] err platform dependent error code + * + * @notapi + */ +#define _dac_isr_error_code(dacp, err) { \ + dac_lld_stop_conversion(dacp); \ + if ((dacp)->grpp->error_cb != NULL) { \ + (dacp)->state = DAC_ERROR; \ + (dacp)->grpp->error_cb(dacp, err); \ + if ((dacp)->state == DAC_ERROR) \ + (dacp)->state = DAC_READY; \ + } \ + (dacp)->grpp = NULL; \ + _dac_timeout_isr(dacp); \ +} +/** @} */ + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +#ifdef __cplusplus +extern "C" { +#endif + void dacInit(void); + void dacObjectInit(DACDriver *dacp); + msg_t dacStart(DACDriver *dacp, const DACConfig *config); + void dacStop(DACDriver *dacp); + void dacPutChannelX(DACDriver *dacp, + dacchannel_t channel, + dacsample_t sample); + void dacStartConversion(DACDriver *dacp, const DACConversionGroup *grpp, + dacsample_t *samples, size_t depth); + void dacStartConversionI(DACDriver *dacp, const DACConversionGroup *grpp, + dacsample_t *samples, size_t depth); + void dacStopConversion(DACDriver *dacp); + void dacStopConversionI(DACDriver *dacp); +#if DAC_USE_SYNCHRONIZATION + msg_t dacConvert(DACDriver *dacp, const DACConversionGroup *grpp, + dacsample_t *samples, size_t depth); + 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); +#endif +#ifdef __cplusplus +} +#endif + +#endif /* HAL_USE_DAC == TRUE */ + +#endif /* HAL_DAC_H */ + +/** @} */ diff --git a/os/hal/ports/STM32/LLD/DACv1/hal_dac_lld.c b/os/hal/ports/STM32/LLD/DACv1/hal_dac_lld.c index 3b5fc9560..730ff7615 100644 --- a/os/hal/ports/STM32/LLD/DACv1/hal_dac_lld.c +++ b/os/hal/ports/STM32/LLD/DACv1/hal_dac_lld.c @@ -1,801 +1,830 @@ -/* - ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -/** - * @file DACv1/hal_dac_lld.c - * @brief STM32 DAC subsystem low level driver source. - * - * @addtogroup DAC - * @{ - */ - -#include "hal.h" - -#if HAL_USE_DAC || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Driver local definitions. */ -/*===========================================================================*/ - -/* Because ST headers naming inconsistencies.*/ -#if !defined(DAC1) -#define DAC1 DAC -#endif - -#define DAC1_CH1_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_DAC_DAC1_CH1_DMA_STREAM, \ - STM32_DAC1_CH1_DMA_CHN) - -#define DAC1_CH2_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_DAC_DAC1_CH2_DMA_STREAM, \ - STM32_DAC1_CH2_DMA_CHN) - -#define DAC2_CH1_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_DAC_DAC2_CH1_DMA_STREAM, \ - STM32_DAC2_CH1_DMA_CHN) - -#define DAC2_CH2_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_DAC_DAC2_CH2_DMA_STREAM, \ - STM32_DAC2_CH2_DMA_CHN) - -#define DAC3_CH1_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_DAC_DAC3_CH1_DMA_STREAM, \ - STM32_DAC3_CH1_DMA_CHN) - -#define DAC3_CH2_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_DAC_DAC3_CH2_DMA_STREAM, \ - STM32_DAC3_CH2_DMA_CHN) - -#define DAC4_CH1_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_DAC_DAC4_CH1_DMA_STREAM, \ - STM32_DAC4_CH1_DMA_CHN) - -#define DAC4_CH2_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_DAC_DAC4_CH2_DMA_STREAM, \ - STM32_DAC4_CH2_DMA_CHN) - -#define CHANNEL_DATA_OFFSET 3U - -/*===========================================================================*/ -/* Driver exported variables. */ -/*===========================================================================*/ - -/** @brief DAC1 CH1 driver identifier.*/ -#if STM32_DAC_USE_DAC1_CH1 || defined(__DOXYGEN__) -DACDriver DACD1; -#endif - -/** @brief DAC1 CH2 driver identifier.*/ -#if (STM32_DAC_USE_DAC1_CH2 && !STM32_DAC_DUAL_MODE) || defined(__DOXYGEN__) -DACDriver DACD2; -#endif - -/** @brief DAC2 CH1 driver identifier.*/ -#if STM32_DAC_USE_DAC2_CH1 || defined(__DOXYGEN__) -DACDriver DACD3; -#endif - -/** @brief DAC2 CH2 driver identifier.*/ -#if (STM32_DAC_USE_DAC2_CH2 && !STM32_DAC_DUAL_MODE) || defined(__DOXYGEN__) -DACDriver DACD4; -#endif - -/** @brief DAC3 CH1 driver identifier.*/ -#if STM32_DAC_USE_DAC3_CH1 || defined(__DOXYGEN__) -DACDriver DACD5; -#endif - -/** @brief DAC3 CH2 driver identifier.*/ -#if (STM32_DAC_USE_DAC3_CH2 && !STM32_DAC_DUAL_MODE) || defined(__DOXYGEN__) -DACDriver DACD6; -#endif - -/** @brief DAC4 CH1 driver identifier.*/ -#if STM32_DAC_USE_DAC4_CH1 || defined(__DOXYGEN__) -DACDriver DACD7; -#endif - -/** @brief DAC4 CH2 driver identifier.*/ -#if (STM32_DAC_USE_DAC4_CH2 && !STM32_DAC_DUAL_MODE) || defined(__DOXYGEN__) -DACDriver DACD8; -#endif - -/*===========================================================================*/ -/* Driver local variables. */ -/*===========================================================================*/ - -#if STM32_DAC_USE_DAC1_CH1 == TRUE -static const dacparams_t dac1_ch1_params = { - .dac = DAC1, - .dataoffset = 0U, - .regshift = 0U, - .regmask = 0xFFFF0000U, - .dmastream = STM32_DAC_DAC1_CH1_DMA_STREAM, -#if STM32_DMA_SUPPORTS_DMAMUX - .peripheral = STM32_DMAMUX1_DAC1_CH1, -#endif - .dmamode = STM32_DMA_CR_CHSEL(DAC1_CH1_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_DAC_DAC1_CH1_DMA_PRIORITY) | - STM32_DMA_CR_MINC | STM32_DMA_CR_CIRC | STM32_DMA_CR_DIR_M2P | - STM32_DMA_CR_DMEIE | STM32_DMA_CR_TEIE | STM32_DMA_CR_HTIE | - STM32_DMA_CR_TCIE, - .dmairqprio = STM32_DAC_DAC1_CH1_IRQ_PRIORITY -}; -#endif - -#if STM32_DAC_USE_DAC1_CH2 == TRUE -static const dacparams_t dac1_ch2_params = { - .dac = DAC1, - .dataoffset = CHANNEL_DATA_OFFSET, - .regshift = 16U, - .regmask = 0x0000FFFFU, - .dmastream = STM32_DAC_DAC1_CH2_DMA_STREAM, -#if STM32_DMA_SUPPORTS_DMAMUX - .peripheral = STM32_DMAMUX1_DAC1_CH2, -#endif - .dmamode = STM32_DMA_CR_CHSEL(DAC1_CH2_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_DAC_DAC1_CH2_DMA_PRIORITY) | - STM32_DMA_CR_MINC | STM32_DMA_CR_CIRC | STM32_DMA_CR_DIR_M2P | - STM32_DMA_CR_DMEIE | STM32_DMA_CR_TEIE | STM32_DMA_CR_HTIE | - STM32_DMA_CR_TCIE, - .dmairqprio = STM32_DAC_DAC1_CH2_IRQ_PRIORITY -}; -#endif - -#if STM32_DAC_USE_DAC2_CH1 == TRUE -static const dacparams_t dac2_ch1_params = { - .dac = DAC2, - .dataoffset = 0U, - .regshift = 0U, - .regmask = 0xFFFF0000U, - .dmastream = STM32_DAC_DAC2_CH1_DMA_STREAM, -#if STM32_DMA_SUPPORTS_DMAMUX - .peripheral = STM32_DMAMUX1_DAC2_CH1, -#endif - .dmamode = STM32_DMA_CR_CHSEL(DAC2_CH1_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_DAC_DAC2_CH1_DMA_PRIORITY) | - STM32_DMA_CR_MINC | STM32_DMA_CR_CIRC | STM32_DMA_CR_DIR_M2P | - STM32_DMA_CR_DMEIE | STM32_DMA_CR_TEIE | STM32_DMA_CR_HTIE | - STM32_DMA_CR_TCIE, - .dmairqprio = STM32_DAC_DAC2_CH1_IRQ_PRIORITY -}; -#endif - -#if STM32_DAC_USE_DAC2_CH2 == TRUE -static const dacparams_t dac2_ch2_params = { - .dac = DAC2, - .dataoffset = CHANNEL_DATA_OFFSET, - .regshift = 16U, - .regmask = 0x0000FFFFU, - .dmastream = STM32_DAC_DAC2_CH2_DMA_STREAM, -#if STM32_DMA_SUPPORTS_DMAMUX - .peripheral = STM32_DMAMUX1_DAC2_CH2, -#endif - .dmamode = STM32_DMA_CR_CHSEL(DAC2_CH2_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_DAC_DAC2_CH2_DMA_PRIORITY) | - STM32_DMA_CR_MINC | STM32_DMA_CR_CIRC | STM32_DMA_CR_DIR_M2P | - STM32_DMA_CR_DMEIE | STM32_DMA_CR_TEIE | STM32_DMA_CR_HTIE | - STM32_DMA_CR_TCIE, - .dmairqprio = STM32_DAC_DAC2_CH2_IRQ_PRIORITY -}; -#endif - -#if STM32_DAC_USE_DAC3_CH1 == TRUE -static const dacparams_t dac3_ch1_params = { - .dac = DAC3, - .dataoffset = 0U, - .regshift = 0U, - .regmask = 0xFFFF0000U, - .dmastream = STM32_DAC_DAC3_CH1_DMA_STREAM, -#if STM32_DMA_SUPPORTS_DMAMUX - .peripheral = STM32_DMAMUX1_DAC3_CH1, -#endif - .dmamode = STM32_DMA_CR_CHSEL(DAC3_CH1_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_DAC_DAC3_CH1_DMA_PRIORITY) | - STM32_DMA_CR_MINC | STM32_DMA_CR_CIRC | STM32_DMA_CR_DIR_M2P | - STM32_DMA_CR_DMEIE | STM32_DMA_CR_TEIE | STM32_DMA_CR_HTIE | - STM32_DMA_CR_TCIE, - .dmairqprio = STM32_DAC_DAC3_CH1_IRQ_PRIORITY -}; -#endif - -#if STM32_DAC_USE_DAC3_CH2 == TRUE -static const dacparams_t dac3_ch2_params = { - .dac = DAC3, - .dataoffset = CHANNEL_DATA_OFFSET, - .regshift = 16U, - .regmask = 0x0000FFFFU, - .dmastream = STM32_DAC_DAC3_CH2_DMA_STREAM, -#if STM32_DMA_SUPPORTS_DMAMUX - .peripheral = STM32_DMAMUX1_DAC3_CH2, -#endif - .dmamode = STM32_DMA_CR_CHSEL(DAC3_CH2_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_DAC_DAC3_CH2_DMA_PRIORITY) | - STM32_DMA_CR_MINC | STM32_DMA_CR_CIRC | STM32_DMA_CR_DIR_M2P | - STM32_DMA_CR_DMEIE | STM32_DMA_CR_TEIE | STM32_DMA_CR_HTIE | - STM32_DMA_CR_TCIE, - .dmairqprio = STM32_DAC_DAC3_CH2_IRQ_PRIORITY -}; -#endif - -#if STM32_DAC_USE_DAC4_CH1 == TRUE -static const dacparams_t dac4_ch1_params = { - .dac = DAC4, - .dataoffset = 0U, - .regshift = 0U, - .regmask = 0xFFFF0000U, - .dmastream = STM32_DAC_DAC4_CH1_DMA_STREAM, -#if STM32_DMA_SUPPORTS_DMAMUX - .peripheral = STM32_DMAMUX1_DAC4_CH1, -#endif - .dmamode = STM32_DMA_CR_CHSEL(DAC4_CH1_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_DAC_DAC4_CH1_DMA_PRIORITY) | - STM32_DMA_CR_MINC | STM32_DMA_CR_CIRC | STM32_DMA_CR_DIR_M2P | - STM32_DMA_CR_DMEIE | STM32_DMA_CR_TEIE | STM32_DMA_CR_HTIE | - STM32_DMA_CR_TCIE, - .dmairqprio = STM32_DAC_DAC4_CH1_IRQ_PRIORITY -}; -#endif - -#if STM32_DAC_USE_DAC4_CH2 == TRUE -static const dacparams_t dac4_ch2_params = { - .dac = DAC4, - .dataoffset = CHANNEL_DATA_OFFSET, - .regshift = 16U, - .regmask = 0x0000FFFFU, - .dmastream = STM32_DAC_DAC4_CH2_DMA_STREAM, -#if STM32_DMA_SUPPORTS_DMAMUX - .peripheral = STM32_DMAMUX1_DAC4_CH2, -#endif - .dmamode = STM32_DMA_CR_CHSEL(DAC4_CH2_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_DAC_DAC4_CH2_DMA_PRIORITY) | - STM32_DMA_CR_MINC | STM32_DMA_CR_CIRC | STM32_DMA_CR_DIR_M2P | - STM32_DMA_CR_DMEIE | STM32_DMA_CR_TEIE | STM32_DMA_CR_HTIE | - STM32_DMA_CR_TCIE, - .dmairqprio = STM32_DAC_DAC4_CH2_IRQ_PRIORITY -}; -#endif - -/*===========================================================================*/ -/* Driver local functions. */ -/*===========================================================================*/ - -/** - * @brief Shared end/half-of-tx service routine. - * - * @param[in] dacp pointer to the @p DACDriver object - * @param[in] flags pre-shifted content of the ISR register - */ -static void dac_lld_serve_tx_interrupt(DACDriver *dacp, uint32_t flags) { - - if ((flags & (STM32_DMA_ISR_TEIF | STM32_DMA_ISR_DMEIF)) != 0) { - /* DMA errors handling.*/ - dac_lld_stop_conversion(dacp); - _dac_isr_error_code(dacp, DAC_ERR_DMAFAILURE); - } - else { - if ((flags & STM32_DMA_ISR_HTIF) != 0) { - /* Half transfer processing.*/ - _dac_isr_half_code(dacp); - } - if ((flags & STM32_DMA_ISR_TCIF) != 0) { - /* Transfer complete processing.*/ - _dac_isr_full_code(dacp); - } - } -} - -/*===========================================================================*/ -/* Driver interrupt handlers. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver exported functions. */ -/*===========================================================================*/ - -/** - * @brief Low level DAC driver initialization. - * - * @notapi - */ -void dac_lld_init(void) { - -#if STM32_DAC_USE_DAC1_CH1 - dacObjectInit(&DACD1); - DACD1.params = &dac1_ch1_params; - DACD1.dma = NULL; -#endif - -#if STM32_DAC_USE_DAC1_CH2 - dacObjectInit(&DACD2); - DACD2.params = &dac1_ch2_params; - DACD2.dma = NULL; -#endif - -#if STM32_DAC_USE_DAC2_CH1 - dacObjectInit(&DACD3); - DACD3.params = &dac2_ch1_params; - DACD3.dma = NULL; -#endif - -#if STM32_DAC_USE_DAC2_CH2 - dacObjectInit(&DACD4); - DACD4.params = &dac2_ch2_params; - DACD4.dma = NULL; -#endif - -#if STM32_DAC_USE_DAC3_CH1 - dacObjectInit(&DACD5); - DACD5.params = &dac3_ch1_params; - DACD5.dma = NULL; -#endif - -#if STM32_DAC_USE_DAC3_CH2 - dacObjectInit(&DACD6); - DACD6.params = &dac3_ch2_params; - DACD6.dma = NULL; -#endif - -#if STM32_DAC_USE_DAC4_CH1 - dacObjectInit(&DACD7); - DACD7.params = &dac4_ch1_params; - DACD7.dma = NULL; -#endif - -#if STM32_DAC_USE_DAC4_CH2 - dacObjectInit(&DACD8); - DACD8.params = &dac4_ch2_params; - DACD8.dma = NULL; -#endif -} - -/** - * @brief Configures and activates the DAC peripheral. - * - * @param[in] dacp pointer to the @p DACDriver object - * - * @notapi - */ -void dac_lld_start(DACDriver *dacp) { - - /* If the driver is in DAC_STOP state then a full initialization is - required.*/ - if (dacp->state == DAC_STOP) { - dacchannel_t channel = 0; - - /* Enabling the clock source.*/ -#if STM32_DAC_USE_DAC1_CH1 - if (&DACD1 == dacp) { - rccEnableDAC1(true); - } -#endif - -#if STM32_DAC_USE_DAC1_CH2 - if (&DACD2 == dacp) { - rccEnableDAC1(true); - channel = 1; - } -#endif - -#if STM32_DAC_USE_DAC2_CH1 - if (&DACD3 == dacp) { - rccEnableDAC2(true); - } -#endif - -#if STM32_DAC_USE_DAC2_CH2 - if (&DACD4 == dacp) { - rccEnableDAC2(true); - channel = 1; - } -#endif - -#if STM32_DAC_USE_DAC3_CH1 - if (&DACD5 == dacp) { - rccEnableDAC3(true); - } -#endif - -#if STM32_DAC_USE_DAC3_CH2 - if (&DACD6 == dacp) { - rccEnableDAC3(true); - channel = 1; - } -#endif - -#if STM32_DAC_USE_DAC4_CH1 - if (&DACD7 == dacp) { - rccEnableDAC4(true); - } -#endif - -#if STM32_DAC_USE_DAC4_CH2 - if (&DACD8 == dacp) { - rccEnableDAC4(true); - channel = 1; - } -#endif - - /* Enabling DAC in SW triggering mode initially, initializing data to - zero.*/ -#if STM32_DAC_DUAL_MODE == FALSE - { - uint32_t cr; - - 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); - } -#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 - } -} - -/** - * @brief Deactivates the DAC peripheral. - * - * @param[in] dacp pointer to the @p DACDriver object - * - * @notapi - */ -void dac_lld_stop(DACDriver *dacp) { - - /* If in ready state then disables the DAC clock.*/ - if (dacp->state == DAC_READY) { - - /* Disabling DAC.*/ - dacp->params->dac->CR &= dacp->params->regmask; - -#if STM32_DAC_USE_DAC1_CH1 - if (&DACD1 == dacp) { -#if defined(DAC_CR_EN2) - if ((dacp->params->dac->CR & DAC_CR_EN2) == 0U) { - rccDisableDAC1(); - } -#else - rccDisableDAC1(); -#endif - } -#endif - -#if STM32_DAC_USE_DAC1_CH2 - if (&DACD2 == dacp) { - if ((dacp->params->dac->CR & DAC_CR_EN1) == 0U) { - rccDisableDAC1(); - } - } -#endif - -#if STM32_DAC_USE_DAC2_CH1 - if (&DACD3 == dacp) { -#if defined(DAC_CR_EN2) - if ((dacp->params->dac->CR & DAC_CR_EN2) == 0U) { - rccDisableDAC2(); - } -#else - rccDisableDAC2(); -#endif - } -#endif - -#if STM32_DAC_USE_DAC2_CH2 - if (&DACD4 == dacp) { - if ((dacp->params->dac->CR & DAC_CR_EN1) == 0U) { - rccDisableDAC2(); - } - } -#endif - -#if STM32_DAC_USE_DAC3_CH1 - if (&DACD5 == dacp) { -#if defined(DAC_CR_EN2) - if ((dacp->params->dac->CR & DAC_CR_EN2) == 0U) { - rccDisableDAC3(); - } -#else - rccDisableDAC3(); -#endif - } -#endif - -#if STM32_DAC_USE_DAC3_CH2 - if (&DACD6 == dacp) { - if ((dacp->params->dac->CR & DAC_CR_EN1) == 0U) { - rccDisableDAC3(); - } - } -#endif - -#if STM32_DAC_USE_DAC4_CH1 - if (&DACD7 == dacp) { -#if defined(DAC_CR_EN2) - if ((dacp->params->dac->CR & DAC_CR_EN2) == 0U) { - rccDisableDAC4(); - } -#else - rccDisableDAC4(); -#endif - } -#endif - -#if STM32_DAC_USE_DAC4_CH2 - if (&DACD8 == dacp) { - if ((dacp->params->dac->CR & DAC_CR_EN1) == 0U) { - rccDisableDAC4(); - } - } -#endif - } -} - -/** - * @brief Outputs a value directly on a DAC channel. - * - * @param[in] dacp pointer to the @p DACDriver object - * @param[in] channel DAC channel number - * @param[in] sample value to be output - * - * @api - */ -void dac_lld_put_channel(DACDriver *dacp, - dacchannel_t channel, - dacsample_t sample) { - - switch (dacp->config->datamode) { - case DAC_DHRM_12BIT_RIGHT: -#if STM32_DAC_DUAL_MODE - case DAC_DHRM_12BIT_RIGHT_DUAL: -#endif - if (channel == 0U) { -#if STM32_DAC_DUAL_MODE - dacp->params->dac->DHR12R1 = (uint32_t)sample; -#else - *(&dacp->params->dac->DHR12R1 + dacp->params->dataoffset) = (uint32_t)sample; -#endif - } -#if (STM32_HAS_DAC1_CH2 || STM32_HAS_DAC2_CH2 || \ - STM32_HAS_DAC3_CH2 || STM32_HAS_DAC4_CH2) - else { - dacp->params->dac->DHR12R2 = (uint32_t)sample; - } -#endif - break; - case DAC_DHRM_12BIT_LEFT: -#if STM32_DAC_DUAL_MODE - case DAC_DHRM_12BIT_LEFT_DUAL: -#endif - if (channel == 0U) { -#if STM32_DAC_DUAL_MODE - dacp->params->dac->DHR12L1 = (uint32_t)sample; -#else - *(&dacp->params->dac->DHR12L1 + dacp->params->dataoffset) = (uint32_t)sample; -#endif - } -#if (STM32_HAS_DAC1_CH2 || STM32_HAS_DAC2_CH2 || \ - STM32_HAS_DAC3_CH2 || STM32_HAS_DAC4_CH2) - else { - dacp->params->dac->DHR12L2 = (uint32_t)sample; - } -#endif - break; - case DAC_DHRM_8BIT_RIGHT: -#if STM32_DAC_DUAL_MODE - case DAC_DHRM_8BIT_RIGHT_DUAL: -#endif - if (channel == 0U) { -#if STM32_DAC_DUAL_MODE - dacp->params->dac->DHR8R1 = (uint32_t)sample; -#else - *(&dacp->params->dac->DHR8R1 + dacp->params->dataoffset) = (uint32_t)sample; -#endif - } -#if (STM32_HAS_DAC1_CH2 || STM32_HAS_DAC2_CH2 || \ - STM32_HAS_DAC3_CH2 || STM32_HAS_DAC4_CH2) - else { - dacp->params->dac->DHR8R2 = (uint32_t)sample; - } -#endif - break; - default: - osalDbgAssert(false, "unexpected DAC mode"); - break; - } -} - -/** - * @brief Starts a DAC conversion. - * @details Starts an asynchronous conversion operation. - * @note In @p DAC_DHRM_8BIT_RIGHT mode the parameters passed to the - * callback are wrong because two samples are packed in a single - * dacsample_t element. This will not be corrected, do not rely - * on those parameters. - * @note In @p DAC_DHRM_8BIT_RIGHT_DUAL mode two samples are treated - * 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. - * - * @param[in] dacp pointer to the @p DACDriver object - * - * @notapi - */ -void dac_lld_start_conversion(DACDriver *dacp) { - uint32_t n, cr, dmamode; - - /* Number of DMA operations per buffer.*/ - n = dacp->depth * dacp->grpp->num_channels; - - /* Allocating the DMA channel.*/ - dacp->dma = dmaStreamAllocI(dacp->params->dmastream, - dacp->params->dmairqprio, - (stm32_dmaisr_t)dac_lld_serve_tx_interrupt, - (void *)dacp); - osalDbgAssert(dacp->dma != NULL, "unable to allocate stream"); -#if STM32_DMA_SUPPORTS_DMAMUX - dmaSetRequestSource(dacp->dma, dacp->params->peripheral); -#endif - - /* DMA settings depend on the chosen DAC mode.*/ - switch (dacp->config->datamode) { - /* Sets the DAC data register */ - case DAC_DHRM_12BIT_RIGHT: - osalDbgAssert(dacp->grpp->num_channels == 1, "invalid number of channels"); - - dmaStreamSetPeripheral(dacp->dma, &dacp->params->dac->DHR12R1 + - dacp->params->dataoffset); - dmamode = dacp->params->dmamode | -#if STM32_DMA_ADVANCED == FALSE - STM32_DMA_CR_PSIZE_WORD | STM32_DMA_CR_MSIZE_HWORD; -#else - STM32_DMA_CR_PSIZE_HWORD | STM32_DMA_CR_MSIZE_HWORD; -#endif - break; - case DAC_DHRM_12BIT_LEFT: - osalDbgAssert(dacp->grpp->num_channels == 1, "invalid number of channels"); - - dmaStreamSetPeripheral(dacp->dma, &dacp->params->dac->DHR12L1 + - dacp->params->dataoffset); - dmamode = dacp->params->dmamode | -#if STM32_DMA_ADVANCED == FALSE - STM32_DMA_CR_PSIZE_WORD | STM32_DMA_CR_MSIZE_HWORD; -#else - STM32_DMA_CR_PSIZE_HWORD | STM32_DMA_CR_MSIZE_HWORD; -#endif - break; - case DAC_DHRM_8BIT_RIGHT: - osalDbgAssert(dacp->grpp->num_channels == 1, "invalid number of channels"); - - dmaStreamSetPeripheral(dacp->dma, &dacp->params->dac->DHR8R1 + - dacp->params->dataoffset); - dmamode = dacp->params->dmamode | -#if STM32_DMA_ADVANCED == FALSE - STM32_DMA_CR_PSIZE_WORD | STM32_DMA_CR_MSIZE_BYTE; -#else - STM32_DMA_CR_MSIZE_BYTE | STM32_DMA_CR_MSIZE_BYTE; -#endif - - /* In this mode the size of the buffer is halved because two samples - packed in a single dacsample_t element.*/ - n = (n + 1) / 2; - break; -#if STM32_DAC_DUAL_MODE == TRUE - case DAC_DHRM_12BIT_RIGHT_DUAL: - osalDbgAssert(dacp->grpp->num_channels == 2, "invalid number of channels"); - - dmaStreamSetPeripheral(dacp->dma, &dacp->params->dac->DHR12RD); - dmamode = dacp->params->dmamode | - STM32_DMA_CR_PSIZE_WORD | STM32_DMA_CR_MSIZE_WORD; - n /= 2; - break; - case DAC_DHRM_12BIT_LEFT_DUAL: - osalDbgAssert(dacp->grpp->num_channels == 2, "invalid number of channels"); - - dmaStreamSetPeripheral(dacp->dma, &dacp->params->dac->DHR12LD); - dmamode = dacp->params->dmamode | - STM32_DMA_CR_PSIZE_WORD | STM32_DMA_CR_MSIZE_WORD; - n /= 2; - break; - case DAC_DHRM_8BIT_RIGHT_DUAL: - osalDbgAssert(dacp->grpp->num_channels == 1, "invalid number of channels"); - - dmaStreamSetPeripheral(dacp->dma, &dacp->params->dac->DHR8RD); - dmamode = dacp->params->dmamode | -#if STM32_DMA_ADVANCED == FALSE - STM32_DMA_CR_PSIZE_WORD | STM32_DMA_CR_MSIZE_HWORD; -#else - STM32_DMA_CR_PSIZE_HWORD | STM32_DMA_CR_MSIZE_HWORD; -#endif - n /= 2; - break; -#endif - default: - osalDbgAssert(false, "unexpected DAC mode"); - return; - } - - dmaStreamSetMemory0(dacp->dma, dacp->samples); - dmaStreamSetTransactionSize(dacp->dma, n); - dmaStreamSetMode(dacp->dma, dmamode | - STM32_DMA_CR_DMEIE | STM32_DMA_CR_TEIE | - STM32_DMA_CR_HTIE | STM32_DMA_CR_TCIE); - dmaStreamEnable(dacp->dma); - - /* DAC configuration.*/ - cr = dacp->params->dac->CR; - -#if STM32_DAC_DUAL_MODE == FALSE - 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; -#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); -#endif - - dacp->params->dac->CR = cr; -} - -/** - * @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. - * - * @param[in] dacp pointer to the @p DACDriver object - * - * @iclass - */ -void dac_lld_stop_conversion(DACDriver *dacp) { - uint32_t cr; - - /* DMA channel disabled and released.*/ - dmaStreamDisable(dacp->dma); - dmaStreamFreeI(dacp->dma); - dacp->dma = NULL; - - cr = dacp->params->dac->CR; - -#if STM32_DAC_DUAL_MODE == FALSE - cr &= dacp->params->regmask; - cr |= (DAC_CR_EN1 | dacp->config->cr) << 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; - } -#endif - - dacp->params->dac->CR = cr; -} - -#endif /* HAL_USE_DAC */ - -/** @} */ +/* + ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file DACv1/hal_dac_lld.c + * @brief STM32 DAC subsystem low level driver source. + * + * @addtogroup DAC + * @{ + */ + +#include "hal.h" + +#if HAL_USE_DAC || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver local definitions. */ +/*===========================================================================*/ + +/* Because ST headers naming inconsistencies.*/ +#if !defined(DAC1) +#define DAC1 DAC +#endif + +#define DAC1_CH1_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_DAC_DAC1_CH1_DMA_STREAM, \ + STM32_DAC1_CH1_DMA_CHN) + +#define DAC1_CH2_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_DAC_DAC1_CH2_DMA_STREAM, \ + STM32_DAC1_CH2_DMA_CHN) + +#define DAC2_CH1_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_DAC_DAC2_CH1_DMA_STREAM, \ + STM32_DAC2_CH1_DMA_CHN) + +#define DAC2_CH2_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_DAC_DAC2_CH2_DMA_STREAM, \ + STM32_DAC2_CH2_DMA_CHN) + +#define DAC3_CH1_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_DAC_DAC3_CH1_DMA_STREAM, \ + STM32_DAC3_CH1_DMA_CHN) + +#define DAC3_CH2_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_DAC_DAC3_CH2_DMA_STREAM, \ + STM32_DAC3_CH2_DMA_CHN) + +#define DAC4_CH1_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_DAC_DAC4_CH1_DMA_STREAM, \ + STM32_DAC4_CH1_DMA_CHN) + +#define DAC4_CH2_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_DAC_DAC4_CH2_DMA_STREAM, \ + 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. */ +/*===========================================================================*/ + +/** @brief DAC1 CH1 driver identifier.*/ +#if STM32_DAC_USE_DAC1_CH1 || defined(__DOXYGEN__) +DACDriver DACD1; +#endif + +/** @brief DAC1 CH2 driver identifier.*/ +#if (STM32_DAC_USE_DAC1_CH2 && !STM32_DAC_DUAL_MODE) || defined(__DOXYGEN__) +DACDriver DACD2; +#endif + +/** @brief DAC2 CH1 driver identifier.*/ +#if STM32_DAC_USE_DAC2_CH1 || defined(__DOXYGEN__) +DACDriver DACD3; +#endif + +/** @brief DAC2 CH2 driver identifier.*/ +#if (STM32_DAC_USE_DAC2_CH2 && !STM32_DAC_DUAL_MODE) || defined(__DOXYGEN__) +DACDriver DACD4; +#endif + +/** @brief DAC3 CH1 driver identifier.*/ +#if STM32_DAC_USE_DAC3_CH1 || defined(__DOXYGEN__) +DACDriver DACD5; +#endif + +/** @brief DAC3 CH2 driver identifier.*/ +#if (STM32_DAC_USE_DAC3_CH2 && !STM32_DAC_DUAL_MODE) || defined(__DOXYGEN__) +DACDriver DACD6; +#endif + +/** @brief DAC4 CH1 driver identifier.*/ +#if STM32_DAC_USE_DAC4_CH1 || defined(__DOXYGEN__) +DACDriver DACD7; +#endif + +/** @brief DAC4 CH2 driver identifier.*/ +#if (STM32_DAC_USE_DAC4_CH2 && !STM32_DAC_DUAL_MODE) || defined(__DOXYGEN__) +DACDriver DACD8; +#endif + +/*===========================================================================*/ +/* Driver local variables. */ +/*===========================================================================*/ + +#if STM32_DAC_USE_DAC1_CH1 == TRUE +static const dacparams_t dac1_ch1_params = { + .dac = DAC1, + .dataoffset = 0U, + .regshift = 0U, + .regmask = CHANNEL_REGISTER_MASK1, + .dmastream = STM32_DAC_DAC1_CH1_DMA_STREAM, +#if STM32_DMA_SUPPORTS_DMAMUX + .peripheral = STM32_DMAMUX1_DAC1_CH1, +#endif + .dmamode = STM32_DMA_CR_CHSEL(DAC1_CH1_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_DAC_DAC1_CH1_DMA_PRIORITY) | + STM32_DMA_CR_MINC | STM32_DMA_CR_CIRC | STM32_DMA_CR_DIR_M2P | + STM32_DMA_CR_DMEIE | STM32_DMA_CR_TEIE | STM32_DMA_CR_HTIE | + STM32_DMA_CR_TCIE, + .dmairqprio = STM32_DAC_DAC1_CH1_IRQ_PRIORITY +}; +#endif + +#if STM32_DAC_USE_DAC1_CH2 == TRUE +static const dacparams_t dac1_ch2_params = { + .dac = DAC1, + .dataoffset = CHANNEL_DATA_OFFSET, + .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, +#endif + .dmamode = STM32_DMA_CR_CHSEL(DAC1_CH2_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_DAC_DAC1_CH2_DMA_PRIORITY) | + STM32_DMA_CR_MINC | STM32_DMA_CR_CIRC | STM32_DMA_CR_DIR_M2P | + STM32_DMA_CR_DMEIE | STM32_DMA_CR_TEIE | STM32_DMA_CR_HTIE | + STM32_DMA_CR_TCIE, + .dmairqprio = STM32_DAC_DAC1_CH2_IRQ_PRIORITY +}; +#endif + +#if STM32_DAC_USE_DAC2_CH1 == TRUE +static const dacparams_t dac2_ch1_params = { + .dac = DAC2, + .dataoffset = 0U, + .regshift = 0U, + .regmask = CHANNEL_REGISTER_MASK1, + .dmastream = STM32_DAC_DAC2_CH1_DMA_STREAM, +#if STM32_DMA_SUPPORTS_DMAMUX + .peripheral = STM32_DMAMUX1_DAC2_CH1, +#endif + .dmamode = STM32_DMA_CR_CHSEL(DAC2_CH1_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_DAC_DAC2_CH1_DMA_PRIORITY) | + STM32_DMA_CR_MINC | STM32_DMA_CR_CIRC | STM32_DMA_CR_DIR_M2P | + STM32_DMA_CR_DMEIE | STM32_DMA_CR_TEIE | STM32_DMA_CR_HTIE | + STM32_DMA_CR_TCIE, + .dmairqprio = STM32_DAC_DAC2_CH1_IRQ_PRIORITY +}; +#endif + +#if STM32_DAC_USE_DAC2_CH2 == TRUE +static const dacparams_t dac2_ch2_params = { + .dac = DAC2, + .dataoffset = CHANNEL_DATA_OFFSET, + .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, +#endif + .dmamode = STM32_DMA_CR_CHSEL(DAC2_CH2_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_DAC_DAC2_CH2_DMA_PRIORITY) | + STM32_DMA_CR_MINC | STM32_DMA_CR_CIRC | STM32_DMA_CR_DIR_M2P | + STM32_DMA_CR_DMEIE | STM32_DMA_CR_TEIE | STM32_DMA_CR_HTIE | + STM32_DMA_CR_TCIE, + .dmairqprio = STM32_DAC_DAC2_CH2_IRQ_PRIORITY +}; +#endif + +#if STM32_DAC_USE_DAC3_CH1 == TRUE +static const dacparams_t dac3_ch1_params = { + .dac = DAC3, + .dataoffset = 0U, + .regshift = 0U, + .regmask = CHANNEL_REGISTER_MASK1, + .dmastream = STM32_DAC_DAC3_CH1_DMA_STREAM, +#if STM32_DMA_SUPPORTS_DMAMUX + .peripheral = STM32_DMAMUX1_DAC3_CH1, +#endif + .dmamode = STM32_DMA_CR_CHSEL(DAC3_CH1_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_DAC_DAC3_CH1_DMA_PRIORITY) | + STM32_DMA_CR_MINC | STM32_DMA_CR_CIRC | STM32_DMA_CR_DIR_M2P | + STM32_DMA_CR_DMEIE | STM32_DMA_CR_TEIE | STM32_DMA_CR_HTIE | + STM32_DMA_CR_TCIE, + .dmairqprio = STM32_DAC_DAC3_CH1_IRQ_PRIORITY +}; +#endif + +#if STM32_DAC_USE_DAC3_CH2 == TRUE +static const dacparams_t dac3_ch2_params = { + .dac = DAC3, + .dataoffset = CHANNEL_DATA_OFFSET, + .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, +#endif + .dmamode = STM32_DMA_CR_CHSEL(DAC3_CH2_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_DAC_DAC3_CH2_DMA_PRIORITY) | + STM32_DMA_CR_MINC | STM32_DMA_CR_CIRC | STM32_DMA_CR_DIR_M2P | + STM32_DMA_CR_DMEIE | STM32_DMA_CR_TEIE | STM32_DMA_CR_HTIE | + STM32_DMA_CR_TCIE, + .dmairqprio = STM32_DAC_DAC3_CH2_IRQ_PRIORITY +}; +#endif + +#if STM32_DAC_USE_DAC4_CH1 == TRUE +static const dacparams_t dac4_ch1_params = { + .dac = DAC4, + .dataoffset = 0U, + .regshift = 0U, + .regmask = CHANNEL_REGISTER_MASK1, + .dmastream = STM32_DAC_DAC4_CH1_DMA_STREAM, +#if STM32_DMA_SUPPORTS_DMAMUX + .peripheral = STM32_DMAMUX1_DAC4_CH1, +#endif + .dmamode = STM32_DMA_CR_CHSEL(DAC4_CH1_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_DAC_DAC4_CH1_DMA_PRIORITY) | + STM32_DMA_CR_MINC | STM32_DMA_CR_CIRC | STM32_DMA_CR_DIR_M2P | + STM32_DMA_CR_DMEIE | STM32_DMA_CR_TEIE | STM32_DMA_CR_HTIE | + STM32_DMA_CR_TCIE, + .dmairqprio = STM32_DAC_DAC4_CH1_IRQ_PRIORITY +}; +#endif + +#if STM32_DAC_USE_DAC4_CH2 == TRUE +static const dacparams_t dac4_ch2_params = { + .dac = DAC4, + .dataoffset = CHANNEL_DATA_OFFSET, + .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, +#endif + .dmamode = STM32_DMA_CR_CHSEL(DAC4_CH2_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_DAC_DAC4_CH2_DMA_PRIORITY) | + STM32_DMA_CR_MINC | STM32_DMA_CR_CIRC | STM32_DMA_CR_DIR_M2P | + STM32_DMA_CR_DMEIE | STM32_DMA_CR_TEIE | STM32_DMA_CR_HTIE | + STM32_DMA_CR_TCIE, + .dmairqprio = STM32_DAC_DAC4_CH2_IRQ_PRIORITY +}; +#endif + +/*===========================================================================*/ +/* Driver local functions. */ +/*===========================================================================*/ + +/** + * @brief Shared end/half-of-tx service routine. + * + * @param[in] dacp pointer to the @p DACDriver object + * @param[in] flags pre-shifted content of the ISR register + */ +static void dac_lld_serve_tx_interrupt(DACDriver *dacp, uint32_t flags) { + + if ((flags & (STM32_DMA_ISR_TEIF | STM32_DMA_ISR_DMEIF)) != 0) { + /* DMA errors handling.*/ + dac_lld_stop_conversion(dacp); + _dac_isr_error_code(dacp, DAC_ERR_DMAFAILURE); + } + else { + if ((flags & STM32_DMA_ISR_HTIF) != 0) { + /* Half transfer processing.*/ + _dac_isr_half_code(dacp); + } + if ((flags & STM32_DMA_ISR_TCIF) != 0) { + /* Transfer complete processing.*/ + _dac_isr_full_code(dacp); + } + } +} + +/*===========================================================================*/ +/* Driver interrupt handlers. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +/** + * @brief Low level DAC driver initialization. + * + * @notapi + */ +void dac_lld_init(void) { + +#if STM32_DAC_USE_DAC1_CH1 + dacObjectInit(&DACD1); + DACD1.params = &dac1_ch1_params; + DACD1.dma = NULL; +#endif + +#if STM32_DAC_USE_DAC1_CH2 + dacObjectInit(&DACD2); + DACD2.params = &dac1_ch2_params; + DACD2.dma = NULL; +#endif + +#if STM32_DAC_USE_DAC2_CH1 + dacObjectInit(&DACD3); + DACD3.params = &dac2_ch1_params; + DACD3.dma = NULL; +#endif + +#if STM32_DAC_USE_DAC2_CH2 + dacObjectInit(&DACD4); + DACD4.params = &dac2_ch2_params; + DACD4.dma = NULL; +#endif + +#if STM32_DAC_USE_DAC3_CH1 + dacObjectInit(&DACD5); + DACD5.params = &dac3_ch1_params; + DACD5.dma = NULL; +#endif + +#if STM32_DAC_USE_DAC3_CH2 + dacObjectInit(&DACD6); + DACD6.params = &dac3_ch2_params; + DACD6.dma = NULL; +#endif + +#if STM32_DAC_USE_DAC4_CH1 + dacObjectInit(&DACD7); + DACD7.params = &dac4_ch1_params; + DACD7.dma = NULL; +#endif + +#if STM32_DAC_USE_DAC4_CH2 + dacObjectInit(&DACD8); + DACD8.params = &dac4_ch2_params; + DACD8.dma = NULL; +#endif +} + +/** + * @brief Configures and activates the DAC peripheral. + * + * @param[in] dacp pointer to the @p DACDriver object + * + * @notapi + */ +void dac_lld_start(DACDriver *dacp) { + + /* If the driver is in DAC_STOP state then a full initialization is + required.*/ + if (dacp->state == DAC_STOP) { + dacchannel_t channel = 0; + + /* Enabling the clock source.*/ +#if STM32_DAC_USE_DAC1_CH1 + if (&DACD1 == dacp) { + rccEnableDAC1(true); + } +#endif + +#if STM32_DAC_USE_DAC1_CH2 + if (&DACD2 == dacp) { + rccEnableDAC1(true); + channel = 1; + } +#endif + +#if STM32_DAC_USE_DAC2_CH1 + if (&DACD3 == dacp) { + rccEnableDAC2(true); + } +#endif + +#if STM32_DAC_USE_DAC2_CH2 + if (&DACD4 == dacp) { + rccEnableDAC2(true); + channel = 1; + } +#endif + +#if STM32_DAC_USE_DAC3_CH1 + if (&DACD5 == dacp) { + rccEnableDAC3(true); + } +#endif + +#if STM32_DAC_USE_DAC3_CH2 + if (&DACD6 == dacp) { + rccEnableDAC3(true); + channel = 1; + } +#endif + +#if STM32_DAC_USE_DAC4_CH1 + if (&DACD7 == dacp) { + rccEnableDAC4(true); + } +#endif + +#if STM32_DAC_USE_DAC4_CH2 + if (&DACD8 == dacp) { + rccEnableDAC4(true); + channel = 1; + } +#endif + +#if STM32_DAC_DUAL_MODE == FALSE + /* Enabling DAC in SW triggering mode initially, initializing data to + configuration default.*/ + { + uint32_t reg; + + /* 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 /* 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 */ + } +} + +/** + * @brief Deactivates the DAC peripheral. + * + * @param[in] dacp pointer to the @p DACDriver object + * + * @notapi + */ +void dac_lld_stop(DACDriver *dacp) { + + /* If in ready state then disables the DAC clock.*/ + if (dacp->state == DAC_READY) { + + /* Disabling DAC.*/ + dacp->params->dac->CR &= dacp->params->regmask; + +#if STM32_DAC_USE_DAC1_CH1 + if (&DACD1 == dacp) { +#if defined(DAC_CR_EN2) + if ((dacp->params->dac->CR & DAC_CR_EN2) == 0U) { + rccDisableDAC1(); + } +#else + rccDisableDAC1(); +#endif + } +#endif + +#if STM32_DAC_USE_DAC1_CH2 + if (&DACD2 == dacp) { + if ((dacp->params->dac->CR & DAC_CR_EN1) == 0U) { + rccDisableDAC1(); + } + } +#endif + +#if STM32_DAC_USE_DAC2_CH1 + if (&DACD3 == dacp) { +#if defined(DAC_CR_EN2) + if ((dacp->params->dac->CR & DAC_CR_EN2) == 0U) { + rccDisableDAC2(); + } +#else + rccDisableDAC2(); +#endif + } +#endif + +#if STM32_DAC_USE_DAC2_CH2 + if (&DACD4 == dacp) { + if ((dacp->params->dac->CR & DAC_CR_EN1) == 0U) { + rccDisableDAC2(); + } + } +#endif + +#if STM32_DAC_USE_DAC3_CH1 + if (&DACD5 == dacp) { +#if defined(DAC_CR_EN2) + if ((dacp->params->dac->CR & DAC_CR_EN2) == 0U) { + rccDisableDAC3(); + } +#else + rccDisableDAC3(); +#endif + } +#endif + +#if STM32_DAC_USE_DAC3_CH2 + if (&DACD6 == dacp) { + if ((dacp->params->dac->CR & DAC_CR_EN1) == 0U) { + rccDisableDAC3(); + } + } +#endif + +#if STM32_DAC_USE_DAC4_CH1 + if (&DACD7 == dacp) { +#if defined(DAC_CR_EN2) + if ((dacp->params->dac->CR & DAC_CR_EN2) == 0U) { + rccDisableDAC4(); + } +#else + rccDisableDAC4(); +#endif + } +#endif + +#if STM32_DAC_USE_DAC4_CH2 + if (&DACD8 == dacp) { + if ((dacp->params->dac->CR & DAC_CR_EN1) == 0U) { + rccDisableDAC4(); + } + } +#endif + } +} + +/** + * @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 + * @param[in] sample value to be output + * + * @api + */ +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 + case DAC_DHRM_12BIT_RIGHT_DUAL: +#endif + if (channel == 0U) { +#if STM32_DAC_DUAL_MODE + dacp->params->dac->DHR12R1 = (uint32_t)sample; +#else + *(&dacp->params->dac->DHR12R1 + dacp->params->dataoffset) = (uint32_t)sample; +#endif + } +#if (STM32_HAS_DAC1_CH2 || STM32_HAS_DAC2_CH2 || \ + STM32_HAS_DAC3_CH2 || STM32_HAS_DAC4_CH2) + else { + dacp->params->dac->DHR12R2 = (uint32_t)sample; + } +#endif + break; + case DAC_DHRM_12BIT_LEFT: +#if STM32_DAC_DUAL_MODE + case DAC_DHRM_12BIT_LEFT_DUAL: +#endif + if (channel == 0U) { +#if STM32_DAC_DUAL_MODE + dacp->params->dac->DHR12L1 = (uint32_t)sample; +#else + *(&dacp->params->dac->DHR12L1 + dacp->params->dataoffset) = (uint32_t)sample; +#endif + } +#if (STM32_HAS_DAC1_CH2 || STM32_HAS_DAC2_CH2 || \ + STM32_HAS_DAC3_CH2 || STM32_HAS_DAC4_CH2) + else { + dacp->params->dac->DHR12L2 = (uint32_t)sample; + } +#endif + break; + case DAC_DHRM_8BIT_RIGHT: +#if STM32_DAC_DUAL_MODE + case DAC_DHRM_8BIT_RIGHT_DUAL: +#endif + if (channel == 0U) { +#if STM32_DAC_DUAL_MODE + dacp->params->dac->DHR8R1 = (uint32_t)sample; +#else + *(&dacp->params->dac->DHR8R1 + dacp->params->dataoffset) = (uint32_t)sample; +#endif + } +#if (STM32_HAS_DAC1_CH2 || STM32_HAS_DAC2_CH2 || \ + STM32_HAS_DAC3_CH2 || STM32_HAS_DAC4_CH2) + else { + dacp->params->dac->DHR8R2 = (uint32_t)sample; + } +#endif + break; + default: + osalDbgAssert(false, "unexpected DAC mode"); + break; + } +} + +/** + * @brief Starts a DAC conversion. + * @details Starts an asynchronous conversion operation. + * @note In @p DAC_DHRM_8BIT_RIGHT mode the parameters passed to the + * callback are wrong because two samples are packed in a single + * dacsample_t element. This will not be corrected, do not rely + * on those parameters. + * @note In @p DAC_DHRM_8BIT_RIGHT_DUAL mode two samples are treated + * 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 + * + * @notapi + */ +void dac_lld_start_conversion(DACDriver *dacp) { + uint32_t n, cr, dmamode; + + /* Number of DMA operations per buffer.*/ + n = dacp->depth * dacp->grpp->num_channels; + + /* Allocating the DMA channel.*/ + dacp->dma = dmaStreamAllocI(dacp->params->dmastream, + dacp->params->dmairqprio, + (stm32_dmaisr_t)dac_lld_serve_tx_interrupt, + (void *)dacp); + osalDbgAssert(dacp->dma != NULL, "unable to allocate stream"); +#if STM32_DMA_SUPPORTS_DMAMUX + dmaSetRequestSource(dacp->dma, dacp->params->peripheral); +#endif + + /* DMA settings depend on the chosen DAC mode.*/ + switch (dacp->config->datamode) { + /* Sets the DAC data register */ + case DAC_DHRM_12BIT_RIGHT: + osalDbgAssert(dacp->grpp->num_channels == 1, "invalid number of channels"); + + dmaStreamSetPeripheral(dacp->dma, &dacp->params->dac->DHR12R1 + + dacp->params->dataoffset); + dmamode = dacp->params->dmamode | +#if STM32_DMA_ADVANCED == FALSE + STM32_DMA_CR_PSIZE_WORD | STM32_DMA_CR_MSIZE_HWORD; +#else + STM32_DMA_CR_PSIZE_HWORD | STM32_DMA_CR_MSIZE_HWORD; +#endif + break; + case DAC_DHRM_12BIT_LEFT: + osalDbgAssert(dacp->grpp->num_channels == 1, "invalid number of channels"); + + dmaStreamSetPeripheral(dacp->dma, &dacp->params->dac->DHR12L1 + + dacp->params->dataoffset); + dmamode = dacp->params->dmamode | +#if STM32_DMA_ADVANCED == FALSE + STM32_DMA_CR_PSIZE_WORD | STM32_DMA_CR_MSIZE_HWORD; +#else + STM32_DMA_CR_PSIZE_HWORD | STM32_DMA_CR_MSIZE_HWORD; +#endif + break; + case DAC_DHRM_8BIT_RIGHT: + osalDbgAssert(dacp->grpp->num_channels == 1, "invalid number of channels"); + + dmaStreamSetPeripheral(dacp->dma, &dacp->params->dac->DHR8R1 + + dacp->params->dataoffset); + dmamode = dacp->params->dmamode | +#if STM32_DMA_ADVANCED == FALSE + STM32_DMA_CR_PSIZE_WORD | STM32_DMA_CR_MSIZE_BYTE; +#else + STM32_DMA_CR_MSIZE_BYTE | STM32_DMA_CR_MSIZE_BYTE; +#endif + + /* In this mode the size of the buffer is halved because two samples + packed in a single dacsample_t element.*/ + n = (n + 1) / 2; + break; +#if STM32_DAC_DUAL_MODE == TRUE + case DAC_DHRM_12BIT_RIGHT_DUAL: + osalDbgAssert(dacp->grpp->num_channels == 2, "invalid number of channels"); + + dmaStreamSetPeripheral(dacp->dma, &dacp->params->dac->DHR12RD); + dmamode = dacp->params->dmamode | + STM32_DMA_CR_PSIZE_WORD | STM32_DMA_CR_MSIZE_WORD; + n /= 2; + break; + case DAC_DHRM_12BIT_LEFT_DUAL: + osalDbgAssert(dacp->grpp->num_channels == 2, "invalid number of channels"); + + dmaStreamSetPeripheral(dacp->dma, &dacp->params->dac->DHR12LD); + dmamode = dacp->params->dmamode | + STM32_DMA_CR_PSIZE_WORD | STM32_DMA_CR_MSIZE_WORD; + n /= 2; + break; + case DAC_DHRM_8BIT_RIGHT_DUAL: + osalDbgAssert(dacp->grpp->num_channels == 1, "invalid number of channels"); + + dmaStreamSetPeripheral(dacp->dma, &dacp->params->dac->DHR8RD); + dmamode = dacp->params->dmamode | +#if STM32_DMA_ADVANCED == FALSE + STM32_DMA_CR_PSIZE_WORD | STM32_DMA_CR_MSIZE_HWORD; +#else + STM32_DMA_CR_PSIZE_HWORD | STM32_DMA_CR_MSIZE_HWORD; +#endif + n /= 2; + break; +#endif + default: + osalDbgAssert(false, "unexpected DAC mode"); + return; + } + + dmaStreamSetMemory0(dacp->dma, dacp->samples); + dmaStreamSetTransactionSize(dacp->dma, n); + dmaStreamSetMode(dacp->dma, dmamode | + STM32_DMA_CR_DMEIE | STM32_DMA_CR_TEIE | + STM32_DMA_CR_HTIE | STM32_DMA_CR_TCIE); + dmaStreamEnable(dacp->dma); + + /* 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; +#else + /* 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; +} + +/** + * @brief Stops an ongoing conversion. + * @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 + * + * @iclass + */ +void dac_lld_stop_conversion(DACDriver *dacp) { + uint32_t cr; + + /* DMA channel disabled and released.*/ + dmaStreamDisable(dacp->dma); + 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->regmask)) << + dacp->params->regshift; +#else + dacp->params->dac->MCR = dacp->config->mcr; + cr = dacp->config->cr | DAC_CR_EN1 | DAC_CR_EN2; +#endif + + dacp->params->dac->CR = cr; +} + +#endif /* HAL_USE_DAC */ + +/** @} */ diff --git a/os/hal/ports/STM32/LLD/DACv1/hal_dac_lld.h b/os/hal/ports/STM32/LLD/DACv1/hal_dac_lld.h index 80672fa75..7fefd5327 100644 --- a/os/hal/ports/STM32/LLD/DACv1/hal_dac_lld.h +++ b/os/hal/ports/STM32/LLD/DACv1/hal_dac_lld.h @@ -1,662 +1,671 @@ -/* - ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -/** - * @file DACv1/hal_dac_lld.h - * @brief STM32 DAC subsystem low level driver header. - * - * @addtogroup DAC - * @{ - */ - -#ifndef HAL_DAC_LLD_H -#define HAL_DAC_LLD_H - -#if HAL_USE_DAC || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Driver constants. */ -/*===========================================================================*/ - -/** - * @name DAC trigger modes - * @{ - */ -#define DAC_TRG_MASK 7U -#define DAC_TRG(n) (n) -/** @} */ - -/*===========================================================================*/ -/* Driver pre-compile time settings. */ -/*===========================================================================*/ - -/** - * @name Configuration options - * @{ - */ -/** - * @brief Enables the DAC dual mode. - * @note In dual mode DAC second channels cannot be accessed individually. - */ -#if !defined(STM32_DAC_DUAL_MODE) || defined(__DOXYGEN__) -#define STM32_DAC_DUAL_MODE FALSE -#endif - -/** - * @brief DAC1 CH1 driver enable switch. - * @details If set to @p TRUE the support for DAC1 channel 1 is included. - * @note The default is @p FALSE. - */ -#if !defined(STM32_DAC_USE_DAC1_CH1) || defined(__DOXYGEN__) -#define STM32_DAC_USE_DAC1_CH1 FALSE -#endif - -/** - * @brief DAC1 CH2 driver enable switch. - * @details If set to @p TRUE the support for DAC1 channel 2 is included. - * @note The default is @p FALSE. - */ -#if !defined(STM32_DAC_USE_DAC1_CH2) || defined(__DOXYGEN__) -#define STM32_DAC_USE_DAC1_CH2 FALSE -#endif - -/** - * @brief DAC2 CH1 driver enable switch. - * @details If set to @p TRUE the support for DAC2 channel 1 is included. - * @note The default is @p FALSE. - */ -#if !defined(STM32_DAC_USE_DAC2_CH1) || defined(__DOXYGEN__) -#define STM32_DAC_USE_DAC2_CH1 FALSE -#endif - -/** - * @brief DAC2 CH2 driver enable switch. - * @details If set to @p TRUE the support for DAC2 channel 2 is included. - * @note The default is @p FALSE. - */ -#if !defined(STM32_DAC_USE_DAC2_CH2) || defined(__DOXYGEN__) -#define STM32_DAC_USE_DAC2_CH2 FALSE -#endif - -/** - * @brief DAC3 CH1 driver enable switch. - * @details If set to @p TRUE the support for DAC3 channel 1 is included. - * @note The default is @p FALSE. - */ -#if !defined(STM32_DAC_USE_DAC3_CH1) || defined(__DOXYGEN__) -#define STM32_DAC_USE_DAC3_CH1 FALSE -#endif - -/** - * @brief DAC3 CH2 driver enable switch. - * @details If set to @p TRUE the support for DAC3 channel 2 is included. - * @note The default is @p FALSE. - */ -#if !defined(STM32_DAC_USE_DAC3_CH2) || defined(__DOXYGEN__) -#define STM32_DAC_USE_DAC3_CH2 FALSE -#endif - -/** - * @brief DAC4 CH1 driver enable switch. - * @details If set to @p TRUE the support for DAC4 channel 1 is included. - * @note The default is @p FALSE. - */ -#if !defined(STM32_DAC_USE_DAC4_CH1) || defined(__DOXYGEN__) -#define STM32_DAC_USE_DAC4_CH1 FALSE -#endif - -/** - * @brief DAC4 CH2 driver enable switch. - * @details If set to @p TRUE the support for DAC4 channel 2 is included. - * @note The default is @p FALSE. - */ -#if !defined(STM32_DAC_USE_DAC4_CH2) || defined(__DOXYGEN__) -#define STM32_DAC_USE_DAC4_CH2 FALSE -#endif - -/** - * @brief DAC1 CH1 interrupt priority level setting. - */ -#if !defined(STM32_DAC_DAC1_CH1_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_DAC_DAC1_CH1_IRQ_PRIORITY 10 -#endif - -/** - * @brief DAC1 CH2 interrupt priority level setting. - */ -#if !defined(STM32_DAC_DAC1_CH2_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_DAC_DAC1_CH2_IRQ_PRIORITY 10 -#endif - -/** - * @brief DAC2 CH1 interrupt priority level setting. - */ -#if !defined(STM32_DAC_DAC2_CH1_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_DAC_DAC2_CH1_IRQ_PRIORITY 10 -#endif - -/** - * @brief DAC2 CH2 interrupt priority level setting. - */ -#if !defined(STM32_DAC_DAC2_CH2_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_DAC_DAC2_CH2_IRQ_PRIORITY 10 -#endif - -/** - * @brief DAC3 CH1 interrupt priority level setting. - */ -#if !defined(STM32_DAC_DAC3_CH1_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_DAC_DAC3_CH1_IRQ_PRIORITY 10 -#endif - -/** - * @brief DAC3 CH2 interrupt priority level setting. - */ -#if !defined(STM32_DAC_DAC3_CH2_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_DAC_DAC3_CH2_IRQ_PRIORITY 10 -#endif - -/** - * @brief DAC4 CH1 interrupt priority level setting. - */ -#if !defined(STM32_DAC_DAC4_CH1_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_DAC_DAC4_CH1_IRQ_PRIORITY 10 -#endif - -/** - * @brief DAC4 CH2 interrupt priority level setting. - */ -#if !defined(STM32_DAC_DAC4_CH2_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_DAC_DAC4_CH2_IRQ_PRIORITY 10 -#endif - -/** - * @brief DAC1 CH1 DMA priority (0..3|lowest..highest). - */ -#if !defined(STM32_DAC_DAC1_CH1_DMA_PRIORITY) || defined(__DOXYGEN__) -#define STM32_DAC_DAC1_CH1_DMA_PRIORITY 2 -#endif - -/** - * @brief DAC1 CH2 DMA priority (0..3|lowest..highest). - */ -#if !defined(STM32_DAC_DAC1_CH2_DMA_PRIORITY) || defined(__DOXYGEN__) -#define STM32_DAC_DAC1_CH2_DMA_PRIORITY 2 -#endif - -/** - * @brief DAC2 CH1 DMA priority (0..3|lowest..highest). - */ -#if !defined(STM32_DAC_DAC2_CH1_DMA_PRIORITY) || defined(__DOXYGEN__) -#define STM32_DAC_DAC2_CH1_DMA_PRIORITY 2 -#endif - -/** - * @brief DAC2 CH2 DMA priority (0..3|lowest..highest). - */ -#if !defined(STM32_DAC_DAC2_CH2_DMA_PRIORITY) || defined(__DOXYGEN__) -#define STM32_DAC_DAC2_CH2_DMA_PRIORITY 2 -#endif - -/** - * @brief DAC3 CH1 DMA priority (0..3|lowest..highest). - */ -#if !defined(STM32_DAC_DAC3_CH1_DMA_PRIORITY) || defined(__DOXYGEN__) -#define STM32_DAC_DAC3_CH1_DMA_PRIORITY 2 -#endif - -/** - * @brief DAC3 CH2 DMA priority (0..3|lowest..highest). - */ -#if !defined(STM32_DAC_DAC3_CH2_DMA_PRIORITY) || defined(__DOXYGEN__) -#define STM32_DAC_DAC3_CH2_DMA_PRIORITY 2 -#endif - -/** - * @brief DAC4 CH1 DMA priority (0..3|lowest..highest). - */ -#if !defined(STM32_DAC_DAC4_CH1_DMA_PRIORITY) || defined(__DOXYGEN__) -#define STM32_DAC_DAC4_CH1_DMA_PRIORITY 2 -#endif - -/** - * @brief DAC4 CH2 DMA priority (0..3|lowest..highest). - */ -#if !defined(STM32_DAC_DAC4_CH2_DMA_PRIORITY) || defined(__DOXYGEN__) -#define STM32_DAC_DAC4_CH2_DMA_PRIORITY 2 -#endif -/** @} */ - -/*===========================================================================*/ -/* Derived constants and error checks. */ -/*===========================================================================*/ - -/* Handling missing registry keys.*/ -#if !defined(STM32_HAS_DAC1_CH1) -#define STM32_HAS_DAC1_CH1 FALSE -#endif -#if !defined(STM32_HAS_DAC1_CH2) -#define STM32_HAS_DAC1_CH2 FALSE -#endif -#if !defined(STM32_HAS_DAC2_CH1) -#define STM32_HAS_DAC2_CH1 FALSE -#endif -#if !defined(STM32_HAS_DAC2_CH2) -#define STM32_HAS_DAC2_CH2 FALSE -#endif -#if !defined(STM32_HAS_DAC3_CH1) -#define STM32_HAS_DAC3_CH1 FALSE -#endif -#if !defined(STM32_HAS_DAC3_CH2) -#define STM32_HAS_DAC3_CH2 FALSE -#endif -#if !defined(STM32_HAS_DAC4_CH1) -#define STM32_HAS_DAC4_CH1 FALSE -#endif -#if !defined(STM32_HAS_DAC4_CH2) -#define STM32_HAS_DAC4_CH2 FALSE -#endif - -#if STM32_DAC_USE_DAC1_CH1 && !STM32_HAS_DAC1_CH1 -#error "DAC1 CH1 not present in the selected device" -#endif - -#if STM32_DAC_USE_DAC1_CH2 && !STM32_HAS_DAC1_CH2 -#error "DAC1 CH2 not present in the selected device" -#endif - -#if STM32_DAC_USE_DAC2_CH1 && !STM32_HAS_DAC2_CH1 -#error "DAC2 CH1 not present in the selected device" -#endif - -#if STM32_DAC_USE_DAC2_CH2 && !STM32_HAS_DAC2_CH2 -#error "DAC2 CH2 not present in the selected device" -#endif - -#if STM32_DAC_USE_DAC3_CH1 && !STM32_HAS_DAC3_CH1 -#error "DAC3 CH1 not present in the selected device" -#endif - -#if STM32_DAC_USE_DAC3_CH2 && !STM32_HAS_DAC3_CH2 -#error "DAC3 CH2 not present in the selected device" -#endif - -#if STM32_DAC_USE_DAC4_CH1 && !STM32_HAS_DAC4_CH1 -#error "DAC4 CH1 not present in the selected device" -#endif - -#if STM32_DAC_USE_DAC4_CH2 && !STM32_HAS_DAC4_CH2 -#error "DAC4 CH2 not present in the selected device" -#endif - -#if (STM32_DAC_USE_DAC1_CH2 || STM32_DAC_USE_DAC2_CH2 || \ - STM32_DAC_USE_DAC3_CH2 || STM32_DAC_USE_DAC4_CH2) && STM32_DAC_DUAL_MODE -#error "DACx CH2 cannot be used independently in dual mode" -#endif - -#if !STM32_DAC_USE_DAC1_CH1 && !STM32_DAC_USE_DAC1_CH2 && \ - !STM32_DAC_USE_DAC2_CH1 && !STM32_DAC_USE_DAC2_CH2 && \ - !STM32_DAC_USE_DAC3_CH1 && !STM32_DAC_USE_DAC3_CH2 && \ - !STM32_DAC_USE_DAC4_CH1 && !STM32_DAC_USE_DAC4_CH2 -#error "DAC driver activated but no DAC peripheral assigned" -#endif - -#if STM32_DAC_USE_DAC1_CH1 && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_DAC_DAC1_CH1_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to DAC1 CH1" -#endif - -#if STM32_DAC_USE_DAC1_CH2 && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_DAC_DAC1_CH2_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to DAC1 CH2" -#endif - -#if STM32_DAC_USE_DAC2_CH1 && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_DAC_DAC2_CH1_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to DAC2 CH1" -#endif - -#if STM32_DAC_USE_DAC2_CH2 && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_DAC_DAC2_CH2_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to DAC2 CH2" -#endif - -#if STM32_DAC_USE_DAC3_CH1 && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_DAC_DAC3_CH1_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to DAC3 CH1" -#endif - -#if STM32_DAC_USE_DAC3_CH2 && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_DAC_DAC3_CH2_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to DAC3 CH2" -#endif - -#if STM32_DAC_USE_DAC4_CH1 && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_DAC_DAC4_CH1_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to DAC4 CH1" -#endif - -#if STM32_DAC_USE_DAC4_CH2 && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_DAC_DAC4_CH2_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to DAC4 CH2" -#endif - -/* The following checks are only required when there is a DMA able to - reassign streams to different channels.*/ -#if STM32_ADVANCED_DMA - -/* Check on the presence of the DMA streams settings in mcuconf.h.*/ -#if STM32_DAC_USE_DAC1_CH1 && !defined(STM32_DAC_DAC1_CH1_DMA_STREAM) -#error "DAC1 CH1 DMA stream not defined" -#endif - -#if STM32_DAC_USE_DAC1_CH2 && !defined(STM32_DAC_DAC1_CH2_DMA_STREAM) -#error "DAC1 CH2 DMA stream not defined" -#endif - -#if STM32_DAC_USE_DAC2_CH1 && !defined(STM32_DAC_DAC2_CH1_DMA_STREAM) -#error "DAC2 CH1 DMA stream not defined" -#endif - -#if STM32_DAC_USE_DAC2_CH2 && !defined(STM32_DAC_DAC2_CH2_DMA_STREAM) -#error "DAC2 CH2 DMA stream not defined" -#endif - -#if STM32_DAC_USE_DAC3_CH1 && !defined(STM32_DAC_DAC3_CH1_DMA_STREAM) -#error "DAC3 CH1 DMA stream not defined" -#endif - -#if STM32_DAC_USE_DAC3_CH2 && !defined(STM32_DAC_DAC3_CH2_DMA_STREAM) -#error "DAC3 CH2 DMA stream not defined" -#endif - -#if STM32_DAC_USE_DAC4_CH1 && !defined(STM32_DAC_DAC4_CH1_DMA_STREAM) -#error "DAC4 CH1 DMA stream not defined" -#endif - -#if STM32_DAC_USE_DAC4_CH2 && !defined(STM32_DAC_DAC4_CH2_DMA_STREAM) -#error "DAC4 CH2 DMA stream not defined" -#endif - -#if STM32_DMA_SUPPORTS_DMAMUX - -#else /* !STM32_DMA_SUPPORTS_DMAMUX */ - -/* Check on the validity of the assigned DMA streams.*/ -#if STM32_DAC_USE_DAC1_CH1 && \ - !STM32_DMA_IS_VALID_ID(STM32_DAC_DAC1_CH1_DMA_STREAM, STM32_DAC1_CH1_DMA_MSK) -#error "invalid DMA stream associated to DAC1 CH1" -#endif - -#if STM32_DAC_USE_DAC1_CH2 && \ - !STM32_DMA_IS_VALID_ID(STM32_DAC_DAC1_CH2_DMA_STREAM, STM32_DAC1_CH2_DMA_MSK) -#error "invalid DMA stream associated to DAC1 CH2" -#endif - -#if STM32_DAC_USE_DAC2_CH1 && \ - !STM32_DMA_IS_VALID_ID(STM32_DAC_DAC2_CH1_DMA_STREAM, STM32_DAC2_CH1_DMA_MSK) -#error "invalid DMA stream associated to DAC2 CH1" -#endif - -#if STM32_DAC_USE_DAC2_CH2 && \ - !STM32_DMA_IS_VALID_ID(STM32_DAC_DAC2_CH2_DMA_STREAM, STM32_DAC2_CH2_DMA_MSK) -#error "invalid DMA stream associated to DAC2 CH2" -#endif - -#if STM32_DAC_USE_DAC3_CH1 && \ - !STM32_DMA_IS_VALID_ID(STM32_DAC_DAC3_CH1_DMA_STREAM, STM32_DAC3_CH1_DMA_MSK) -#error "invalid DMA stream associated to DAC1 CH1" -#endif - -#if STM32_DAC_USE_DAC3_CH2 && \ - !STM32_DMA_IS_VALID_ID(STM32_DAC_DAC3_CH2_DMA_STREAM, STM32_DAC3_CH2_DMA_MSK) -#error "invalid DMA stream associated to DAC1 CH2" -#endif - -#if STM32_DAC_USE_DAC4_CH1 && \ - !STM32_DMA_IS_VALID_ID(STM32_DAC_DAC4_CH1_DMA_STREAM, STM32_DAC4_CH1_DMA_MSK) -#error "invalid DMA stream associated to DAC2 CH1" -#endif - -#if STM32_DAC_USE_DAC4_CH2 && \ - !STM32_DMA_IS_VALID_ID(STM32_DAC_DAC4_CH2_DMA_STREAM, STM32_DAC4_CH2_DMA_MSK) -#error "invalid DMA stream associated to DAC2 CH2" -#endif - -#endif /* !STM32_DMA_SUPPORTS_DMAMUX */ - -#endif /* STM32_ADVANCED_DMA */ - -#if STM32_DAC_USE_DAC1_CH1 && \ - !STM32_DMA_IS_VALID_PRIORITY(STM32_DAC_DAC1_CH1_DMA_PRIORITY) -#error "Invalid DMA priority assigned to DAC1 CH1" -#endif - -#if STM32_DAC_USE_DAC1_CH2 && \ - !STM32_DMA_IS_VALID_PRIORITY(STM32_DAC_DAC1_CH2_DMA_PRIORITY) -#error "Invalid DMA priority assigned to DAC1 CH2" -#endif - -#if STM32_DAC_USE_DAC2_CH1 && \ - !STM32_DMA_IS_VALID_PRIORITY(STM32_DAC_DAC2_CH1_DMA_PRIORITY) -#error "Invalid DMA priority assigned to DAC2 CH1" -#endif - -#if STM32_DAC_USE_DAC2_CH2 && \ - !STM32_DMA_IS_VALID_PRIORITY(STM32_DAC_DAC2_CH2_DMA_PRIORITY) -#error "Invalid DMA priority assigned to DAC2 CH2" -#endif - -#if STM32_DAC_USE_DAC3_CH1 && \ - !STM32_DMA_IS_VALID_PRIORITY(STM32_DAC_DAC3_CH1_DMA_PRIORITY) -#error "Invalid DMA priority assigned to DAC3 CH1" -#endif - -#if STM32_DAC_USE_DAC3_CH2 && \ - !STM32_DMA_IS_VALID_PRIORITY(STM32_DAC_DAC3_CH2_DMA_PRIORITY) -#error "Invalid DMA priority assigned to DAC3 CH2" -#endif - -#if STM32_DAC_USE_DAC4_CH1 && \ - !STM32_DMA_IS_VALID_PRIORITY(STM32_DAC_DAC4_CH1_DMA_PRIORITY) -#error "Invalid DMA priority assigned to DAC4 CH1" -#endif - -#if STM32_DAC_USE_DAC4_CH2 && \ - !STM32_DMA_IS_VALID_PRIORITY(STM32_DAC_DAC4_CH2_DMA_PRIORITY) -#error "Invalid DMA priority assigned to DAC4 CH2" -#endif - -#if !defined(STM32_DMA_REQUIRED) -#define STM32_DMA_REQUIRED -#endif - -/** - * @brief Max DAC channels. - */ -#if STM32_DAC_DUAL_MODE == FALSE -#define DAC_MAX_CHANNELS 2 -#else -#define DAC_MAX_CHANNELS 1 -#endif - -/*===========================================================================*/ -/* Driver data structures and types. */ -/*===========================================================================*/ - -/** - * @brief Type of a DAC channel index. - */ -typedef uint32_t dacchannel_t; - -/** - * @brief Type representing a DAC sample. - */ -typedef uint16_t dacsample_t; - -/** - * @brief DAC channel parameters type. - */ -typedef struct { - /** - * @brief Pointer to the DAC registers block. - */ - DAC_TypeDef *dac; - /** - * @brief DAC data registers offset. - */ - uint32_t dataoffset; - /** - * @brief DAC CR register bit offset. - */ - uint32_t regshift; - /** - * @brief DAC CR register mask. - */ - uint32_t regmask; - /** - * @brief Associated DMA stream. - */ - uint32_t dmastream; - /** - * @brief Mode bits for the DMA. - */ - uint32_t dmamode; - /** - * @brief DMA channel IRQ priority. - */ - uint32_t dmairqprio; -#if (STM32_DMA_SUPPORTS_DMAMUX == TRUE) || defined(__DOXYGEN__) - /** - * @brief DMAMUX peripheral selector. - */ - uint32_t peripheral; -#endif -} dacparams_t; - -/** - * @brief Possible DAC failure causes. - * @note Error codes are architecture dependent and should not relied - * upon. - */ -typedef enum { - DAC_ERR_DMAFAILURE = 0, /**< DMA operations failure. */ - DAC_ERR_UNDERFLOW = 1 /**< DAC overflow condition. */ -} dacerror_t; - -/** - * @brief Samples alignment and size mode. - */ -typedef enum { - DAC_DHRM_12BIT_RIGHT = 0, - DAC_DHRM_12BIT_LEFT = 1, - DAC_DHRM_8BIT_RIGHT = 2, -#if STM32_DAC_DUAL_MODE && !defined(__DOXYGEN__) - DAC_DHRM_12BIT_RIGHT_DUAL = 3, - DAC_DHRM_12BIT_LEFT_DUAL = 4, - DAC_DHRM_8BIT_RIGHT_DUAL = 5 -#endif -} dacdhrmode_t; - -/*===========================================================================*/ -/* Driver macros. */ -/*===========================================================================*/ - -/** - * @brief Low level fields of the DAC driver structure. - */ -#define dac_lld_driver_fields \ - /* DAC channel parameters.*/ \ - const dacparams_t *params; \ - /* Associated DMA.*/ \ - const stm32_dma_stream_t *dma - -/** - * @brief Low level fields of the DAC configuration structure. - */ -#define dac_lld_config_fields \ - /* Initial output on DAC channels.*/ \ - dacsample_t init; \ - /* DAC data holding register mode.*/ \ - dacdhrmode_t datamode; \ - /* DAC control register lower 16 bits.*/ \ - uint32_t cr - -/** - * @brief Low level fields of the DAC group configuration structure. - */ -#define dac_lld_conversion_group_fields \ - /* DAC initialization data. This field contains the (not shifted) value \ - to be put into the TSEL field of the DAC CR register during \ - initialization. All other fields are handled internally.*/ \ - uint32_t trigger - -/*===========================================================================*/ -/* External declarations. */ -/*===========================================================================*/ - -#if STM32_DAC_USE_DAC1_CH1 && !defined(__DOXYGEN__) -extern DACDriver DACD1; -#endif - -#if STM32_DAC_USE_DAC1_CH2 && !STM32_DAC_DUAL_MODE && !defined(__DOXYGEN__) -extern DACDriver DACD2; -#endif - -#if STM32_DAC_USE_DAC2_CH1 && !defined(__DOXYGEN__) -extern DACDriver DACD3; -#endif - -#if STM32_DAC_USE_DAC2_CH2 && !STM32_DAC_DUAL_MODE && !defined(__DOXYGEN__) -extern DACDriver DACD4; -#endif - -#if STM32_DAC_USE_DAC3_CH1 && !defined(__DOXYGEN__) -extern DACDriver DACD5; -#endif - -#if STM32_DAC_USE_DAC3_CH2 && !STM32_DAC_DUAL_MODE && !defined(__DOXYGEN__) -extern DACDriver DACD6; -#endif - -#if STM32_DAC_USE_DAC4_CH1 && !defined(__DOXYGEN__) -extern DACDriver DACD7; -#endif - -#if STM32_DAC_USE_DAC4_CH2 && !STM32_DAC_DUAL_MODE && !defined(__DOXYGEN__) -extern DACDriver DACD8; -#endif - -#ifdef __cplusplus -extern "C" { -#endif - void dac_lld_init(void); - void dac_lld_start(DACDriver *dacp); - void dac_lld_stop(DACDriver *dacp); - void dac_lld_put_channel(DACDriver *dacp, - dacchannel_t channel, - dacsample_t sample); - void dac_lld_start_conversion(DACDriver *dacp); - void dac_lld_stop_conversion(DACDriver *dacp); -#ifdef __cplusplus -} -#endif - -#endif /* HAL_USE_DAC */ - -#endif /* HAL_DAC_LLD_H */ - -/** @} */ +/* + ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file DACv1/hal_dac_lld.h + * @brief STM32 DAC subsystem low level driver header. + * + * @addtogroup DAC + * @{ + */ + +#ifndef HAL_DAC_LLD_H +#define HAL_DAC_LLD_H + +#if HAL_USE_DAC || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver constants. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver pre-compile time settings. */ +/*===========================================================================*/ + +/** + * @name Configuration options + * @{ + */ +/** + * @brief Enables the DAC dual mode. + * @note In dual mode DAC second channels cannot be accessed individually. + */ +#if !defined(STM32_DAC_DUAL_MODE) || defined(__DOXYGEN__) +#define STM32_DAC_DUAL_MODE FALSE +#endif + +/** + * @brief DAC1 CH1 driver enable switch. + * @details If set to @p TRUE the support for DAC1 channel 1 is included. + * @note The default is @p FALSE. + */ +#if !defined(STM32_DAC_USE_DAC1_CH1) || defined(__DOXYGEN__) +#define STM32_DAC_USE_DAC1_CH1 FALSE +#endif + +/** + * @brief DAC1 CH2 driver enable switch. + * @details If set to @p TRUE the support for DAC1 channel 2 is included. + * @note The default is @p FALSE. + */ +#if !defined(STM32_DAC_USE_DAC1_CH2) || defined(__DOXYGEN__) +#define STM32_DAC_USE_DAC1_CH2 FALSE +#endif + +/** + * @brief DAC2 CH1 driver enable switch. + * @details If set to @p TRUE the support for DAC2 channel 1 is included. + * @note The default is @p FALSE. + */ +#if !defined(STM32_DAC_USE_DAC2_CH1) || defined(__DOXYGEN__) +#define STM32_DAC_USE_DAC2_CH1 FALSE +#endif + +/** + * @brief DAC2 CH2 driver enable switch. + * @details If set to @p TRUE the support for DAC2 channel 2 is included. + * @note The default is @p FALSE. + */ +#if !defined(STM32_DAC_USE_DAC2_CH2) || defined(__DOXYGEN__) +#define STM32_DAC_USE_DAC2_CH2 FALSE +#endif + +/** + * @brief DAC3 CH1 driver enable switch. + * @details If set to @p TRUE the support for DAC3 channel 1 is included. + * @note The default is @p FALSE. + */ +#if !defined(STM32_DAC_USE_DAC3_CH1) || defined(__DOXYGEN__) +#define STM32_DAC_USE_DAC3_CH1 FALSE +#endif + +/** + * @brief DAC3 CH2 driver enable switch. + * @details If set to @p TRUE the support for DAC3 channel 2 is included. + * @note The default is @p FALSE. + */ +#if !defined(STM32_DAC_USE_DAC3_CH2) || defined(__DOXYGEN__) +#define STM32_DAC_USE_DAC3_CH2 FALSE +#endif + +/** + * @brief DAC4 CH1 driver enable switch. + * @details If set to @p TRUE the support for DAC4 channel 1 is included. + * @note The default is @p FALSE. + */ +#if !defined(STM32_DAC_USE_DAC4_CH1) || defined(__DOXYGEN__) +#define STM32_DAC_USE_DAC4_CH1 FALSE +#endif + +/** + * @brief DAC4 CH2 driver enable switch. + * @details If set to @p TRUE the support for DAC4 channel 2 is included. + * @note The default is @p FALSE. + */ +#if !defined(STM32_DAC_USE_DAC4_CH2) || defined(__DOXYGEN__) +#define STM32_DAC_USE_DAC4_CH2 FALSE +#endif + +/** + * @brief DAC1 CH1 interrupt priority level setting. + */ +#if !defined(STM32_DAC_DAC1_CH1_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_DAC_DAC1_CH1_IRQ_PRIORITY 10 +#endif + +/** + * @brief DAC1 CH2 interrupt priority level setting. + */ +#if !defined(STM32_DAC_DAC1_CH2_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_DAC_DAC1_CH2_IRQ_PRIORITY 10 +#endif + +/** + * @brief DAC2 CH1 interrupt priority level setting. + */ +#if !defined(STM32_DAC_DAC2_CH1_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_DAC_DAC2_CH1_IRQ_PRIORITY 10 +#endif + +/** + * @brief DAC2 CH2 interrupt priority level setting. + */ +#if !defined(STM32_DAC_DAC2_CH2_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_DAC_DAC2_CH2_IRQ_PRIORITY 10 +#endif + +/** + * @brief DAC3 CH1 interrupt priority level setting. + */ +#if !defined(STM32_DAC_DAC3_CH1_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_DAC_DAC3_CH1_IRQ_PRIORITY 10 +#endif + +/** + * @brief DAC3 CH2 interrupt priority level setting. + */ +#if !defined(STM32_DAC_DAC3_CH2_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_DAC_DAC3_CH2_IRQ_PRIORITY 10 +#endif + +/** + * @brief DAC4 CH1 interrupt priority level setting. + */ +#if !defined(STM32_DAC_DAC4_CH1_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_DAC_DAC4_CH1_IRQ_PRIORITY 10 +#endif + +/** + * @brief DAC4 CH2 interrupt priority level setting. + */ +#if !defined(STM32_DAC_DAC4_CH2_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_DAC_DAC4_CH2_IRQ_PRIORITY 10 +#endif + +/** + * @brief DAC1 CH1 DMA priority (0..3|lowest..highest). + */ +#if !defined(STM32_DAC_DAC1_CH1_DMA_PRIORITY) || defined(__DOXYGEN__) +#define STM32_DAC_DAC1_CH1_DMA_PRIORITY 2 +#endif + +/** + * @brief DAC1 CH2 DMA priority (0..3|lowest..highest). + */ +#if !defined(STM32_DAC_DAC1_CH2_DMA_PRIORITY) || defined(__DOXYGEN__) +#define STM32_DAC_DAC1_CH2_DMA_PRIORITY 2 +#endif + +/** + * @brief DAC2 CH1 DMA priority (0..3|lowest..highest). + */ +#if !defined(STM32_DAC_DAC2_CH1_DMA_PRIORITY) || defined(__DOXYGEN__) +#define STM32_DAC_DAC2_CH1_DMA_PRIORITY 2 +#endif + +/** + * @brief DAC2 CH2 DMA priority (0..3|lowest..highest). + */ +#if !defined(STM32_DAC_DAC2_CH2_DMA_PRIORITY) || defined(__DOXYGEN__) +#define STM32_DAC_DAC2_CH2_DMA_PRIORITY 2 +#endif + +/** + * @brief DAC3 CH1 DMA priority (0..3|lowest..highest). + */ +#if !defined(STM32_DAC_DAC3_CH1_DMA_PRIORITY) || defined(__DOXYGEN__) +#define STM32_DAC_DAC3_CH1_DMA_PRIORITY 2 +#endif + +/** + * @brief DAC3 CH2 DMA priority (0..3|lowest..highest). + */ +#if !defined(STM32_DAC_DAC3_CH2_DMA_PRIORITY) || defined(__DOXYGEN__) +#define STM32_DAC_DAC3_CH2_DMA_PRIORITY 2 +#endif + +/** + * @brief DAC4 CH1 DMA priority (0..3|lowest..highest). + */ +#if !defined(STM32_DAC_DAC4_CH1_DMA_PRIORITY) || defined(__DOXYGEN__) +#define STM32_DAC_DAC4_CH1_DMA_PRIORITY 2 +#endif + +/** + * @brief DAC4 CH2 DMA priority (0..3|lowest..highest). + */ +#if !defined(STM32_DAC_DAC4_CH2_DMA_PRIORITY) || defined(__DOXYGEN__) +#define STM32_DAC_DAC4_CH2_DMA_PRIORITY 2 +#endif +/** @} */ + +/*===========================================================================*/ +/* Derived constants and error checks. */ +/*===========================================================================*/ + +/* Handling missing registry keys.*/ +#if !defined(STM32_HAS_DAC1_CH1) +#define STM32_HAS_DAC1_CH1 FALSE +#endif +#if !defined(STM32_HAS_DAC1_CH2) +#define STM32_HAS_DAC1_CH2 FALSE +#endif +#if !defined(STM32_HAS_DAC2_CH1) +#define STM32_HAS_DAC2_CH1 FALSE +#endif +#if !defined(STM32_HAS_DAC2_CH2) +#define STM32_HAS_DAC2_CH2 FALSE +#endif +#if !defined(STM32_HAS_DAC3_CH1) +#define STM32_HAS_DAC3_CH1 FALSE +#endif +#if !defined(STM32_HAS_DAC3_CH2) +#define STM32_HAS_DAC3_CH2 FALSE +#endif +#if !defined(STM32_HAS_DAC4_CH1) +#define STM32_HAS_DAC4_CH1 FALSE +#endif +#if !defined(STM32_HAS_DAC4_CH2) +#define STM32_HAS_DAC4_CH2 FALSE +#endif + +#if STM32_DAC_USE_DAC1_CH1 && !STM32_HAS_DAC1_CH1 +#error "DAC1 CH1 not present in the selected device" +#endif + +#if STM32_DAC_USE_DAC1_CH2 && !STM32_HAS_DAC1_CH2 +#error "DAC1 CH2 not present in the selected device" +#endif + +#if STM32_DAC_USE_DAC2_CH1 && !STM32_HAS_DAC2_CH1 +#error "DAC2 CH1 not present in the selected device" +#endif + +#if STM32_DAC_USE_DAC2_CH2 && !STM32_HAS_DAC2_CH2 +#error "DAC2 CH2 not present in the selected device" +#endif + +#if STM32_DAC_USE_DAC3_CH1 && !STM32_HAS_DAC3_CH1 +#error "DAC3 CH1 not present in the selected device" +#endif + +#if STM32_DAC_USE_DAC3_CH2 && !STM32_HAS_DAC3_CH2 +#error "DAC3 CH2 not present in the selected device" +#endif + +#if STM32_DAC_USE_DAC4_CH1 && !STM32_HAS_DAC4_CH1 +#error "DAC4 CH1 not present in the selected device" +#endif + +#if STM32_DAC_USE_DAC4_CH2 && !STM32_HAS_DAC4_CH2 +#error "DAC4 CH2 not present in the selected device" +#endif + +#if (STM32_DAC_USE_DAC1_CH2 || STM32_DAC_USE_DAC2_CH2 || \ + STM32_DAC_USE_DAC3_CH2 || STM32_DAC_USE_DAC4_CH2) && STM32_DAC_DUAL_MODE +#error "DACx CH2 cannot be used independently in dual mode" +#endif + +#if !STM32_DAC_USE_DAC1_CH1 && !STM32_DAC_USE_DAC1_CH2 && \ + !STM32_DAC_USE_DAC2_CH1 && !STM32_DAC_USE_DAC2_CH2 && \ + !STM32_DAC_USE_DAC3_CH1 && !STM32_DAC_USE_DAC3_CH2 && \ + !STM32_DAC_USE_DAC4_CH1 && !STM32_DAC_USE_DAC4_CH2 +#error "DAC driver activated but no DAC peripheral assigned" +#endif + +#if STM32_DAC_USE_DAC1_CH1 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_DAC_DAC1_CH1_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to DAC1 CH1" +#endif + +#if STM32_DAC_USE_DAC1_CH2 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_DAC_DAC1_CH2_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to DAC1 CH2" +#endif + +#if STM32_DAC_USE_DAC2_CH1 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_DAC_DAC2_CH1_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to DAC2 CH1" +#endif + +#if STM32_DAC_USE_DAC2_CH2 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_DAC_DAC2_CH2_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to DAC2 CH2" +#endif + +#if STM32_DAC_USE_DAC3_CH1 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_DAC_DAC3_CH1_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to DAC3 CH1" +#endif + +#if STM32_DAC_USE_DAC3_CH2 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_DAC_DAC3_CH2_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to DAC3 CH2" +#endif + +#if STM32_DAC_USE_DAC4_CH1 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_DAC_DAC4_CH1_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to DAC4 CH1" +#endif + +#if STM32_DAC_USE_DAC4_CH2 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_DAC_DAC4_CH2_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to DAC4 CH2" +#endif + +/* The following checks are only required when there is a DMA able to + reassign streams to different channels.*/ +#if STM32_ADVANCED_DMA + +/* Check on the presence of the DMA streams settings in mcuconf.h.*/ +#if STM32_DAC_USE_DAC1_CH1 && !defined(STM32_DAC_DAC1_CH1_DMA_STREAM) +#error "DAC1 CH1 DMA stream not defined" +#endif + +#if STM32_DAC_USE_DAC1_CH2 && !defined(STM32_DAC_DAC1_CH2_DMA_STREAM) +#error "DAC1 CH2 DMA stream not defined" +#endif + +#if STM32_DAC_USE_DAC2_CH1 && !defined(STM32_DAC_DAC2_CH1_DMA_STREAM) +#error "DAC2 CH1 DMA stream not defined" +#endif + +#if STM32_DAC_USE_DAC2_CH2 && !defined(STM32_DAC_DAC2_CH2_DMA_STREAM) +#error "DAC2 CH2 DMA stream not defined" +#endif + +#if STM32_DAC_USE_DAC3_CH1 && !defined(STM32_DAC_DAC3_CH1_DMA_STREAM) +#error "DAC3 CH1 DMA stream not defined" +#endif + +#if STM32_DAC_USE_DAC3_CH2 && !defined(STM32_DAC_DAC3_CH2_DMA_STREAM) +#error "DAC3 CH2 DMA stream not defined" +#endif + +#if STM32_DAC_USE_DAC4_CH1 && !defined(STM32_DAC_DAC4_CH1_DMA_STREAM) +#error "DAC4 CH1 DMA stream not defined" +#endif + +#if STM32_DAC_USE_DAC4_CH2 && !defined(STM32_DAC_DAC4_CH2_DMA_STREAM) +#error "DAC4 CH2 DMA stream not defined" +#endif + +#if STM32_DMA_SUPPORTS_DMAMUX + +#else /* !STM32_DMA_SUPPORTS_DMAMUX */ + +/* Check on the validity of the assigned DMA streams.*/ +#if STM32_DAC_USE_DAC1_CH1 && \ + !STM32_DMA_IS_VALID_ID(STM32_DAC_DAC1_CH1_DMA_STREAM, STM32_DAC1_CH1_DMA_MSK) +#error "invalid DMA stream associated to DAC1 CH1" +#endif + +#if STM32_DAC_USE_DAC1_CH2 && \ + !STM32_DMA_IS_VALID_ID(STM32_DAC_DAC1_CH2_DMA_STREAM, STM32_DAC1_CH2_DMA_MSK) +#error "invalid DMA stream associated to DAC1 CH2" +#endif + +#if STM32_DAC_USE_DAC2_CH1 && \ + !STM32_DMA_IS_VALID_ID(STM32_DAC_DAC2_CH1_DMA_STREAM, STM32_DAC2_CH1_DMA_MSK) +#error "invalid DMA stream associated to DAC2 CH1" +#endif + +#if STM32_DAC_USE_DAC2_CH2 && \ + !STM32_DMA_IS_VALID_ID(STM32_DAC_DAC2_CH2_DMA_STREAM, STM32_DAC2_CH2_DMA_MSK) +#error "invalid DMA stream associated to DAC2 CH2" +#endif + +#if STM32_DAC_USE_DAC3_CH1 && \ + !STM32_DMA_IS_VALID_ID(STM32_DAC_DAC3_CH1_DMA_STREAM, STM32_DAC3_CH1_DMA_MSK) +#error "invalid DMA stream associated to DAC1 CH1" +#endif + +#if STM32_DAC_USE_DAC3_CH2 && \ + !STM32_DMA_IS_VALID_ID(STM32_DAC_DAC3_CH2_DMA_STREAM, STM32_DAC3_CH2_DMA_MSK) +#error "invalid DMA stream associated to DAC1 CH2" +#endif + +#if STM32_DAC_USE_DAC4_CH1 && \ + !STM32_DMA_IS_VALID_ID(STM32_DAC_DAC4_CH1_DMA_STREAM, STM32_DAC4_CH1_DMA_MSK) +#error "invalid DMA stream associated to DAC2 CH1" +#endif + +#if STM32_DAC_USE_DAC4_CH2 && \ + !STM32_DMA_IS_VALID_ID(STM32_DAC_DAC4_CH2_DMA_STREAM, STM32_DAC4_CH2_DMA_MSK) +#error "invalid DMA stream associated to DAC2 CH2" +#endif + +#endif /* !STM32_DMA_SUPPORTS_DMAMUX */ + +#endif /* STM32_ADVANCED_DMA */ + +#if STM32_DAC_USE_DAC1_CH1 && \ + !STM32_DMA_IS_VALID_PRIORITY(STM32_DAC_DAC1_CH1_DMA_PRIORITY) +#error "Invalid DMA priority assigned to DAC1 CH1" +#endif + +#if STM32_DAC_USE_DAC1_CH2 && \ + !STM32_DMA_IS_VALID_PRIORITY(STM32_DAC_DAC1_CH2_DMA_PRIORITY) +#error "Invalid DMA priority assigned to DAC1 CH2" +#endif + +#if STM32_DAC_USE_DAC2_CH1 && \ + !STM32_DMA_IS_VALID_PRIORITY(STM32_DAC_DAC2_CH1_DMA_PRIORITY) +#error "Invalid DMA priority assigned to DAC2 CH1" +#endif + +#if STM32_DAC_USE_DAC2_CH2 && \ + !STM32_DMA_IS_VALID_PRIORITY(STM32_DAC_DAC2_CH2_DMA_PRIORITY) +#error "Invalid DMA priority assigned to DAC2 CH2" +#endif + +#if STM32_DAC_USE_DAC3_CH1 && \ + !STM32_DMA_IS_VALID_PRIORITY(STM32_DAC_DAC3_CH1_DMA_PRIORITY) +#error "Invalid DMA priority assigned to DAC3 CH1" +#endif + +#if STM32_DAC_USE_DAC3_CH2 && \ + !STM32_DMA_IS_VALID_PRIORITY(STM32_DAC_DAC3_CH2_DMA_PRIORITY) +#error "Invalid DMA priority assigned to DAC3 CH2" +#endif + +#if STM32_DAC_USE_DAC4_CH1 && \ + !STM32_DMA_IS_VALID_PRIORITY(STM32_DAC_DAC4_CH1_DMA_PRIORITY) +#error "Invalid DMA priority assigned to DAC4 CH1" +#endif + +#if STM32_DAC_USE_DAC4_CH2 && \ + !STM32_DMA_IS_VALID_PRIORITY(STM32_DAC_DAC4_CH2_DMA_PRIORITY) +#error "Invalid DMA priority assigned to DAC4 CH2" +#endif + +#if !defined(STM32_DMA_REQUIRED) +#define STM32_DMA_REQUIRED +#endif + +/** + * @brief Max DAC channels. + */ +#if STM32_DAC_DUAL_MODE == TRUE +#define DAC_MAX_CHANNELS 2 +#else +#define DAC_MAX_CHANNELS 1 +#endif + +/*===========================================================================*/ +/* Driver data structures and types. */ +/*===========================================================================*/ + +/** + * @brief Type of a DAC channel index. + */ +typedef uint32_t dacchannel_t; + +/** + * @brief Type representing a DAC sample. + */ +typedef uint16_t dacsample_t; + +/** + * @brief DAC channel parameters type. + */ +typedef struct { + /** + * @brief Pointer to the DAC registers block. + */ + DAC_TypeDef *dac; + /** + * @brief DAC data registers offset. + */ + uint32_t dataoffset; + /** + * @brief DAC CR register bit offset. + */ + uint32_t regshift; + /** + * @brief DAC CR register mask. + */ + uint32_t regmask; + /** + * @brief Associated DMA stream. + */ + uint32_t dmastream; + /** + * @brief Mode bits for the DMA. + */ + uint32_t dmamode; + /** + * @brief DMA channel IRQ priority. + */ + uint32_t dmairqprio; +#if (STM32_DMA_SUPPORTS_DMAMUX == TRUE) || defined(__DOXYGEN__) + /** + * @brief DMAMUX peripheral selector. + */ + uint32_t peripheral; +#endif +} dacparams_t; + +/** + * @brief Possible DAC failure causes. + * @note Error codes are architecture dependent and should not relied + * upon. + */ +typedef enum { + DAC_ERR_DMAFAILURE = 0, /**< DMA operations failure. */ + DAC_ERR_UNDERFLOW = 1 /**< DAC overflow condition. */ +} dacerror_t; + +/** + * @brief Samples alignment and size mode. + */ +typedef enum { + DAC_DHRM_12BIT_RIGHT = 0, + DAC_DHRM_12BIT_LEFT = 1, + DAC_DHRM_8BIT_RIGHT = 2, +#if STM32_DAC_DUAL_MODE && !defined(__DOXYGEN__) + DAC_DHRM_12BIT_RIGHT_DUAL = 3, + DAC_DHRM_12BIT_LEFT_DUAL = 4, + DAC_DHRM_8BIT_RIGHT_DUAL = 5 +#endif +} dacdhrmode_t; + +/** + * @brief Low level fields of the DAC driver structure. + */ +#define dac_lld_driver_fields \ + /* DAC channel parameters.*/ \ + const dacparams_t *params; \ + /* Associated DMA.*/ \ + const stm32_dma_stream_t *dma + +/** + * @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 channel.*/ \ + uint32_t init; \ + /* DAC data holding register mode.*/ \ + dacdhrmode_t datamode; \ + /* DAC control register.*/ \ + uint32_t cr; \ + /* DAC mode control register.*/ \ + uint32_t mcr + +/** + * @brief Low level fields of the DAC group configuration structure. + */ +#define dac_lld_conversion_group_fields \ + /* DAC initialization data. This field contains the (not shifted) value \ + to be put into the TSEL field of the DAC CR register during \ + 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. */ +/*===========================================================================*/ + +#if STM32_DAC_USE_DAC1_CH1 && !defined(__DOXYGEN__) +extern DACDriver DACD1; +#endif + +#if STM32_DAC_USE_DAC1_CH2 && !STM32_DAC_DUAL_MODE && !defined(__DOXYGEN__) +extern DACDriver DACD2; +#endif + +#if STM32_DAC_USE_DAC2_CH1 && !defined(__DOXYGEN__) +extern DACDriver DACD3; +#endif + +#if STM32_DAC_USE_DAC2_CH2 && !STM32_DAC_DUAL_MODE && !defined(__DOXYGEN__) +extern DACDriver DACD4; +#endif + +#if STM32_DAC_USE_DAC3_CH1 && !defined(__DOXYGEN__) +extern DACDriver DACD5; +#endif + +#if STM32_DAC_USE_DAC3_CH2 && !STM32_DAC_DUAL_MODE && !defined(__DOXYGEN__) +extern DACDriver DACD6; +#endif + +#if STM32_DAC_USE_DAC4_CH1 && !defined(__DOXYGEN__) +extern DACDriver DACD7; +#endif + +#if STM32_DAC_USE_DAC4_CH2 && !STM32_DAC_DUAL_MODE && !defined(__DOXYGEN__) +extern DACDriver DACD8; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + void dac_lld_init(void); + void dac_lld_start(DACDriver *dacp); + void dac_lld_stop(DACDriver *dacp); + void dac_lld_put_channel(DACDriver *dacp, + dacchannel_t channel, + dacsample_t sample); + void dac_lld_start_conversion(DACDriver *dacp); + void dac_lld_stop_conversion(DACDriver *dacp); +#ifdef __cplusplus +} +#endif + +#endif /* HAL_USE_DAC */ + +#endif /* HAL_DAC_LLD_H */ + +/** @} */ diff --git a/os/hal/src/hal_dac.c b/os/hal/src/hal_dac.c index f0a449f3e..a68a6bf6e 100644 --- a/os/hal/src/hal_dac.c +++ b/os/hal/src/hal_dac.c @@ -1,365 +1,422 @@ -/* - ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -/** - * @file hal_dac.c - * @brief DAC Driver code. - * - * @addtogroup DAC - * @{ - */ - -#include "hal.h" - -#if (HAL_USE_DAC == TRUE) || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Driver local definitions. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver exported variables. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver local variables. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver local functions. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver exported functions. */ -/*===========================================================================*/ - -/** - * @brief DAC Driver initialization. - * @note This function is implicitly invoked by @p halInit(), there is - * no need to explicitly initialize the driver. - * - * @init - */ -void dacInit(void) { - - dac_lld_init(); -} - -/** - * @brief Initializes the standard part of a @p DACDriver structure. - * - * @param[out] dacp pointer to the @p DACDriver object - * - * @init - */ -void dacObjectInit(DACDriver *dacp) { - - dacp->state = DAC_STOP; - dacp->config = NULL; -#if DAC_USE_WAIT - dacp->thread = NULL; -#endif -#if DAC_USE_MUTUAL_EXCLUSION - osalMutexObjectInit(&dacp->mutex); -#endif -#if defined(DAC_DRIVER_EXT_INIT_HOOK) - DAC_DRIVER_EXT_INIT_HOOK(dacp); -#endif -} - -/** - * @brief Configures and activates the DAC peripheral. - * - * @param[in] dacp pointer to the @p DACDriver object - * @param[in] config pointer to the @p DACConfig object, it can be - * @p NULL if the low level driver implementation - * supports a default configuration - * @return The operation status. - * - * @api - */ -msg_t dacStart(DACDriver *dacp, const DACConfig *config) { - msg_t msg; - - osalDbgCheck(dacp != NULL); - - osalSysLock(); - - osalDbgAssert((dacp->state == DAC_STOP) || (dacp->state == DAC_READY), - "invalid state"); - - dacp->config = config; - -#if defined(DAC_LLD_ENHANCED_API) - msg = dac_lld_start(dacp); -#else - dac_lld_start(dacp); - msg = HAL_RET_SUCCESS; -#endif - if (msg == HAL_RET_SUCCESS) { - dacp->state = DAC_READY; - } - else { - dacp->state = DAC_STOP; - } - - osalSysUnlock(); - - return msg; -} - -/** - * @brief Deactivates the DAC peripheral. - * @note Deactivating the peripheral also enforces a release of the slave - * select line. - * - * @param[in] dacp pointer to the @p DACDriver object - * - * @api - */ -void dacStop(DACDriver *dacp) { - - osalDbgCheck(dacp != NULL); - - osalSysLock(); - - osalDbgAssert((dacp->state == DAC_STOP) || (dacp->state == DAC_READY), - "invalid state"); - - dac_lld_stop(dacp); - dacp->config = NULL; - dacp->state = DAC_STOP; - - osalSysUnlock(); -} - -/** - * @brief Outputs a value directly on a DAC channel. - * - * @param[in] dacp pointer to the @p DACDriver object - * @param[in] channel DAC channel number - * @param[in] sample value to be output - * - * @xclass - */ -void dacPutChannelX(DACDriver *dacp, dacchannel_t channel, dacsample_t sample) { - - osalDbgCheck(channel < (dacchannel_t)DAC_MAX_CHANNELS); - osalDbgAssert(dacp->state == DAC_READY, "invalid state"); - - dac_lld_put_channel(dacp, channel, sample); -} - -/** - * @brief Starts a DAC 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] dacp pointer to the @p DACDriver object - * @param[in] grpp pointer to a @p DACConversionGroup object - * @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. - * - * @api - */ -void dacStartConversion(DACDriver *dacp, - const DACConversionGroup *grpp, - dacsample_t *samples, - size_t depth) { - - osalSysLock(); - dacStartConversionI(dacp, grpp, samples, depth); - osalSysUnlock(); -} - -/** - * @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. - * @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] dacp pointer to the @p DACDriver object - * @param[in] grpp pointer to a @p DACConversionGroup object - * @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. - * - * @iclass - */ -void dacStartConversionI(DACDriver *dacp, - const DACConversionGroup *grpp, - dacsample_t *samples, - size_t depth) { - - osalDbgCheckClassI(); - osalDbgCheck((dacp != NULL) && (grpp != NULL) && (samples != NULL) && - ((depth == 1U) || ((depth & 1U) == 0U))); - osalDbgAssert((dacp->state == DAC_READY) || - (dacp->state == DAC_COMPLETE) || - (dacp->state == DAC_ERROR), - "not ready"); - - dacp->samples = samples; - dacp->depth = depth; - dacp->grpp = grpp; - dacp->state = DAC_ACTIVE; - dac_lld_start_conversion(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. - * - * @param[in] dacp pointer to the @p DACDriver object - * - * @api - */ -void dacStopConversion(DACDriver *dacp) { - - osalDbgCheck(dacp != NULL); - - osalSysLock(); - - osalDbgAssert((dacp->state == DAC_READY) || - (dacp->state == DAC_ACTIVE), - "invalid state"); - - if (dacp->state != DAC_READY) { - dac_lld_stop_conversion(dacp); - dacp->grpp = NULL; - dacp->state = DAC_READY; - _dac_reset_s(dacp); - } - - osalSysUnlock(); -} - -/** - * @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. - * - * @param[in] dacp pointer to the @p DACDriver object - * - * @iclass - */ -void dacStopConversionI(DACDriver *dacp) { - - osalDbgCheckClassI(); - osalDbgCheck(dacp != NULL); - osalDbgAssert((dacp->state == DAC_READY) || - (dacp->state == DAC_ACTIVE) || - (dacp->state == DAC_COMPLETE), - "invalid state"); - - if (dacp->state != DAC_READY) { - dac_lld_stop_conversion(dacp); - dacp->grpp = NULL; - dacp->state = DAC_READY; - _dac_reset_i(dacp); - } -} - -#if (DAC_USE_WAIT == 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 - * 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] 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. - * @retval MSG_TIMEOUT The conversion has been stopped because an hardware - * error. - * - * @api - */ -msg_t dacConvert(DACDriver *dacp, - const DACConversionGroup *grpp, - dacsample_t *samples, - size_t depth) { - msg_t msg; - - osalSysLock(); - - dacStartConversionI(dacp, grpp, samples, depth); - msg = osalThreadSuspendS(&dacp->thread); - - osalSysUnlock(); - return msg; -} -#endif /* DAC_USE_WAIT == TRUE */ - -#if (DAC_USE_MUTUAL_EXCLUSION == TRUE) || defined(__DOXYGEN__) -/** - * @brief Gains exclusive access to the DAC bus. - * @details This function tries to gain ownership to the DAC bus, if the bus - * is already being used then the invoking thread is queued. - * @pre In order to use this function the option @p DAC_USE_MUTUAL_EXCLUSION - * must be enabled. - * - * @param[in] dacp pointer to the @p DACDriver object - * - * @api - */ -void dacAcquireBus(DACDriver *dacp) { - - osalDbgCheck(dacp != NULL); - - osalMutexLock(&dacp->mutex); -} - -/** - * @brief Releases exclusive access to the DAC bus. - * @pre In order to use this function the option @p DAC_USE_MUTUAL_EXCLUSION - * must be enabled. - * - * @param[in] dacp pointer to the @p DACDriver object - * - * @api - */ -void dacReleaseBus(DACDriver *dacp) { - - osalDbgCheck(dacp != NULL); - - osalMutexUnlock(&dacp->mutex); -} -#endif /* DAC_USE_MUTUAL_EXCLUSION == TRUE */ - -#endif /* HAL_USE_DAC == TRUE */ - -/** @} */ +/* + ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file hal_dac.c + * @brief DAC Driver code. + * + * @addtogroup DAC + * @{ + */ + +#include "hal.h" + +#if (HAL_USE_DAC == TRUE) || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver local definitions. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver exported variables. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver local variables. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver local functions. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +/** + * @brief DAC Driver initialization. + * @note This function is implicitly invoked by @p halInit(), there is + * no need to explicitly initialize the driver. + * + * @init + */ +void dacInit(void) { + + dac_lld_init(); +} + +/** + * @brief Initializes the standard part of a @p DACDriver structure. + * + * @param[out] dacp pointer to the @p DACDriver object + * + * @init + */ +void dacObjectInit(DACDriver *dacp) { + + dacp->state = DAC_STOP; + dacp->config = NULL; +#if DAC_USE_WAIT + dacp->thread = NULL; +#endif +#if DAC_USE_MUTUAL_EXCLUSION + osalMutexObjectInit(&dacp->mutex); +#endif +#if defined(DAC_DRIVER_EXT_INIT_HOOK) + DAC_DRIVER_EXT_INIT_HOOK(dacp); +#endif +} + +/** + * @brief Configures and activates the DAC peripheral. + * + * @param[in] dacp pointer to the @p DACDriver object + * @param[in] config pointer to the @p DACConfig object, it can be + * @p NULL if the low level driver implementation + * supports a default configuration + * @return The operation status. + * + * @api + */ +msg_t dacStart(DACDriver *dacp, const DACConfig *config) { + msg_t msg; + + osalDbgCheck(dacp != NULL); + + osalSysLock(); + + osalDbgAssert((dacp->state == DAC_STOP) || (dacp->state == DAC_READY), + "invalid state"); + + dacp->config = config; + +#if defined(DAC_LLD_ENHANCED_API) + msg = dac_lld_start(dacp); +#else + dac_lld_start(dacp); + msg = HAL_RET_SUCCESS; +#endif + if (msg == HAL_RET_SUCCESS) { + dacp->state = DAC_READY; + } + else { + dacp->state = DAC_STOP; + } + + osalSysUnlock(); + + return msg; +} + +/** + * @brief Deactivates the DAC peripheral. + * @note Deactivating the peripheral also enforces a release of the slave + * select line. + * + * @param[in] dacp pointer to the @p DACDriver object + * + * @api + */ +void dacStop(DACDriver *dacp) { + + osalDbgCheck(dacp != NULL); + + osalSysLock(); + + osalDbgAssert((dacp->state == DAC_STOP) || (dacp->state == DAC_READY), + "invalid state"); + + dac_lld_stop(dacp); + dacp->config = NULL; + dacp->state = DAC_STOP; + + osalSysUnlock(); +} + +/** + * @brief Outputs a value directly on a DAC channel. + * + * @param[in] dacp pointer to the @p DACDriver object + * @param[in] channel DAC channel number + * @param[in] sample value to be output + * + * @xclass + */ +void dacPutChannelX(DACDriver *dacp, dacchannel_t channel, dacsample_t sample) { + + osalDbgCheck(channel < (dacchannel_t)DAC_MAX_CHANNELS); + osalDbgAssert(dacp->state == DAC_READY || dacp->state == DAC_ACTIVE, + "invalid state"); + + dac_lld_put_channel(dacp, channel, sample); +} + +/** + * @brief Starts a DAC 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] dacp pointer to the @p DACDriver object + * @param[in] grpp pointer to a @p DACConversionGroup object + * @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. + * + * @api + */ +void dacStartConversion(DACDriver *dacp, + const DACConversionGroup *grpp, + dacsample_t *samples, + size_t depth) { + + osalSysLock(); + dacStartConversionI(dacp, grpp, samples, depth); + osalSysUnlock(); +} + +/** + * @brief Starts a DAC conversion. + * @details Starts an asynchronous conversion operation. + * @post The callbacks associated to the conversion group will be invoked + * 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 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[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. + * + * @iclass + */ +void dacStartConversionI(DACDriver *dacp, + const DACConversionGroup *grpp, + dacsample_t *samples, + size_t depth) { + + osalDbgCheckClassI(); + osalDbgCheck((dacp != NULL) && (grpp != NULL) && (samples != NULL) && + ((depth == 1U) || ((depth & 1U) == 0U))); + osalDbgAssert((dacp->state == DAC_READY) || + (dacp->state == DAC_COMPLETE) || + (dacp->state == DAC_ERROR), + "not ready"); + + dacp->samples = samples; + dacp->depth = depth; + dacp->grpp = grpp; + dacp->state = DAC_ACTIVE; + dac_lld_start_conversion(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. + * + * @param[in] dacp pointer to the @p DACDriver object + * + * @api + */ +void dacStopConversion(DACDriver *dacp) { + + osalDbgCheck(dacp != NULL); + + osalSysLock(); + + osalDbgAssert((dacp->state == DAC_READY) || + (dacp->state == DAC_ACTIVE), + "invalid state"); + + if (dacp->state != DAC_READY) { + dac_lld_stop_conversion(dacp); + dacp->grpp = NULL; + dacp->state = DAC_READY; + _dac_reset_s(dacp); + } + + osalSysUnlock(); +} + +/** + * @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. + * + * @param[in] dacp pointer to the @p DACDriver object + * + * @iclass + */ +void dacStopConversionI(DACDriver *dacp) { + + osalDbgCheckClassI(); + osalDbgCheck(dacp != NULL); + osalDbgAssert((dacp->state == DAC_READY) || + (dacp->state == DAC_ACTIVE) || + (dacp->state == DAC_COMPLETE), + "invalid state"); + + if (dacp->state != DAC_READY) { + dac_lld_stop_conversion(dacp); + dacp->grpp = NULL; + dacp->state = DAC_READY; + _dac_reset_i(dacp); + } +} + +#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 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[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 dacStopConversion() or @p dacStopConversionI(). + * @retval MSG_TIMEOUT The conversion has been stopped because an hardware + * error. + * + * @api + */ +msg_t dacConvert(DACDriver *dacp, + const DACConversionGroup *grpp, + dacsample_t *samples, + size_t depth) { + msg_t msg; + + osalSysLock(); + + dacStartConversionI(dacp, grpp, samples, depth); + msg = osalThreadSuspendS(&dacp->thread); + + osalSysUnlock(); + 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. + * + * @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__) +/** + * @brief Gains exclusive access to the DAC bus. + * @details This function tries to gain ownership to the DAC bus, if the bus + * is already being used then the invoking thread is queued. + * @pre In order to use this function the option @p DAC_USE_MUTUAL_EXCLUSION + * must be enabled. + * + * @param[in] dacp pointer to the @p DACDriver object + * + * @api + */ +void dacAcquireBus(DACDriver *dacp) { + + osalDbgCheck(dacp != NULL); + + osalMutexLock(&dacp->mutex); +} + +/** + * @brief Releases exclusive access to the DAC bus. + * @pre In order to use this function the option @p DAC_USE_MUTUAL_EXCLUSION + * must be enabled. + * + * @param[in] dacp pointer to the @p DACDriver object + * + * @api + */ +void dacReleaseBus(DACDriver *dacp) { + + osalDbgCheck(dacp != NULL); + + osalMutexUnlock(&dacp->mutex); +} +#endif /* DAC_USE_MUTUAL_EXCLUSION == TRUE */ + +#endif /* HAL_USE_DAC == TRUE */ + +/** @} */ diff --git a/readme.txt b/readme.txt index 5dc90645c..b1b8e40cd 100644 --- a/readme.txt +++ b/readme.txt @@ -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.