From 34945d78e933fc62bedcc975e88be02a0b7fcc2e Mon Sep 17 00:00:00 2001 From: Joey Castillo Date: Mon, 2 Aug 2021 13:48:35 -0400 Subject: major project reorg, move library one level up --- watch-library/hpl/adc/hpl_adc.c | 769 +++++ watch-library/hpl/adc/hpl_adc_base.h | 72 + watch-library/hpl/core/hpl_core_m0plus_base.c | 200 ++ watch-library/hpl/core/hpl_core_port.h | 61 + watch-library/hpl/core/hpl_init.c | 74 + watch-library/hpl/dmac/hpl_dmac.c | 244 ++ watch-library/hpl/eic/hpl_eic.c | 255 ++ watch-library/hpl/gclk/hpl_gclk.c | 163 ++ watch-library/hpl/gclk/hpl_gclk_base.h | 87 + watch-library/hpl/mclk/hpl_mclk.c | 45 + watch-library/hpl/osc32kctrl/hpl_osc32kctrl.c | 86 + watch-library/hpl/oscctrl/hpl_oscctrl.c | 179 ++ watch-library/hpl/pm/hpl_pm.c | 77 + watch-library/hpl/pm/hpl_pm_base.h | 45 + watch-library/hpl/port/hpl_gpio_base.h | 170 ++ watch-library/hpl/rtc/hpl_rtc.c | 397 +++ watch-library/hpl/rtc/hpl_rtc_base.h | 52 + watch-library/hpl/sercom/hpl_sercom.c | 2929 ++++++++++++++++++++ watch-library/hpl/slcd/hpl_slcd.c | 336 +++ watch-library/hpl/slcd/hpl_slcd_cm.h | 59 + .../hpl/slcd/hpl_slcd_cm_14_seg_mapping.h | 104 + watch-library/hpl/slcd/hpl_slcd_cm_7_seg_mapping.h | 68 + watch-library/hpl/systick/hpl_systick.c | 103 + watch-library/hpl/tc/hpl_tc.c | 374 +++ watch-library/hpl/tc/hpl_tc_base.h | 160 ++ watch-library/hpl/tcc/hpl_tcc.c | 364 +++ watch-library/hpl/tcc/hpl_tcc.h | 160 ++ 27 files changed, 7633 insertions(+) create mode 100644 watch-library/hpl/adc/hpl_adc.c create mode 100644 watch-library/hpl/adc/hpl_adc_base.h create mode 100644 watch-library/hpl/core/hpl_core_m0plus_base.c create mode 100644 watch-library/hpl/core/hpl_core_port.h create mode 100644 watch-library/hpl/core/hpl_init.c create mode 100644 watch-library/hpl/dmac/hpl_dmac.c create mode 100644 watch-library/hpl/eic/hpl_eic.c create mode 100644 watch-library/hpl/gclk/hpl_gclk.c create mode 100644 watch-library/hpl/gclk/hpl_gclk_base.h create mode 100644 watch-library/hpl/mclk/hpl_mclk.c create mode 100644 watch-library/hpl/osc32kctrl/hpl_osc32kctrl.c create mode 100644 watch-library/hpl/oscctrl/hpl_oscctrl.c create mode 100644 watch-library/hpl/pm/hpl_pm.c create mode 100644 watch-library/hpl/pm/hpl_pm_base.h create mode 100644 watch-library/hpl/port/hpl_gpio_base.h create mode 100644 watch-library/hpl/rtc/hpl_rtc.c create mode 100644 watch-library/hpl/rtc/hpl_rtc_base.h create mode 100644 watch-library/hpl/sercom/hpl_sercom.c create mode 100644 watch-library/hpl/slcd/hpl_slcd.c create mode 100644 watch-library/hpl/slcd/hpl_slcd_cm.h create mode 100644 watch-library/hpl/slcd/hpl_slcd_cm_14_seg_mapping.h create mode 100644 watch-library/hpl/slcd/hpl_slcd_cm_7_seg_mapping.h create mode 100644 watch-library/hpl/systick/hpl_systick.c create mode 100644 watch-library/hpl/tc/hpl_tc.c create mode 100644 watch-library/hpl/tc/hpl_tc_base.h create mode 100644 watch-library/hpl/tcc/hpl_tcc.c create mode 100644 watch-library/hpl/tcc/hpl_tcc.h (limited to 'watch-library/hpl') diff --git a/watch-library/hpl/adc/hpl_adc.c b/watch-library/hpl/adc/hpl_adc.c new file mode 100644 index 00000000..032302cb --- /dev/null +++ b/watch-library/hpl/adc/hpl_adc.c @@ -0,0 +1,769 @@ + +/** + * \file + * + * \brief SAM Analog Digital Converter + * + * Copyright (c) 2015-2018 Microchip Technology Inc. and its subsidiaries. + * + * \asf_license_start + * + * \page License + * + * Subject to your compliance with these terms, you may use Microchip + * software and any derivatives exclusively with Microchip products. + * It is your responsibility to comply with third party license terms applicable + * to your use of third party software (including open source software) that + * may accompany Microchip software. + * + * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, + * WHETHER EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, + * INCLUDING ANY IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, + * AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE + * LIABLE FOR ANY INDIRECT, SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL + * LOSS, DAMAGE, COST OR EXPENSE OF ANY KIND WHATSOEVER RELATED TO THE + * SOFTWARE, HOWEVER CAUSED, EVEN IF MICROCHIP HAS BEEN ADVISED OF THE + * POSSIBILITY OR THE DAMAGES ARE FORESEEABLE. TO THE FULLEST EXTENT + * ALLOWED BY LAW, MICROCHIP'S TOTAL LIABILITY ON ALL CLAIMS IN ANY WAY + * RELATED TO THIS SOFTWARE WILL NOT EXCEED THE AMOUNT OF FEES, IF ANY, + * THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR THIS SOFTWARE. + * + * \asf_license_stop + * + */ + +#include +#include +#include +#include +#include +#include + +#ifndef CONF_ADC_0_ENABLE +#define CONF_ADC_0_ENABLE 0 +#endif +#ifndef CONF_ADC_1_ENABLE +#define CONF_ADC_1_ENABLE 0 +#endif + +/** + * \brief Macro is used to fill ADC configuration structure based on its number + * + * \param[in] n The number of structures + */ +#define ADC_CONFIGURATION(n) \ + { \ + (n), \ + (CONF_ADC_##n##_RUNSTDBY << ADC_CTRLA_RUNSTDBY_Pos) | (CONF_ADC_##n##_ONDEMAND << ADC_CTRLA_ONDEMAND_Pos), \ + ADC_CTRLB_PRESCALER(CONF_ADC_##n##_PRESCALER), \ + (CONF_ADC_##n##_REFCOMP << ADC_REFCTRL_REFCOMP_Pos) | ADC_REFCTRL_REFSEL(CONF_ADC_##n##_REFSEL), \ + (CONF_ADC_##n##_WINMONEO << ADC_EVCTRL_WINMONEO_Pos) \ + | (CONF_ADC_##n##_RESRDYEO << ADC_EVCTRL_RESRDYEO_Pos) \ + | (CONF_ADC_##n##_STARTINV << ADC_EVCTRL_STARTINV_Pos) \ + | (CONF_ADC_##n##_FLUSHINV << ADC_EVCTRL_FLUSHINV_Pos) \ + | (CONF_ADC_##n##_STARTEI << ADC_EVCTRL_STARTEI_Pos) \ + | (CONF_ADC_##n##_FLUSHEI << ADC_EVCTRL_FLUSHEI_Pos), \ + ADC_INPUTCTRL_MUXNEG(CONF_ADC_##n##_MUXNEG) | ADC_INPUTCTRL_MUXPOS(CONF_ADC_##n##_MUXPOS), \ + ADC_CTRLC_WINMODE(CONF_ADC_##n##_WINMODE) | ADC_CTRLC_RESSEL(CONF_ADC_##n##_RESSEL) \ + | (CONF_ADC_##n##_CORREN << ADC_CTRLC_CORREN_Pos) | (CONF_ADC_##n##_FREERUN << ADC_CTRLC_FREERUN_Pos) \ + | (CONF_ADC_##n##_LEFTADJ << ADC_CTRLC_LEFTADJ_Pos) \ + | (CONF_ADC_##n##_DIFFMODE << ADC_CTRLC_DIFFMODE_Pos), \ + ADC_AVGCTRL_ADJRES(CONF_ADC_##n##_ADJRES) | ADC_AVGCTRL_SAMPLENUM(CONF_ADC_##n##_SAMPLENUM), \ + (CONF_ADC_##n##_OFFCOMP << ADC_SAMPCTRL_OFFCOMP_Pos) | ADC_SAMPCTRL_SAMPLEN(CONF_ADC_##n##_SAMPLEN), \ + ADC_WINLT_WINLT(CONF_ADC_##n##_WINLT), ADC_WINUT_WINUT(CONF_ADC_##n##_WINUT), \ + ADC_GAINCORR_GAINCORR(CONF_ADC_##n##_GAINCORR), ADC_OFFSETCORR_OFFSETCORR(CONF_ADC_##n##_OFFSETCORR), \ + CONF_ADC_##n##_DBGRUN << ADC_DBGCTRL_DBGRUN_Pos, ADC_SEQCTRL_SEQEN(CONF_ADC_##n##_SEQEN), \ + } + +/** + * \brief ADC configuration + */ +struct adc_configuration { + uint8_t number; + hri_adc_ctrla_reg_t ctrl_a; + hri_adc_ctrlb_reg_t ctrl_b; + hri_adc_refctrl_reg_t ref_ctrl; + hri_adc_evctrl_reg_t ev_ctrl; + hri_adc_inputctrl_reg_t input_ctrl; + hri_adc_ctrlc_reg_t ctrl_c; + hri_adc_avgctrl_reg_t avg_ctrl; + hri_adc_sampctrl_reg_t samp_ctrl; + hri_adc_winlt_reg_t win_lt; + hri_adc_winut_reg_t win_ut; + hri_adc_gaincorr_reg_t gain_corr; + hri_adc_offsetcorr_reg_t offset_corr; + hri_adc_dbgctrl_reg_t dbg_ctrl; + hri_adc_seqctrl_reg_t seq_ctrl; +}; + +#define ADC_AMOUNT (CONF_ADC_0_ENABLE + CONF_ADC_1_ENABLE) + +/** + * \brief Array of ADC configurations + */ +static const struct adc_configuration _adcs[] = { +#if CONF_ADC_0_ENABLE == 1 + ADC_CONFIGURATION(0), +#endif +#if CONF_ADC_1_ENABLE == 1 + ADC_CONFIGURATION(1), +#endif +}; + +static void _adc_set_reference_source(void *const hw, const adc_reference_t reference); + +/** + * \brief Retrieve ordinal number of the given adc hardware instance + */ +static uint8_t _adc_get_hardware_index(const void *const hw) +{ + + (void)hw; + return 0; +} + +/** \brief Return the pointer to register settings of specific ADC + * \param[in] hw_addr The hardware register base address. + * \return Pointer to register settings of specific ADC. + */ +static uint8_t _adc_get_regs(const uint32_t hw_addr) +{ + uint8_t n = _adc_get_hardware_index((const void *)hw_addr); + uint8_t i; + + for (i = 0; i < sizeof(_adcs) / sizeof(struct adc_configuration); i++) { + if (_adcs[i].number == n) { + return i; + } + } + + ASSERT(false); + return 0; +} + +/** + * \brief Retrieve IRQ number for the given hardware instance + */ +static uint8_t _adc_get_irq_num(const struct _adc_async_device *const device) +{ + + (void)device; + return ADC_IRQn; +} + +/** + * \brief Init irq param with the given afec hardware instance + */ +static void _adc_init_irq_param(const void *const hw, struct _adc_async_device *dev) +{ +} + +/** + * \brief Initialize ADC + * + * \param[in] hw The pointer to hardware instance + * \param[in] i The number of hardware instance + */ +static int32_t _adc_init(void *const hw, const uint8_t i) +{ + uint16_t calib_reg = 0; + calib_reg = ADC_CALIB_BIASREFBUF((*(uint32_t *)ADC_FUSES_BIASREFBUF_ADDR >> ADC_FUSES_BIASREFBUF_Pos)) + | ADC_CALIB_BIASCOMP((*(uint32_t *)ADC_FUSES_BIASCOMP_ADDR >> ADC_FUSES_BIASCOMP_Pos)); + + ASSERT(hw == ADC); + + if (!hri_adc_is_syncing(hw, ADC_SYNCBUSY_SWRST)) { + if (hri_adc_get_CTRLA_reg(hw, ADC_CTRLA_ENABLE)) { + hri_adc_clear_CTRLA_ENABLE_bit(hw); + hri_adc_wait_for_sync(hw, ADC_SYNCBUSY_ENABLE); + } + hri_adc_write_CTRLA_reg(hw, ADC_CTRLA_SWRST); + } + hri_adc_wait_for_sync(hw, ADC_SYNCBUSY_SWRST); + + hri_adc_write_CALIB_reg(hw, calib_reg); + hri_adc_write_CTRLB_reg(hw, _adcs[i].ctrl_b); + hri_adc_write_REFCTRL_reg(hw, _adcs[i].ref_ctrl); + hri_adc_write_EVCTRL_reg(hw, _adcs[i].ev_ctrl); + hri_adc_write_INPUTCTRL_reg(hw, _adcs[i].input_ctrl); + hri_adc_write_CTRLC_reg(hw, _adcs[i].ctrl_c); + hri_adc_write_AVGCTRL_reg(hw, _adcs[i].avg_ctrl); + hri_adc_write_SAMPCTRL_reg(hw, _adcs[i].samp_ctrl); + hri_adc_write_WINLT_reg(hw, _adcs[i].win_lt); + hri_adc_write_WINUT_reg(hw, _adcs[i].win_ut); + hri_adc_write_GAINCORR_reg(hw, _adcs[i].gain_corr); + hri_adc_write_OFFSETCORR_reg(hw, _adcs[i].offset_corr); + hri_adc_write_DBGCTRL_reg(hw, _adcs[i].dbg_ctrl); + hri_adc_write_SEQCTRL_reg(hw, _adcs[i].seq_ctrl); + hri_adc_write_CTRLA_reg(hw, _adcs[i].ctrl_a); + + return ERR_NONE; +} + +/** + * \brief De-initialize ADC + * + * \param[in] hw The pointer to hardware instance + */ +static inline void _adc_deinit(void *hw) +{ + hri_adc_clear_CTRLA_ENABLE_bit(hw); + hri_adc_set_CTRLA_SWRST_bit(hw); +} + +/** + * \brief Initialize ADC + */ +int32_t _adc_sync_init(struct _adc_sync_device *const device, void *const hw) +{ + ASSERT(device); + + device->hw = hw; + + return _adc_init(hw, _adc_get_regs((uint32_t)hw)); +} + +/** + * \brief Initialize ADC + */ +int32_t _adc_async_init(struct _adc_async_device *const device, void *const hw) +{ + int32_t init_status; + + ASSERT(device); + + init_status = _adc_init(hw, _adc_get_regs((uint32_t)hw)); + if (init_status) { + return init_status; + } + device->hw = hw; + _adc_init_irq_param(hw, device); + NVIC_DisableIRQ(_adc_get_irq_num(device)); + NVIC_ClearPendingIRQ(_adc_get_irq_num(device)); + NVIC_EnableIRQ(_adc_get_irq_num(device)); + + return ERR_NONE; +} + +/** + * \brief Initialize ADC + */ +int32_t _adc_dma_init(struct _adc_dma_device *const device, void *const hw) +{ + ASSERT(device); + + device->hw = hw; + + return _adc_init(hw, _adc_get_regs((uint32_t)hw)); +} + +/** + * \brief De-initialize ADC + */ +void _adc_sync_deinit(struct _adc_sync_device *const device) +{ + _adc_deinit(device->hw); +} + +/** + * \brief De-initialize ADC + */ +void _adc_async_deinit(struct _adc_async_device *const device) +{ + NVIC_DisableIRQ(_adc_get_irq_num(device)); + NVIC_ClearPendingIRQ(_adc_get_irq_num(device)); + + _adc_deinit(device->hw); +} + +/** + * \brief De-initialize ADC + */ +void _adc_dma_deinit(struct _adc_dma_device *const device) +{ + _adc_deinit(device->hw); +} + +/** + * \brief Enable ADC + */ +void _adc_sync_enable_channel(struct _adc_sync_device *const device, const uint8_t channel) +{ + (void)channel; + + hri_adc_set_CTRLA_ENABLE_bit(device->hw); +} + +/** + * \brief Enable ADC + */ +void _adc_async_enable_channel(struct _adc_async_device *const device, const uint8_t channel) +{ + (void)channel; + + hri_adc_set_CTRLA_ENABLE_bit(device->hw); +} + +/** + * \brief Enable ADC + */ +void _adc_dma_enable_channel(struct _adc_dma_device *const device, const uint8_t channel) +{ + (void)channel; + + hri_adc_set_CTRLA_ENABLE_bit(device->hw); +} + +/** + * \brief Disable ADC + */ +void _adc_sync_disable_channel(struct _adc_sync_device *const device, const uint8_t channel) +{ + (void)channel; + + hri_adc_clear_CTRLA_ENABLE_bit(device->hw); +} + +/** + * \brief Disable ADC + */ +void _adc_async_disable_channel(struct _adc_async_device *const device, const uint8_t channel) +{ + (void)channel; + + hri_adc_clear_CTRLA_ENABLE_bit(device->hw); +} + +/** + * \brief Disable ADC + */ +void _adc_dma_disable_channel(struct _adc_dma_device *const device, const uint8_t channel) +{ + (void)channel; + + hri_adc_clear_CTRLA_ENABLE_bit(device->hw); +} + +/** + * \brief Return address of ADC DMA source + */ +uint32_t _adc_get_source_for_dma(struct _adc_dma_device *const device) +{ + return (uint32_t) & (((Adc *)(device->hw))->RESULT.reg); +} + +/** + * \brief Retrieve ADC conversion data size + */ +uint8_t _adc_sync_get_data_size(const struct _adc_sync_device *const device) +{ + return hri_adc_read_CTRLC_RESSEL_bf(device->hw) == ADC_CTRLC_RESSEL_8BIT_Val ? 1 : 2; +} + +/** + * \brief Retrieve ADC conversion data size + */ +uint8_t _adc_async_get_data_size(const struct _adc_async_device *const device) +{ + return hri_adc_read_CTRLC_RESSEL_bf(device->hw) == ADC_CTRLC_RESSEL_8BIT_Val ? 1 : 2; +} + +/** + * \brief Retrieve ADC conversion data size + */ +uint8_t _adc_dma_get_data_size(const struct _adc_dma_device *const device) +{ + return hri_adc_read_CTRLC_RESSEL_bf(device->hw) == ADC_CTRLC_RESSEL_8BIT_Val ? 1 : 2; +} + +/** + * \brief Check if conversion is done + */ +bool _adc_sync_is_channel_conversion_done(const struct _adc_sync_device *const device, const uint8_t channel) +{ + (void)channel; + + return hri_adc_get_interrupt_RESRDY_bit(device->hw); +} + +/** + * \brief Check if conversion is done + */ +bool _adc_async_is_channel_conversion_done(const struct _adc_async_device *const device, const uint8_t channel) +{ + (void)channel; + + return hri_adc_get_interrupt_RESRDY_bit(device->hw); +} + +/** + * \brief Check if conversion is done + */ +bool _adc_dma_is_conversion_done(const struct _adc_dma_device *const device) +{ + return hri_adc_get_interrupt_RESRDY_bit(device->hw); +} + +/** + * \brief Make conversion + */ +void _adc_sync_convert(struct _adc_sync_device *const device) +{ + hri_adc_set_SWTRIG_START_bit(device->hw); +} + +/** + * \brief Make conversion + */ +void _adc_async_convert(struct _adc_async_device *const device) +{ + hri_adc_set_SWTRIG_START_bit(device->hw); +} + +/** + * \brief Make conversion + */ +void _adc_dma_convert(struct _adc_dma_device *const device) +{ + hri_adc_set_SWTRIG_START_bit(device->hw); +} + +/** + * \brief Retrieve the conversion result + */ +uint16_t _adc_sync_read_channel_data(const struct _adc_sync_device *const device, const uint8_t channel) +{ + (void)channel; + + return hri_adc_read_RESULT_reg(device->hw); +} + +/** + * \brief Retrieve the conversion result + */ +uint16_t _adc_async_read_channel_data(const struct _adc_async_device *const device, const uint8_t channel) +{ + (void)channel; + + return hri_adc_read_RESULT_reg(device->hw); +} + +/** + * \brief Set reference source + */ +void _adc_sync_set_reference_source(struct _adc_sync_device *const device, const adc_reference_t reference) +{ + _adc_set_reference_source(device->hw, reference); +} + +/** + * \brief Set reference source + */ +void _adc_async_set_reference_source(struct _adc_async_device *const device, const adc_reference_t reference) +{ + _adc_set_reference_source(device->hw, reference); +} + +/** + * \brief Set reference source + */ +void _adc_dma_set_reference_source(struct _adc_dma_device *const device, const adc_reference_t reference) +{ + _adc_set_reference_source(device->hw, reference); +} + +/** + * \brief Set resolution + */ +void _adc_sync_set_resolution(struct _adc_sync_device *const device, const adc_resolution_t resolution) +{ + hri_adc_write_CTRLC_RESSEL_bf(device->hw, resolution); +} + +/** + * \brief Set resolution + */ +void _adc_async_set_resolution(struct _adc_async_device *const device, const adc_resolution_t resolution) +{ + hri_adc_write_CTRLC_RESSEL_bf(device->hw, resolution); +} + +/** + * \brief Set resolution + */ +void _adc_dma_set_resolution(struct _adc_dma_device *const device, const adc_resolution_t resolution) +{ + hri_adc_write_CTRLC_RESSEL_bf(device->hw, resolution); +} + +/** + * \brief Set channels input sources + */ +void _adc_sync_set_inputs(struct _adc_sync_device *const device, const adc_pos_input_t pos_input, + const adc_neg_input_t neg_input, const uint8_t channel) +{ + (void)channel; + + hri_adc_write_INPUTCTRL_MUXPOS_bf(device->hw, pos_input); + hri_adc_write_INPUTCTRL_MUXNEG_bf(device->hw, neg_input); +} + +/** + * \brief Set channels input sources + */ +void _adc_async_set_inputs(struct _adc_async_device *const device, const adc_pos_input_t pos_input, + const adc_neg_input_t neg_input, const uint8_t channel) +{ + (void)channel; + + hri_adc_write_INPUTCTRL_MUXPOS_bf(device->hw, pos_input); + hri_adc_write_INPUTCTRL_MUXNEG_bf(device->hw, neg_input); +} + +/** + * \brief Set channels input source + */ +void _adc_dma_set_inputs(struct _adc_dma_device *const device, const adc_pos_input_t pos_input, + const adc_neg_input_t neg_input, const uint8_t channel) +{ + (void)channel; + + hri_adc_write_INPUTCTRL_MUXPOS_bf(device->hw, pos_input); + hri_adc_write_INPUTCTRL_MUXNEG_bf(device->hw, neg_input); +} + +/** + * \brief Set thresholds + */ +void _adc_sync_set_thresholds(struct _adc_sync_device *const device, const adc_threshold_t low_threshold, + const adc_threshold_t up_threshold) +{ + hri_adc_write_WINLT_reg(device->hw, low_threshold); + hri_adc_write_WINUT_reg(device->hw, up_threshold); +} + +/** + * \brief Set thresholds + */ +void _adc_async_set_thresholds(struct _adc_async_device *const device, const adc_threshold_t low_threshold, + const adc_threshold_t up_threshold) +{ + hri_adc_write_WINLT_reg(device->hw, low_threshold); + hri_adc_write_WINUT_reg(device->hw, up_threshold); +} + +/** + * \brief Set thresholds + */ +void _adc_dma_set_thresholds(struct _adc_dma_device *const device, const adc_threshold_t low_threshold, + const adc_threshold_t up_threshold) +{ + hri_adc_write_WINLT_reg(device->hw, low_threshold); + hri_adc_write_WINUT_reg(device->hw, up_threshold); +} + +/** + * \brief Set gain + */ +void _adc_sync_set_channel_gain(struct _adc_sync_device *const device, const uint8_t channel, const adc_gain_t gain) +{ + (void)device, (void)channel, (void)gain; +} + +/** + * \brief Set gain + */ +void _adc_async_set_channel_gain(struct _adc_async_device *const device, const uint8_t channel, const adc_gain_t gain) +{ + (void)device, (void)channel, (void)gain; +} + +/** + * \brief Set gain + */ +void _adc_dma_set_channel_gain(struct _adc_dma_device *const device, const uint8_t channel, const adc_gain_t gain) +{ + (void)device, (void)channel, (void)gain; +} + +/** + * \brief Set conversion mode + */ +void _adc_sync_set_conversion_mode(struct _adc_sync_device *const device, const enum adc_conversion_mode mode) +{ + if (ADC_CONVERSION_MODE_FREERUN == mode) { + hri_adc_set_CTRLC_FREERUN_bit(device->hw); + } else { + hri_adc_clear_CTRLC_FREERUN_bit(device->hw); + } +} + +/** + * \brief Set conversion mode + */ +void _adc_async_set_conversion_mode(struct _adc_async_device *const device, const enum adc_conversion_mode mode) +{ + if (ADC_CONVERSION_MODE_FREERUN == mode) { + hri_adc_set_CTRLC_FREERUN_bit(device->hw); + } else { + hri_adc_clear_CTRLC_FREERUN_bit(device->hw); + } +} + +/** + * \brief Set conversion mode + */ +void _adc_dma_set_conversion_mode(struct _adc_dma_device *const device, const enum adc_conversion_mode mode) +{ + if (ADC_CONVERSION_MODE_FREERUN == mode) { + hri_adc_set_CTRLC_FREERUN_bit(device->hw); + } else { + hri_adc_clear_CTRLC_FREERUN_bit(device->hw); + } +} + +/** + * \brief Set differential mode + */ +void _adc_sync_set_channel_differential_mode(struct _adc_sync_device *const device, const uint8_t channel, + const enum adc_differential_mode mode) +{ + (void)channel; + + if (ADC_DIFFERENTIAL_MODE_DIFFERENTIAL == mode) { + hri_adc_set_CTRLC_DIFFMODE_bit(device->hw); + } else { + hri_adc_clear_CTRLC_DIFFMODE_bit(device->hw); + } +} + +/** + * \brief Set differential mode + */ +void _adc_async_set_channel_differential_mode(struct _adc_async_device *const device, const uint8_t channel, + const enum adc_differential_mode mode) +{ + (void)channel; + + if (ADC_DIFFERENTIAL_MODE_DIFFERENTIAL == mode) { + hri_adc_set_CTRLC_DIFFMODE_bit(device->hw); + } else { + hri_adc_clear_CTRLC_DIFFMODE_bit(device->hw); + } +} + +/** + * \brief Set differential mode + */ +void _adc_dma_set_channel_differential_mode(struct _adc_dma_device *const device, const uint8_t channel, + const enum adc_differential_mode mode) +{ + (void)channel; + + if (ADC_DIFFERENTIAL_MODE_DIFFERENTIAL == mode) { + hri_adc_set_CTRLC_DIFFMODE_bit(device->hw); + } else { + hri_adc_clear_CTRLC_DIFFMODE_bit(device->hw); + } +} + +/** + * \brief Set window mode + */ +void _adc_sync_set_window_mode(struct _adc_sync_device *const device, const adc_window_mode_t mode) +{ + hri_adc_write_CTRLC_WINMODE_bf(device->hw, mode); +} + +/** + * \brief Set window mode + */ +void _adc_async_set_window_mode(struct _adc_async_device *const device, const adc_window_mode_t mode) +{ + hri_adc_write_CTRLC_WINMODE_bf(device->hw, mode); +} + +/** + * \brief Set window mode + */ +void _adc_dma_set_window_mode(struct _adc_dma_device *const device, const adc_window_mode_t mode) +{ + hri_adc_write_CTRLC_WINMODE_bf(device->hw, mode); +} + +/** + * \brief Retrieve threshold state + */ +void _adc_sync_get_threshold_state(const struct _adc_sync_device *const device, adc_threshold_status_t *const state) +{ + *state = hri_adc_get_interrupt_WINMON_bit(device->hw); +} + +/** + * \brief Retrieve threshold state + */ +void _adc_async_get_threshold_state(const struct _adc_async_device *const device, adc_threshold_status_t *const state) +{ + *state = hri_adc_get_interrupt_WINMON_bit(device->hw); +} + +/** + * \brief Retrieve threshold state + */ +void _adc_dma_get_threshold_state(const struct _adc_dma_device *const device, adc_threshold_status_t *const state) +{ + *state = hri_adc_get_interrupt_WINMON_bit(device->hw); +} + +/** + * \brief Enable/disable ADC channel interrupt + */ +void _adc_async_set_irq_state(struct _adc_async_device *const device, const uint8_t channel, + const enum _adc_async_callback_type type, const bool state) +{ + (void)channel; + + void *const hw = device->hw; + + if (ADC_ASYNC_DEVICE_MONITOR_CB == type) { + hri_adc_write_INTEN_WINMON_bit(hw, state); + } else if (ADC_ASYNC_DEVICE_ERROR_CB == type) { + hri_adc_write_INTEN_OVERRUN_bit(hw, state); + } else if (ADC_ASYNC_DEVICE_CONVERT_CB == type) { + hri_adc_write_INTEN_RESRDY_bit(hw, state); + } +} + +/** + * \brief Retrieve ADC sync helper functions + */ +void *_adc_get_adc_sync(void) +{ + return (void *)NULL; +} + +/** + * \brief Retrieve ADC async helper functions + */ +void *_adc_get_adc_async(void) +{ + return (void *)NULL; +} + +/** + * \brief Set ADC reference source + * + * \param[in] hw The pointer to hardware instance + * \param[in] reference The reference to set + */ +static void _adc_set_reference_source(void *const hw, const adc_reference_t reference) +{ + bool enabled = hri_adc_get_CTRLA_ENABLE_bit(hw); + + hri_adc_clear_CTRLA_ENABLE_bit(hw); + hri_adc_write_REFCTRL_REFSEL_bf(hw, reference); + + if (enabled) { + hri_adc_set_CTRLA_ENABLE_bit(hw); + } +} diff --git a/watch-library/hpl/adc/hpl_adc_base.h b/watch-library/hpl/adc/hpl_adc_base.h new file mode 100644 index 00000000..e9b95283 --- /dev/null +++ b/watch-library/hpl/adc/hpl_adc_base.h @@ -0,0 +1,72 @@ +/** + * \file + * + * \brief ADC related functionality declaration. + * + * Copyright (c) 2016-2018 Microchip Technology Inc. and its subsidiaries. + * + * \asf_license_start + * + * \page License + * + * Subject to your compliance with these terms, you may use Microchip + * software and any derivatives exclusively with Microchip products. + * It is your responsibility to comply with third party license terms applicable + * to your use of third party software (including open source software) that + * may accompany Microchip software. + * + * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, + * WHETHER EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, + * INCLUDING ANY IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, + * AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE + * LIABLE FOR ANY INDIRECT, SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL + * LOSS, DAMAGE, COST OR EXPENSE OF ANY KIND WHATSOEVER RELATED TO THE + * SOFTWARE, HOWEVER CAUSED, EVEN IF MICROCHIP HAS BEEN ADVISED OF THE + * POSSIBILITY OR THE DAMAGES ARE FORESEEABLE. TO THE FULLEST EXTENT + * ALLOWED BY LAW, MICROCHIP'S TOTAL LIABILITY ON ALL CLAIMS IN ANY WAY + * RELATED TO THIS SOFTWARE WILL NOT EXCEED THE AMOUNT OF FEES, IF ANY, + * THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR THIS SOFTWARE. + * + * \asf_license_stop + * + */ + +#ifndef _HPL_ADC_ADC_H_INCLUDED +#define _HPL_ADC_ADC_H_INCLUDED + +#include +#include + +/** + * \addtogroup HPL ADC + * + * \section hpl_adc_rev Revision History + * - v1.0.0 Initial Release + * + *@{ + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \name HPL functions + */ +//@{ + +/** + * \brief Retrieve ADC helper functions + * + * \return A pointer to set of ADC helper functions + */ +void *_adc_get_adc_sync(void); +void *_adc_get_adc_async(void); + +//@} + +#ifdef __cplusplus +} +#endif +/**@}*/ +#endif /* _HPL_USART_UART_H_INCLUDED */ diff --git a/watch-library/hpl/core/hpl_core_m0plus_base.c b/watch-library/hpl/core/hpl_core_m0plus_base.c new file mode 100644 index 00000000..1d32300a --- /dev/null +++ b/watch-library/hpl/core/hpl_core_m0plus_base.c @@ -0,0 +1,200 @@ +/** + * \file + * + * \brief Core related functionality implementation. + * + * Copyright (c) 2014-2018 Microchip Technology Inc. and its subsidiaries. + * + * \asf_license_start + * + * \page License + * + * Subject to your compliance with these terms, you may use Microchip + * software and any derivatives exclusively with Microchip products. + * It is your responsibility to comply with third party license terms applicable + * to your use of third party software (including open source software) that + * may accompany Microchip software. + * + * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, + * WHETHER EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, + * INCLUDING ANY IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, + * AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE + * LIABLE FOR ANY INDIRECT, SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL + * LOSS, DAMAGE, COST OR EXPENSE OF ANY KIND WHATSOEVER RELATED TO THE + * SOFTWARE, HOWEVER CAUSED, EVEN IF MICROCHIP HAS BEEN ADVISED OF THE + * POSSIBILITY OR THE DAMAGES ARE FORESEEABLE. TO THE FULLEST EXTENT + * ALLOWED BY LAW, MICROCHIP'S TOTAL LIABILITY ON ALL CLAIMS IN ANY WAY + * RELATED TO THIS SOFTWARE WILL NOT EXCEED THE AMOUNT OF FEES, IF ANY, + * THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR THIS SOFTWARE. + * + * \asf_license_stop + * + */ + +#include +#include +#include +#include +#include +#ifndef _UNIT_TEST_ +#include +#endif +#include +#include + +#ifndef CONF_CPU_FREQUENCY +#define CONF_CPU_FREQUENCY 1000000 +#endif + +#if CONF_CPU_FREQUENCY < 1000 +#define CPU_FREQ_POWER 3 +#elif CONF_CPU_FREQUENCY < 10000 +#define CPU_FREQ_POWER 4 +#elif CONF_CPU_FREQUENCY < 100000 +#define CPU_FREQ_POWER 5 +#elif CONF_CPU_FREQUENCY < 1000000 +#define CPU_FREQ_POWER 6 +#elif CONF_CPU_FREQUENCY < 10000000 +#define CPU_FREQ_POWER 7 +#elif CONF_CPU_FREQUENCY < 100000000 +#define CPU_FREQ_POWER 8 +#endif + +/** + * \brief The array of interrupt handlers + */ +struct _irq_descriptor *_irq_table[PERIPH_COUNT_IRQn]; + +/** + * \brief Reset MCU + */ +void _reset_mcu(void) +{ + NVIC_SystemReset(); +} + +/** + * \brief Put MCU to sleep + */ +void _go_to_sleep(void) +{ + __DSB(); + __WFI(); +} + +/** + * \brief Retrieve current IRQ number + */ +uint8_t _irq_get_current(void) +{ + return (uint8_t)__get_IPSR() - 16; +} + +/** + * \brief Disable the given IRQ + */ +void _irq_disable(uint8_t n) +{ + NVIC_DisableIRQ((IRQn_Type)n); +} + +/** + * \brief Set the given IRQ + */ +void _irq_set(uint8_t n) +{ + NVIC_SetPendingIRQ((IRQn_Type)n); +} + +/** + * \brief Clear the given IRQ + */ +void _irq_clear(uint8_t n) +{ + NVIC_ClearPendingIRQ((IRQn_Type)n); +} + +/** + * \brief Enable the given IRQ + */ +void _irq_enable(uint8_t n) +{ + NVIC_EnableIRQ((IRQn_Type)n); +} + +/** + * \brief Register IRQ handler + */ +void _irq_register(const uint8_t n, struct _irq_descriptor *const irq) +{ + ASSERT(n < PERIPH_COUNT_IRQn); + + _irq_table[n] = irq; +} + +/** + * \brief Default interrupt handler for unused IRQs. + */ +void Default_Handler(void) +{ + while (1) { + } +} + +/** + * \brief Retrieve the amount of cycles to delay for the given amount of us + */ +static inline uint32_t _get_cycles_for_us_internal(const uint16_t us, const uint32_t freq, const uint8_t power) +{ + switch (power) { + case 8: + return (us * (freq / 100000) - 1) / 10 + 1; + case 7: + return (us * (freq / 10000) - 1) / 100 + 1; + case 6: + return (us * (freq / 1000) - 1) / 1000 + 1; + case 5: + return (us * (freq / 100) - 1) / 10000 + 1; + case 4: + return (us * (freq / 10) - 1) / 100000 + 1; + default: + return (us * freq - 1) / 1000000 + 1; + } +} + +/** + * \brief Retrieve the amount of cycles to delay for the given amount of us + */ +uint32_t _get_cycles_for_us(const uint16_t us) +{ + return _get_cycles_for_us_internal(us, CONF_CPU_FREQUENCY, CPU_FREQ_POWER); +} + +/** + * \brief Retrieve the amount of cycles to delay for the given amount of ms + */ +static inline uint32_t _get_cycles_for_ms_internal(const uint16_t ms, const uint32_t freq, const uint8_t power) +{ + switch (power) { + case 8: + return (ms * (freq / 100000)) * 100; + case 7: + return (ms * (freq / 10000)) * 10; + case 6: + return (ms * (freq / 1000)); + case 5: + return (ms * (freq / 100) - 1) / 10 + 1; + case 4: + return (ms * (freq / 10) - 1) / 100 + 1; + default: + return (ms * freq - 1) / 1000 + 1; + } +} + +/** + * \brief Retrieve the amount of cycles to delay for the given amount of ms + */ +uint32_t _get_cycles_for_ms(const uint16_t ms) +{ + return _get_cycles_for_ms_internal(ms, CONF_CPU_FREQUENCY, CPU_FREQ_POWER); +} diff --git a/watch-library/hpl/core/hpl_core_port.h b/watch-library/hpl/core/hpl_core_port.h new file mode 100644 index 00000000..3f3e8f28 --- /dev/null +++ b/watch-library/hpl/core/hpl_core_port.h @@ -0,0 +1,61 @@ +/** + * \file + * + * \brief Core related functionality implementation. + * + * Copyright (c) 2015-2018 Microchip Technology Inc. and its subsidiaries. + * + * \asf_license_start + * + * \page License + * + * Subject to your compliance with these terms, you may use Microchip + * software and any derivatives exclusively with Microchip products. + * It is your responsibility to comply with third party license terms applicable + * to your use of third party software (including open source software) that + * may accompany Microchip software. + * + * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, + * WHETHER EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, + * INCLUDING ANY IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, + * AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE + * LIABLE FOR ANY INDIRECT, SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL + * LOSS, DAMAGE, COST OR EXPENSE OF ANY KIND WHATSOEVER RELATED TO THE + * SOFTWARE, HOWEVER CAUSED, EVEN IF MICROCHIP HAS BEEN ADVISED OF THE + * POSSIBILITY OR THE DAMAGES ARE FORESEEABLE. TO THE FULLEST EXTENT + * ALLOWED BY LAW, MICROCHIP'S TOTAL LIABILITY ON ALL CLAIMS IN ANY WAY + * RELATED TO THIS SOFTWARE WILL NOT EXCEED THE AMOUNT OF FEES, IF ANY, + * THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR THIS SOFTWARE. + * + * \asf_license_stop + * + */ + +#ifndef _HPL_CORE_PORT_H_INCLUDED +#define _HPL_CORE_PORT_H_INCLUDED + +#include + +/* It's possible to include this file in ARM ASM files (e.g., in FreeRTOS IAR + * portable implement, portasm.s -> FreeRTOSConfig.h -> hpl_core_port.h), + * there will be assembling errors. + * So the following things are not included for assembling. + */ +#if !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__)) + +#ifndef _UNIT_TEST_ +#include +#endif + +/** + * \brief Check if it's in ISR handling + * \return \c true if it's in ISR + */ +static inline bool _is_in_isr(void) +{ + return (SCB->ICSR & SCB_ICSR_VECTACTIVE_Msk); +} + +#endif /* !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__)) */ + +#endif /* _HPL_CORE_PORT_H_INCLUDED */ diff --git a/watch-library/hpl/core/hpl_init.c b/watch-library/hpl/core/hpl_init.c new file mode 100644 index 00000000..900cf420 --- /dev/null +++ b/watch-library/hpl/core/hpl_init.c @@ -0,0 +1,74 @@ +/** + * \file + * + * \brief HPL initialization related functionality implementation. + * + * Copyright (c) 2014-2018 Microchip Technology Inc. and its subsidiaries. + * + * \asf_license_start + * + * \page License + * + * Subject to your compliance with these terms, you may use Microchip + * software and any derivatives exclusively with Microchip products. + * It is your responsibility to comply with third party license terms applicable + * to your use of third party software (including open source software) that + * may accompany Microchip software. + * + * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, + * WHETHER EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, + * INCLUDING ANY IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, + * AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE + * LIABLE FOR ANY INDIRECT, SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL + * LOSS, DAMAGE, COST OR EXPENSE OF ANY KIND WHATSOEVER RELATED TO THE + * SOFTWARE, HOWEVER CAUSED, EVEN IF MICROCHIP HAS BEEN ADVISED OF THE + * POSSIBILITY OR THE DAMAGES ARE FORESEEABLE. TO THE FULLEST EXTENT + * ALLOWED BY LAW, MICROCHIP'S TOTAL LIABILITY ON ALL CLAIMS IN ANY WAY + * RELATED TO THIS SOFTWARE WILL NOT EXCEED THE AMOUNT OF FEES, IF ANY, + * THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR THIS SOFTWARE. + * + * \asf_license_stop + * + */ + +#include +#include +#include +#include + +#include +#include + +/* Referenced GCLKs (out of 0~4), should be initialized firstly + */ +#define _GCLK_INIT_1ST 0x00000000 +/* Not referenced GCLKs, initialized last */ +#define _GCLK_INIT_LAST 0x0000001F + +/** + * \brief Initialize the hardware abstraction layer + */ +void _init_chip(void) +{ + hri_nvmctrl_set_CTRLB_RWS_bf(NVMCTRL, CONF_NVM_WAIT_STATE); + + _set_performance_level(2); + + _osc32kctrl_init_sources(); + _oscctrl_init_sources(); + _mclk_init(); +#if _GCLK_INIT_1ST + _gclk_init_generators_by_fref(_GCLK_INIT_1ST); +#endif + _oscctrl_init_referenced_generators(); + _gclk_init_generators_by_fref(_GCLK_INIT_LAST); + +#if CONF_DMAC_ENABLE + hri_mclk_set_AHBMASK_DMAC_bit(MCLK); + _dma_init(); +#endif + +#if (CONF_PORT_EVCTRL_PORT_0 | CONF_PORT_EVCTRL_PORT_1 | CONF_PORT_EVCTRL_PORT_2 | CONF_PORT_EVCTRL_PORT_3) + _port_event_init(); +#endif +} diff --git a/watch-library/hpl/dmac/hpl_dmac.c b/watch-library/hpl/dmac/hpl_dmac.c new file mode 100644 index 00000000..c12e0254 --- /dev/null +++ b/watch-library/hpl/dmac/hpl_dmac.c @@ -0,0 +1,244 @@ + +/** + * \file + * + * \brief Generic DMAC related functionality. + * + * Copyright (c) 2016-2019 Microchip Technology Inc. and its subsidiaries. + * + * \asf_license_start + * + * \page License + * + * Subject to your compliance with these terms, you may use Microchip + * software and any derivatives exclusively with Microchip products. + * It is your responsibility to comply with third party license terms applicable + * to your use of third party software (including open source software) that + * may accompany Microchip software. + * + * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, + * WHETHER EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, + * INCLUDING ANY IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, + * AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE + * LIABLE FOR ANY INDIRECT, SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL + * LOSS, DAMAGE, COST OR EXPENSE OF ANY KIND WHATSOEVER RELATED TO THE + * SOFTWARE, HOWEVER CAUSED, EVEN IF MICROCHIP HAS BEEN ADVISED OF THE + * POSSIBILITY OR THE DAMAGES ARE FORESEEABLE. TO THE FULLEST EXTENT + * ALLOWED BY LAW, MICROCHIP'S TOTAL LIABILITY ON ALL CLAIMS IN ANY WAY + * RELATED TO THIS SOFTWARE WILL NOT EXCEED THE AMOUNT OF FEES, IF ANY, + * THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR THIS SOFTWARE. + * + * \asf_license_stop + * + */ +#include +#include +#include +#include +#include +#include + +#if CONF_DMAC_ENABLE + +/* Section containing first descriptors for all DMAC channels */ +COMPILER_ALIGNED(16) +DmacDescriptor _descriptor_section[DMAC_CH_NUM] SECTION_DMAC_DESCRIPTOR; + +/* Section containing current descriptors for all DMAC channels */ +COMPILER_ALIGNED(16) +DmacDescriptor _write_back_section[DMAC_CH_NUM] SECTION_DMAC_DESCRIPTOR; + +/* Array containing callbacks for DMAC channels */ +static struct _dma_resource _resources[DMAC_CH_NUM]; + +/* This macro DMAC configuration */ +#define DMAC_CHANNEL_CFG(i, n) \ + {(CONF_DMAC_RUNSTDBY_##n << DMAC_CHCTRLA_RUNSTDBY_Pos) | (CONF_DMAC_ENABLE_##n << DMAC_CHCTRLA_ENABLE_Pos), \ + DMAC_CHCTRLB_TRIGACT(CONF_DMAC_TRIGACT_##n) | DMAC_CHCTRLB_TRIGSRC(CONF_DMAC_TRIGSRC_##n) \ + | DMAC_CHCTRLB_LVL(CONF_DMAC_LVL_##n) | (CONF_DMAC_EVOE_##n << DMAC_CHCTRLB_EVOE_Pos) \ + | (CONF_DMAC_EVIE_##n << DMAC_CHCTRLB_EVIE_Pos) | DMAC_CHCTRLB_EVACT(CONF_DMAC_EVACT_##n), \ + DMAC_BTCTRL_STEPSIZE(CONF_DMAC_STEPSIZE_##n) | (CONF_DMAC_STEPSEL_##n << DMAC_BTCTRL_STEPSEL_Pos) \ + | (CONF_DMAC_DSTINC_##n << DMAC_BTCTRL_DSTINC_Pos) | (CONF_DMAC_SRCINC_##n << DMAC_BTCTRL_SRCINC_Pos) \ + | DMAC_BTCTRL_BEATSIZE(CONF_DMAC_BEATSIZE_##n) | DMAC_BTCTRL_BLOCKACT(CONF_DMAC_BLOCKACT_##n) \ + | DMAC_BTCTRL_EVOSEL(CONF_DMAC_EVOSEL_##n)}, + +/* DMAC channel configuration */ +struct dmac_channel_cfg { + uint8_t ctrla; + uint32_t ctrlb; + uint16_t btctrl; +}; + +/* DMAC channel configurations */ +const static struct dmac_channel_cfg _cfgs[] = {REPEAT_MACRO(DMAC_CHANNEL_CFG, i, DMAC_CH_NUM)}; + +/** + * \brief Initialize DMAC + */ +int32_t _dma_init(void) +{ + uint8_t i = 0; + + hri_dmac_clear_CTRL_DMAENABLE_bit(DMAC); + hri_dmac_clear_CTRL_CRCENABLE_bit(DMAC); + hri_dmac_set_CHCTRLA_SWRST_bit(DMAC); + + hri_dmac_write_CTRL_reg(DMAC, + (CONF_DMAC_LVLEN0 << DMAC_CTRL_LVLEN0_Pos) | (CONF_DMAC_LVLEN1 << DMAC_CTRL_LVLEN1_Pos) + | (CONF_DMAC_LVLEN2 << DMAC_CTRL_LVLEN2_Pos) + | (CONF_DMAC_LVLEN3 << DMAC_CTRL_LVLEN3_Pos)); + hri_dmac_write_DBGCTRL_DBGRUN_bit(DMAC, CONF_DMAC_DBGRUN); + + hri_dmac_write_QOSCTRL_reg(DMAC, + DMAC_QOSCTRL_WRBQOS(CONF_DMAC_WRBQOS) | DMAC_QOSCTRL_FQOS(CONF_DMAC_FQOS) + | DMAC_QOSCTRL_DQOS(CONF_DMAC_DQOS)); + + hri_dmac_write_PRICTRL0_reg( + DMAC, + DMAC_PRICTRL0_LVLPRI0(CONF_DMAC_LVLPRI0) | DMAC_PRICTRL0_LVLPRI1(CONF_DMAC_LVLPRI1) + | DMAC_PRICTRL0_LVLPRI2(CONF_DMAC_LVLPRI2) | DMAC_PRICTRL0_LVLPRI3(CONF_DMAC_LVLPRI3) + | (CONF_DMAC_RRLVLEN0 << DMAC_PRICTRL0_RRLVLEN0_Pos) | (CONF_DMAC_RRLVLEN1 << DMAC_PRICTRL0_RRLVLEN1_Pos) + | (CONF_DMAC_RRLVLEN2 << DMAC_PRICTRL0_RRLVLEN2_Pos) | (CONF_DMAC_RRLVLEN3 << DMAC_PRICTRL0_RRLVLEN3_Pos)); + hri_dmac_write_BASEADDR_reg(DMAC, (uint32_t)_descriptor_section); + hri_dmac_write_WRBADDR_reg(DMAC, (uint32_t)_write_back_section); + + for (; i < DMAC_CH_NUM; i++) { + hri_dmac_write_CHID_reg(DMAC, i); + + hri_dmac_write_CHCTRLA_RUNSTDBY_bit(DMAC, _cfgs[i].ctrla & DMAC_CHCTRLA_RUNSTDBY); + + hri_dmac_write_CHCTRLB_reg(DMAC, _cfgs[i].ctrlb); + hri_dmacdescriptor_write_BTCTRL_reg(&_descriptor_section[i], _cfgs[i].btctrl); + hri_dmacdescriptor_write_DESCADDR_reg(&_descriptor_section[i], 0x0); + } + + NVIC_DisableIRQ(DMAC_IRQn); + NVIC_ClearPendingIRQ(DMAC_IRQn); + NVIC_EnableIRQ(DMAC_IRQn); + + hri_dmac_set_CTRL_DMAENABLE_bit(DMAC); + + return ERR_NONE; +} + +/** + * \brief Enable/disable DMA interrupt + */ +void _dma_set_irq_state(const uint8_t channel, const enum _dma_callback_type type, const bool state) +{ + hri_dmac_write_CHID_reg(DMAC, channel); + + if (DMA_TRANSFER_COMPLETE_CB == type) { + hri_dmac_write_CHINTEN_TCMPL_bit(DMAC, state); + } else if (DMA_TRANSFER_ERROR_CB == type) { + hri_dmac_write_CHINTEN_TERR_bit(DMAC, state); + } +} + +int32_t _dma_set_destination_address(const uint8_t channel, const void *const dst) +{ + hri_dmacdescriptor_write_DSTADDR_reg(&_descriptor_section[channel], (uint32_t)dst); + + return ERR_NONE; +} + +int32_t _dma_set_source_address(const uint8_t channel, const void *const src) +{ + hri_dmacdescriptor_write_SRCADDR_reg(&_descriptor_section[channel], (uint32_t)src); + + return ERR_NONE; +} + +int32_t _dma_set_next_descriptor(const uint8_t current_channel, const uint8_t next_channel) +{ + hri_dmacdescriptor_write_DESCADDR_reg(&_descriptor_section[current_channel], + (uint32_t)&_descriptor_section[next_channel]); + + return ERR_NONE; +} + +int32_t _dma_srcinc_enable(const uint8_t channel, const bool enable) +{ + hri_dmacdescriptor_write_BTCTRL_SRCINC_bit(&_descriptor_section[channel], enable); + + return ERR_NONE; +} + +int32_t _dma_set_data_amount(const uint8_t channel, const uint32_t amount) +{ + uint32_t address = hri_dmacdescriptor_read_DSTADDR_reg(&_descriptor_section[channel]); + uint8_t beat_size = hri_dmacdescriptor_read_BTCTRL_BEATSIZE_bf(&_descriptor_section[channel]); + + if (hri_dmacdescriptor_get_BTCTRL_DSTINC_bit(&_descriptor_section[channel])) { + hri_dmacdescriptor_write_DSTADDR_reg(&_descriptor_section[channel], address + amount * (1 << beat_size)); + } + + address = hri_dmacdescriptor_read_SRCADDR_reg(&_descriptor_section[channel]); + + if (hri_dmacdescriptor_get_BTCTRL_SRCINC_bit(&_descriptor_section[channel])) { + hri_dmacdescriptor_write_SRCADDR_reg(&_descriptor_section[channel], address + amount * (1 << beat_size)); + } + + hri_dmacdescriptor_write_BTCNT_reg(&_descriptor_section[channel], amount); + + return ERR_NONE; +} + +int32_t _dma_enable_transaction(const uint8_t channel, const bool software_trigger) +{ + hri_dmac_write_CHID_reg(DMAC, channel); + hri_dmacdescriptor_set_BTCTRL_VALID_bit(&_descriptor_section[channel]); + hri_dmac_set_CHCTRLA_ENABLE_bit(DMAC); + if (software_trigger) { + hri_dmac_set_SWTRIGCTRL_reg(DMAC, 1 << channel); + } + + return ERR_NONE; +} + +int32_t _dma_get_channel_resource(struct _dma_resource **resource, const uint8_t channel) +{ + *resource = &_resources[channel]; + + return ERR_NONE; +} + +int32_t _dma_dstinc_enable(const uint8_t channel, const bool enable) +{ + hri_dmacdescriptor_write_BTCTRL_DSTINC_bit(&_descriptor_section[channel], enable); + + return ERR_NONE; +} + +/** + * \internal DMAC interrupt handler + */ +static inline void _dmac_handler(void) +{ + uint8_t channel = hri_dmac_read_INTPEND_ID_bf(DMAC); + uint8_t current_channel = hri_dmac_read_CHID_reg(DMAC); + uint8_t flag_status; + struct _dma_resource *tmp_resource = &_resources[channel]; + + hri_dmac_write_CHID_reg(DMAC, channel); + flag_status = hri_dmac_get_CHINTFLAG_reg(DMAC, DMAC_CHINTFLAG_MASK); + + if (flag_status & DMAC_CHINTFLAG_TERR) { + hri_dmac_clear_CHINTFLAG_TERR_bit(DMAC); + tmp_resource->dma_cb.error(tmp_resource); + } else if (flag_status & DMAC_CHINTFLAG_TCMPL) { + hri_dmac_clear_CHINTFLAG_TCMPL_bit(DMAC); + tmp_resource->dma_cb.transfer_done(tmp_resource); + } + hri_dmac_write_CHID_reg(DMAC, current_channel); +} + +/** + * \brief DMAC interrupt handler + */ +void DMAC_Handler(void) +{ + _dmac_handler(); +} + +#endif /* CONF_DMAC_ENABLE */ diff --git a/watch-library/hpl/eic/hpl_eic.c b/watch-library/hpl/eic/hpl_eic.c new file mode 100644 index 00000000..3b473ef5 --- /dev/null +++ b/watch-library/hpl/eic/hpl_eic.c @@ -0,0 +1,255 @@ + +/** + * \file + * + * \brief EIC related functionality implementation. + * + * Copyright (c) 2015-2018 Microchip Technology Inc. and its subsidiaries. + * + * \asf_license_start + * + * \page License + * + * Subject to your compliance with these terms, you may use Microchip + * software and any derivatives exclusively with Microchip products. + * It is your responsibility to comply with third party license terms applicable + * to your use of third party software (including open source software) that + * may accompany Microchip software. + * + * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, + * WHETHER EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, + * INCLUDING ANY IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, + * AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE + * LIABLE FOR ANY INDIRECT, SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL + * LOSS, DAMAGE, COST OR EXPENSE OF ANY KIND WHATSOEVER RELATED TO THE + * SOFTWARE, HOWEVER CAUSED, EVEN IF MICROCHIP HAS BEEN ADVISED OF THE + * POSSIBILITY OR THE DAMAGES ARE FORESEEABLE. TO THE FULLEST EXTENT + * ALLOWED BY LAW, MICROCHIP'S TOTAL LIABILITY ON ALL CLAIMS IN ANY WAY + * RELATED TO THIS SOFTWARE WILL NOT EXCEED THE AMOUNT OF FEES, IF ANY, + * THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR THIS SOFTWARE. + * + * \asf_license_stop + * + */ +#include +#include +#include +#include +#include +#include + +#ifdef __MINGW32__ +#define ffs __builtin_ffs +#endif +#if defined(__CC_ARM) || defined(__ICCARM__) +/* Find the first bit set */ +static int ffs(int v) +{ + int i, bit = 1; + for (i = 0; i < sizeof(int) * 8; i++) { + if (v & bit) { + return i + 1; + } + bit <<= 1; + } + return 0; +} +#endif + +/** + * \brief Invalid external interrupt and pin numbers + */ +#define INVALID_EXTINT_NUMBER 0xFF +#define INVALID_PIN_NUMBER 0xFFFFFFFF + +#ifndef CONFIG_EIC_EXTINT_MAP +/** Dummy mapping to pass compiling. */ +#define CONFIG_EIC_EXTINT_MAP \ + { \ + INVALID_EXTINT_NUMBER, INVALID_PIN_NUMBER \ + } +#endif + +#define EXT_IRQ_AMOUNT 3 + +/** + * \brief EXTINTx and pin number map + */ +struct _eic_map { + uint8_t extint; + uint32_t pin; +}; + +/** + * \brief PIN and EXTINT map for enabled external interrupts + */ +static const struct _eic_map _map[] = {CONFIG_EIC_EXTINT_MAP}; + +/** + * \brief The callback to upper layer's interrupt processing routine + */ +static void (*callback)(const uint32_t pin); + +static void _ext_irq_handler(void); + +/** + * \brief Initialize external interrupt module + */ +int32_t _ext_irq_init(void (*cb)(const uint32_t pin)) +{ + if (!hri_eic_is_syncing(EIC, EIC_SYNCBUSY_SWRST)) { + if (hri_eic_get_CTRLA_reg(EIC, EIC_CTRLA_ENABLE)) { + hri_eic_clear_CTRLA_ENABLE_bit(EIC); + hri_eic_wait_for_sync(EIC, EIC_SYNCBUSY_ENABLE); + } + hri_eic_write_CTRLA_reg(EIC, EIC_CTRLA_SWRST); + } + hri_eic_wait_for_sync(EIC, EIC_SYNCBUSY_SWRST); + + hri_eic_write_CTRLA_CKSEL_bit(EIC, CONF_EIC_CKSEL); + + hri_eic_write_NMICTRL_reg(EIC, + (CONF_EIC_NMIFILTEN << EIC_NMICTRL_NMIFILTEN_Pos) + | EIC_NMICTRL_NMISENSE(CONF_EIC_NMISENSE) | EIC_ASYNCH_ASYNCH(CONF_EIC_NMIASYNCH) + | 0); + + hri_eic_write_EVCTRL_reg(EIC, + (CONF_EIC_EXTINTEO0 << 0) | (CONF_EIC_EXTINTEO1 << 1) | (CONF_EIC_EXTINTEO2 << 2) + | (CONF_EIC_EXTINTEO3 << 3) | (CONF_EIC_EXTINTEO4 << 4) | (CONF_EIC_EXTINTEO5 << 5) + | (CONF_EIC_EXTINTEO6 << 6) | (CONF_EIC_EXTINTEO7 << 7) | (CONF_EIC_EXTINTEO8 << 8) + | (CONF_EIC_EXTINTEO9 << 9) | (CONF_EIC_EXTINTEO10 << 10) | (CONF_EIC_EXTINTEO11 << 11) + | (CONF_EIC_EXTINTEO12 << 12) | (CONF_EIC_EXTINTEO13 << 13) + | (CONF_EIC_EXTINTEO14 << 14) | (CONF_EIC_EXTINTEO15 << 15) | 0); + hri_eic_write_ASYNCH_reg(EIC, + (CONF_EIC_ASYNCH0 << 0) | (CONF_EIC_ASYNCH1 << 1) | (CONF_EIC_ASYNCH2 << 2) + | (CONF_EIC_ASYNCH3 << 3) | (CONF_EIC_ASYNCH4 << 4) | (CONF_EIC_ASYNCH5 << 5) + | (CONF_EIC_ASYNCH6 << 6) | (CONF_EIC_ASYNCH7 << 7) | (CONF_EIC_ASYNCH8 << 8) + | (CONF_EIC_ASYNCH9 << 9) | (CONF_EIC_ASYNCH10 << 10) | (CONF_EIC_ASYNCH11 << 11) + | (CONF_EIC_ASYNCH12 << 12) | (CONF_EIC_ASYNCH13 << 13) | (CONF_EIC_ASYNCH14 << 14) + | (CONF_EIC_ASYNCH15 << 15) | 0); + + hri_eic_write_CONFIG_reg(EIC, + 0, + (CONF_EIC_FILTEN0 << EIC_CONFIG_FILTEN0_Pos) | EIC_CONFIG_SENSE0(CONF_EIC_SENSE0) + | (CONF_EIC_FILTEN1 << EIC_CONFIG_FILTEN1_Pos) | EIC_CONFIG_SENSE1(CONF_EIC_SENSE1) + | (CONF_EIC_FILTEN2 << EIC_CONFIG_FILTEN2_Pos) | EIC_CONFIG_SENSE2(CONF_EIC_SENSE2) + | (CONF_EIC_FILTEN3 << EIC_CONFIG_FILTEN3_Pos) | EIC_CONFIG_SENSE3(CONF_EIC_SENSE3) + | (CONF_EIC_FILTEN4 << EIC_CONFIG_FILTEN4_Pos) | EIC_CONFIG_SENSE4(CONF_EIC_SENSE4) + | (CONF_EIC_FILTEN5 << EIC_CONFIG_FILTEN5_Pos) | EIC_CONFIG_SENSE5(CONF_EIC_SENSE5) + | (CONF_EIC_FILTEN6 << EIC_CONFIG_FILTEN6_Pos) | EIC_CONFIG_SENSE6(CONF_EIC_SENSE6) + | (CONF_EIC_FILTEN7 << EIC_CONFIG_FILTEN7_Pos) | EIC_CONFIG_SENSE7(CONF_EIC_SENSE7) + | 0); + + hri_eic_write_CONFIG_reg(EIC, + 1, + (CONF_EIC_FILTEN8 << EIC_CONFIG_FILTEN0_Pos) | EIC_CONFIG_SENSE0(CONF_EIC_SENSE8) + | (CONF_EIC_FILTEN9 << EIC_CONFIG_FILTEN1_Pos) | EIC_CONFIG_SENSE1(CONF_EIC_SENSE9) + | (CONF_EIC_FILTEN10 << EIC_CONFIG_FILTEN2_Pos) | EIC_CONFIG_SENSE2(CONF_EIC_SENSE10) + | (CONF_EIC_FILTEN11 << EIC_CONFIG_FILTEN3_Pos) | EIC_CONFIG_SENSE3(CONF_EIC_SENSE11) + | (CONF_EIC_FILTEN12 << EIC_CONFIG_FILTEN4_Pos) | EIC_CONFIG_SENSE4(CONF_EIC_SENSE12) + | (CONF_EIC_FILTEN13 << EIC_CONFIG_FILTEN5_Pos) | EIC_CONFIG_SENSE5(CONF_EIC_SENSE13) + | (CONF_EIC_FILTEN14 << EIC_CONFIG_FILTEN6_Pos) | EIC_CONFIG_SENSE6(CONF_EIC_SENSE14) + | (CONF_EIC_FILTEN15 << EIC_CONFIG_FILTEN7_Pos) | EIC_CONFIG_SENSE7(CONF_EIC_SENSE15) + | 0); + + hri_eic_set_CTRLA_ENABLE_bit(EIC); + NVIC_DisableIRQ(EIC_IRQn); + NVIC_ClearPendingIRQ(EIC_IRQn); + NVIC_EnableIRQ(EIC_IRQn); + + callback = cb; + + return ERR_NONE; +} + +/** + * \brief De-initialize external interrupt module + */ +int32_t _ext_irq_deinit(void) +{ + NVIC_DisableIRQ(EIC_IRQn); + callback = NULL; + + hri_eic_clear_CTRLA_ENABLE_bit(EIC); + hri_eic_set_CTRLA_SWRST_bit(EIC); + + return ERR_NONE; +} + +/** + * \brief Enable / disable external irq + */ +int32_t _ext_irq_enable(const uint32_t pin, const bool enable) +{ + uint8_t extint = INVALID_EXTINT_NUMBER; + uint8_t i = 0; + + for (; i < ARRAY_SIZE(_map); i++) { + if (_map[i].pin == pin) { + extint = _map[i].extint; + break; + } + } + if (INVALID_EXTINT_NUMBER == extint) { + return -1; + } + + if (enable) { + hri_eic_set_INTEN_reg(EIC, 1ul << extint); + } else { + hri_eic_clear_INTEN_reg(EIC, 1ul << extint); + hri_eic_clear_INTFLAG_reg(EIC, 1ul << extint); + } + + return ERR_NONE; +} + +/** + * \brief Inter EIC interrupt handler + */ +static void _ext_irq_handler(void) +{ + volatile uint32_t flags = hri_eic_read_INTFLAG_reg(EIC); + int8_t pos; + uint32_t pin = INVALID_PIN_NUMBER; + + hri_eic_clear_INTFLAG_reg(EIC, flags); + + ASSERT(callback); + + while (flags) { + pos = ffs(flags) - 1; + while (-1 != pos) { + uint8_t lower = 0, middle, upper = EXT_IRQ_AMOUNT; + + while (upper >= lower) { + middle = (upper + lower) >> 1; + if (_map[middle].extint == pos) { + pin = _map[middle].pin; + break; + } + if (_map[middle].extint < pos) { + lower = middle + 1; + } else { + upper = middle - 1; + } + } + + if (INVALID_PIN_NUMBER != pin) { + callback(pin); + } + flags &= ~(1ul << pos); + pos = ffs(flags) - 1; + } + flags = hri_eic_read_INTFLAG_reg(EIC); + hri_eic_clear_INTFLAG_reg(EIC, flags); + } +} + +/** + * \brief EIC interrupt handler + */ +void EIC_Handler(void) +{ + _ext_irq_handler(); +} diff --git a/watch-library/hpl/gclk/hpl_gclk.c b/watch-library/hpl/gclk/hpl_gclk.c new file mode 100644 index 00000000..86451165 --- /dev/null +++ b/watch-library/hpl/gclk/hpl_gclk.c @@ -0,0 +1,163 @@ + +/** + * \file + * + * \brief Generic Clock Controller related functionality. + * + * Copyright (c) 2015-2018 Microchip Technology Inc. and its subsidiaries. + * + * \asf_license_start + * + * \page License + * + * Subject to your compliance with these terms, you may use Microchip + * software and any derivatives exclusively with Microchip products. + * It is your responsibility to comply with third party license terms applicable + * to your use of third party software (including open source software) that + * may accompany Microchip software. + * + * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, + * WHETHER EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, + * INCLUDING ANY IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, + * AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE + * LIABLE FOR ANY INDIRECT, SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL + * LOSS, DAMAGE, COST OR EXPENSE OF ANY KIND WHATSOEVER RELATED TO THE + * SOFTWARE, HOWEVER CAUSED, EVEN IF MICROCHIP HAS BEEN ADVISED OF THE + * POSSIBILITY OR THE DAMAGES ARE FORESEEABLE. TO THE FULLEST EXTENT + * ALLOWED BY LAW, MICROCHIP'S TOTAL LIABILITY ON ALL CLAIMS IN ANY WAY + * RELATED TO THIS SOFTWARE WILL NOT EXCEED THE AMOUNT OF FEES, IF ANY, + * THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR THIS SOFTWARE. + * + * \asf_license_stop + * + */ + +#include +#include +#include + +/* Compatible naming definition */ +#ifndef GCLK_GENCTRL_SRC_DPLL +#define GCLK_GENCTRL_SRC_DPLL GCLK_GENCTRL_SRC_FDPLL +#endif + +/** + * \brief Initializes generators + */ +void _gclk_init_generators(void) +{ + +#if CONF_GCLK_GENERATOR_0_CONFIG == 1 + hri_gclk_write_GENCTRL_reg( + GCLK, + 0, + GCLK_GENCTRL_DIV(CONF_GCLK_GEN_0_DIV) | (CONF_GCLK_GEN_0_RUNSTDBY << GCLK_GENCTRL_RUNSTDBY_Pos) + | (CONF_GCLK_GEN_0_DIVSEL << GCLK_GENCTRL_DIVSEL_Pos) | (CONF_GCLK_GEN_0_OE << GCLK_GENCTRL_OE_Pos) + | (CONF_GCLK_GEN_0_OOV << GCLK_GENCTRL_OOV_Pos) | (CONF_GCLK_GEN_0_IDC << GCLK_GENCTRL_IDC_Pos) + | (CONF_GCLK_GENERATOR_0_CONFIG << GCLK_GENCTRL_GENEN_Pos) | CONF_GCLK_GEN_0_SOURCE); +#endif + +#if CONF_GCLK_GENERATOR_1_CONFIG == 1 + hri_gclk_write_GENCTRL_reg( + GCLK, + 1, + GCLK_GENCTRL_DIV(CONF_GCLK_GEN_1_DIV) | (CONF_GCLK_GEN_1_RUNSTDBY << GCLK_GENCTRL_RUNSTDBY_Pos) + | (CONF_GCLK_GEN_1_DIVSEL << GCLK_GENCTRL_DIVSEL_Pos) | (CONF_GCLK_GEN_1_OE << GCLK_GENCTRL_OE_Pos) + | (CONF_GCLK_GEN_1_OOV << GCLK_GENCTRL_OOV_Pos) | (CONF_GCLK_GEN_1_IDC << GCLK_GENCTRL_IDC_Pos) + | (CONF_GCLK_GENERATOR_1_CONFIG << GCLK_GENCTRL_GENEN_Pos) | CONF_GCLK_GEN_1_SOURCE); +#endif + +#if CONF_GCLK_GENERATOR_2_CONFIG == 1 + hri_gclk_write_GENCTRL_reg( + GCLK, + 2, + GCLK_GENCTRL_DIV(CONF_GCLK_GEN_2_DIV) | (CONF_GCLK_GEN_2_RUNSTDBY << GCLK_GENCTRL_RUNSTDBY_Pos) + | (CONF_GCLK_GEN_2_DIVSEL << GCLK_GENCTRL_DIVSEL_Pos) | (CONF_GCLK_GEN_2_OE << GCLK_GENCTRL_OE_Pos) + | (CONF_GCLK_GEN_2_OOV << GCLK_GENCTRL_OOV_Pos) | (CONF_GCLK_GEN_2_IDC << GCLK_GENCTRL_IDC_Pos) + | (CONF_GCLK_GENERATOR_2_CONFIG << GCLK_GENCTRL_GENEN_Pos) | CONF_GCLK_GEN_2_SOURCE); +#endif + +#if CONF_GCLK_GENERATOR_3_CONFIG == 1 + hri_gclk_write_GENCTRL_reg( + GCLK, + 3, + GCLK_GENCTRL_DIV(CONF_GCLK_GEN_3_DIV) | (CONF_GCLK_GEN_3_RUNSTDBY << GCLK_GENCTRL_RUNSTDBY_Pos) + | (CONF_GCLK_GEN_3_DIVSEL << GCLK_GENCTRL_DIVSEL_Pos) | (CONF_GCLK_GEN_3_OE << GCLK_GENCTRL_OE_Pos) + | (CONF_GCLK_GEN_3_OOV << GCLK_GENCTRL_OOV_Pos) | (CONF_GCLK_GEN_3_IDC << GCLK_GENCTRL_IDC_Pos) + | (CONF_GCLK_GENERATOR_3_CONFIG << GCLK_GENCTRL_GENEN_Pos) | CONF_GCLK_GEN_3_SOURCE); +#endif + +#if CONF_GCLK_GENERATOR_4_CONFIG == 1 + hri_gclk_write_GENCTRL_reg( + GCLK, + 4, + GCLK_GENCTRL_DIV(CONF_GCLK_GEN_4_DIV) | (CONF_GCLK_GEN_4_RUNSTDBY << GCLK_GENCTRL_RUNSTDBY_Pos) + | (CONF_GCLK_GEN_4_DIVSEL << GCLK_GENCTRL_DIVSEL_Pos) | (CONF_GCLK_GEN_4_OE << GCLK_GENCTRL_OE_Pos) + | (CONF_GCLK_GEN_4_OOV << GCLK_GENCTRL_OOV_Pos) | (CONF_GCLK_GEN_4_IDC << GCLK_GENCTRL_IDC_Pos) + | (CONF_GCLK_GENERATOR_4_CONFIG << GCLK_GENCTRL_GENEN_Pos) | CONF_GCLK_GEN_4_SOURCE); +#endif +} + +void _gclk_init_generators_by_fref(uint32_t bm) +{ + +#if CONF_GCLK_GENERATOR_0_CONFIG == 1 + if (bm & (1ul << 0)) { + hri_gclk_write_GENCTRL_reg( + GCLK, + 0, + GCLK_GENCTRL_DIV(CONF_GCLK_GEN_0_DIV) | (CONF_GCLK_GEN_0_RUNSTDBY << GCLK_GENCTRL_RUNSTDBY_Pos) + | (CONF_GCLK_GEN_0_DIVSEL << GCLK_GENCTRL_DIVSEL_Pos) | (CONF_GCLK_GEN_0_OE << GCLK_GENCTRL_OE_Pos) + | (CONF_GCLK_GEN_0_OOV << GCLK_GENCTRL_OOV_Pos) | (CONF_GCLK_GEN_0_IDC << GCLK_GENCTRL_IDC_Pos) + | (CONF_GCLK_GENERATOR_0_CONFIG << GCLK_GENCTRL_GENEN_Pos) | CONF_GCLK_GEN_0_SOURCE); + } +#endif + +#if CONF_GCLK_GENERATOR_1_CONFIG == 1 + if (bm & (1ul << 1)) { + hri_gclk_write_GENCTRL_reg( + GCLK, + 1, + GCLK_GENCTRL_DIV(CONF_GCLK_GEN_1_DIV) | (CONF_GCLK_GEN_1_RUNSTDBY << GCLK_GENCTRL_RUNSTDBY_Pos) + | (CONF_GCLK_GEN_1_DIVSEL << GCLK_GENCTRL_DIVSEL_Pos) | (CONF_GCLK_GEN_1_OE << GCLK_GENCTRL_OE_Pos) + | (CONF_GCLK_GEN_1_OOV << GCLK_GENCTRL_OOV_Pos) | (CONF_GCLK_GEN_1_IDC << GCLK_GENCTRL_IDC_Pos) + | (CONF_GCLK_GENERATOR_1_CONFIG << GCLK_GENCTRL_GENEN_Pos) | CONF_GCLK_GEN_1_SOURCE); + } +#endif + +#if CONF_GCLK_GENERATOR_2_CONFIG == 1 + if (bm & (1ul << 2)) { + hri_gclk_write_GENCTRL_reg( + GCLK, + 2, + GCLK_GENCTRL_DIV(CONF_GCLK_GEN_2_DIV) | (CONF_GCLK_GEN_2_RUNSTDBY << GCLK_GENCTRL_RUNSTDBY_Pos) + | (CONF_GCLK_GEN_2_DIVSEL << GCLK_GENCTRL_DIVSEL_Pos) | (CONF_GCLK_GEN_2_OE << GCLK_GENCTRL_OE_Pos) + | (CONF_GCLK_GEN_2_OOV << GCLK_GENCTRL_OOV_Pos) | (CONF_GCLK_GEN_2_IDC << GCLK_GENCTRL_IDC_Pos) + | (CONF_GCLK_GENERATOR_2_CONFIG << GCLK_GENCTRL_GENEN_Pos) | CONF_GCLK_GEN_2_SOURCE); + } +#endif + +#if CONF_GCLK_GENERATOR_3_CONFIG == 1 + if (bm & (1ul << 3)) { + hri_gclk_write_GENCTRL_reg( + GCLK, + 3, + GCLK_GENCTRL_DIV(CONF_GCLK_GEN_3_DIV) | (CONF_GCLK_GEN_3_RUNSTDBY << GCLK_GENCTRL_RUNSTDBY_Pos) + | (CONF_GCLK_GEN_3_DIVSEL << GCLK_GENCTRL_DIVSEL_Pos) | (CONF_GCLK_GEN_3_OE << GCLK_GENCTRL_OE_Pos) + | (CONF_GCLK_GEN_3_OOV << GCLK_GENCTRL_OOV_Pos) | (CONF_GCLK_GEN_3_IDC << GCLK_GENCTRL_IDC_Pos) + | (CONF_GCLK_GENERATOR_3_CONFIG << GCLK_GENCTRL_GENEN_Pos) | CONF_GCLK_GEN_3_SOURCE); + } +#endif + +#if CONF_GCLK_GENERATOR_4_CONFIG == 1 + if (bm & (1ul << 4)) { + hri_gclk_write_GENCTRL_reg( + GCLK, + 4, + GCLK_GENCTRL_DIV(CONF_GCLK_GEN_4_DIV) | (CONF_GCLK_GEN_4_RUNSTDBY << GCLK_GENCTRL_RUNSTDBY_Pos) + | (CONF_GCLK_GEN_4_DIVSEL << GCLK_GENCTRL_DIVSEL_Pos) | (CONF_GCLK_GEN_4_OE << GCLK_GENCTRL_OE_Pos) + | (CONF_GCLK_GEN_4_OOV << GCLK_GENCTRL_OOV_Pos) | (CONF_GCLK_GEN_4_IDC << GCLK_GENCTRL_IDC_Pos) + | (CONF_GCLK_GENERATOR_4_CONFIG << GCLK_GENCTRL_GENEN_Pos) | CONF_GCLK_GEN_4_SOURCE); + } +#endif +} diff --git a/watch-library/hpl/gclk/hpl_gclk_base.h b/watch-library/hpl/gclk/hpl_gclk_base.h new file mode 100644 index 00000000..3e7d2825 --- /dev/null +++ b/watch-library/hpl/gclk/hpl_gclk_base.h @@ -0,0 +1,87 @@ +/** + * \file + * + * \brief Generic Clock Controller. + * + * Copyright (c) 2014-2018 Microchip Technology Inc. and its subsidiaries. + * + * \asf_license_start + * + * \page License + * + * Subject to your compliance with these terms, you may use Microchip + * software and any derivatives exclusively with Microchip products. + * It is your responsibility to comply with third party license terms applicable + * to your use of third party software (including open source software) that + * may accompany Microchip software. + * + * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, + * WHETHER EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, + * INCLUDING ANY IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, + * AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE + * LIABLE FOR ANY INDIRECT, SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL + * LOSS, DAMAGE, COST OR EXPENSE OF ANY KIND WHATSOEVER RELATED TO THE + * SOFTWARE, HOWEVER CAUSED, EVEN IF MICROCHIP HAS BEEN ADVISED OF THE + * POSSIBILITY OR THE DAMAGES ARE FORESEEABLE. TO THE FULLEST EXTENT + * ALLOWED BY LAW, MICROCHIP'S TOTAL LIABILITY ON ALL CLAIMS IN ANY WAY + * RELATED TO THIS SOFTWARE WILL NOT EXCEED THE AMOUNT OF FEES, IF ANY, + * THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR THIS SOFTWARE. + * + * \asf_license_stop + * + */ + +#ifndef _HPL_GCLK_H_INCLUDED +#define _HPL_GCLK_H_INCLUDED + +#include +#ifdef _UNIT_TEST_ +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \addtogroup gclk_group GCLK Hardware Proxy Layer + * + * \section gclk_hpl_rev Revision History + * - v0.0.0.1 Initial Commit + * + *@{ + */ + +/** + * \name HPL functions + */ +//@{ +/** + * \brief Enable clock on the given channel with the given clock source + * + * This function maps the given clock source to the given clock channel + * and enables channel. + * + * \param[in] channel The channel to enable clock for + * \param[in] source The clock source for the given channel + */ +static inline void _gclk_enable_channel(const uint8_t channel, const uint8_t source) +{ + + hri_gclk_write_PCHCTRL_reg(GCLK, channel, source | GCLK_PCHCTRL_CHEN); +} + +/** + * \brief Initialize GCLK generators by function references + * \param[in] bm Bit mapping for referenced generators, + * a bit 1 in position triggers generator initialization. + */ +void _gclk_init_generators_by_fref(uint32_t bm); + +//@} +/**@}*/ +#ifdef __cplusplus +} +#endif + +#endif /* _HPL_GCLK_H_INCLUDED */ diff --git a/watch-library/hpl/mclk/hpl_mclk.c b/watch-library/hpl/mclk/hpl_mclk.c new file mode 100644 index 00000000..2ada7561 --- /dev/null +++ b/watch-library/hpl/mclk/hpl_mclk.c @@ -0,0 +1,45 @@ +/** + * \file + * + * \brief SAM Main Clock. + * + * Copyright (c) 2015-2018 Microchip Technology Inc. and its subsidiaries. + * + * \asf_license_start + * + * \page License + * + * Subject to your compliance with these terms, you may use Microchip + * software and any derivatives exclusively with Microchip products. + * It is your responsibility to comply with third party license terms applicable + * to your use of third party software (including open source software) that + * may accompany Microchip software. + * + * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, + * WHETHER EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, + * INCLUDING ANY IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, + * AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE + * LIABLE FOR ANY INDIRECT, SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL + * LOSS, DAMAGE, COST OR EXPENSE OF ANY KIND WHATSOEVER RELATED TO THE + * SOFTWARE, HOWEVER CAUSED, EVEN IF MICROCHIP HAS BEEN ADVISED OF THE + * POSSIBILITY OR THE DAMAGES ARE FORESEEABLE. TO THE FULLEST EXTENT + * ALLOWED BY LAW, MICROCHIP'S TOTAL LIABILITY ON ALL CLAIMS IN ANY WAY + * RELATED TO THIS SOFTWARE WILL NOT EXCEED THE AMOUNT OF FEES, IF ANY, + * THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR THIS SOFTWARE. + * + * \asf_license_stop + * + */ + +#include +#include + +/** + * \brief Initialize master clock generator + */ +void _mclk_init(void) +{ + void *hw = (void *)MCLK; + hri_mclk_write_BUPDIV_reg(hw, MCLK_BUPDIV_BUPDIV(CONF_MCLK_BUPDIV)); + hri_mclk_write_CPUDIV_reg(hw, MCLK_CPUDIV_CPUDIV(CONF_MCLK_CPUDIV)); +} diff --git a/watch-library/hpl/osc32kctrl/hpl_osc32kctrl.c b/watch-library/hpl/osc32kctrl/hpl_osc32kctrl.c new file mode 100644 index 00000000..b6c624cc --- /dev/null +++ b/watch-library/hpl/osc32kctrl/hpl_osc32kctrl.c @@ -0,0 +1,86 @@ +/** + * \file + * + * \brief SAM 32k Oscillators Controller. + * + * Copyright (c) 2015-2018 Microchip Technology Inc. and its subsidiaries. + * + * \asf_license_start + * + * \page License + * + * Subject to your compliance with these terms, you may use Microchip + * software and any derivatives exclusively with Microchip products. + * It is your responsibility to comply with third party license terms applicable + * to your use of third party software (including open source software) that + * may accompany Microchip software. + * + * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, + * WHETHER EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, + * INCLUDING ANY IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, + * AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE + * LIABLE FOR ANY INDIRECT, SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL + * LOSS, DAMAGE, COST OR EXPENSE OF ANY KIND WHATSOEVER RELATED TO THE + * SOFTWARE, HOWEVER CAUSED, EVEN IF MICROCHIP HAS BEEN ADVISED OF THE + * POSSIBILITY OR THE DAMAGES ARE FORESEEABLE. TO THE FULLEST EXTENT + * ALLOWED BY LAW, MICROCHIP'S TOTAL LIABILITY ON ALL CLAIMS IN ANY WAY + * RELATED TO THIS SOFTWARE WILL NOT EXCEED THE AMOUNT OF FEES, IF ANY, + * THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR THIS SOFTWARE. + * + * \asf_license_stop + * + */ +#include +#include +#include + +/** + * \brief Initialize 32 kHz clock sources + */ +void _osc32kctrl_init_sources(void) +{ + void * hw = (void *)OSC32KCTRL; + uint16_t calib = 0; + +#if CONF_XOSC32K_CONFIG == 1 + hri_osc32kctrl_write_XOSC32K_reg( + hw, + OSC32KCTRL_XOSC32K_STARTUP(CONF_XOSC32K_STARTUP) | (CONF_XOSC32K_ONDEMAND << OSC32KCTRL_XOSC32K_ONDEMAND_Pos) + | (CONF_XOSC32K_RUNSTDBY << OSC32KCTRL_XOSC32K_RUNSTDBY_Pos) + | (CONF_XOSC32K_EN1K << OSC32KCTRL_XOSC32K_EN1K_Pos) | (CONF_XOSC32K_EN32K << OSC32KCTRL_XOSC32K_EN32K_Pos) + | (CONF_XOSC32K_XTALEN << OSC32KCTRL_XOSC32K_XTALEN_Pos) + | (CONF_XOSC32K_ENABLE << OSC32KCTRL_XOSC32K_ENABLE_Pos)); + + hri_osc32kctrl_write_CFDCTRL_reg(hw, + (CONF_XOSC32K_CFDEN << OSC32KCTRL_CFDCTRL_CFDEN_Pos) + | (CONF_XOSC32K_SWBEN << OSC32KCTRL_CFDCTRL_SWBACK_Pos)); + + hri_osc32kctrl_write_EVCTRL_reg(hw, (CONF_XOSC32K_CFDEO << OSC32KCTRL_EVCTRL_CFDEO_Pos)); +#endif + +#if CONF_OSCULP32K_CONFIG == 1 + calib = hri_osc32kctrl_read_OSCULP32K_CALIB_bf(hw); + hri_osc32kctrl_write_OSCULP32K_reg(hw, +#if CONF_OSC32K_CALIB_ENABLE == 1 + OSC32KCTRL_OSCULP32K_CALIB(CONF_OSC32K_CALIB) +#else + OSC32KCTRL_OSCULP32K_CALIB(calib) +#endif + ); +#endif + +#if CONF_XOSC32K_CONFIG +#if CONF_XOSC32K_ENABLE == 1 && CONF_XOSC32K_ONDEMAND == 0 + while (!hri_osc32kctrl_get_STATUS_XOSC32KRDY_bit(hw)) + ; +#endif +#if CONF_OSCULP32K_ULP32KSW == 1 + hri_osc32kctrl_set_OSCULP32K_reg(hw, OSC32KCTRL_OSCULP32K_ULP32KSW); + while (!hri_osc32kctrl_get_STATUS_ULP32KSW_bit(hw)) + ; +#endif +#endif + + hri_osc32kctrl_write_RTCCTRL_reg(hw, OSC32KCTRL_RTCCTRL_RTCSEL(CONF_RTCCTRL)); + (void)calib; +} diff --git a/watch-library/hpl/oscctrl/hpl_oscctrl.c b/watch-library/hpl/oscctrl/hpl_oscctrl.c new file mode 100644 index 00000000..e11d70d8 --- /dev/null +++ b/watch-library/hpl/oscctrl/hpl_oscctrl.c @@ -0,0 +1,179 @@ + +/** + * \file + * + * \brief SAM Oscillators Controller. + * + * Copyright (c) 2015-2018 Microchip Technology Inc. and its subsidiaries. + * + * \asf_license_start + * + * \page License + * + * Subject to your compliance with these terms, you may use Microchip + * software and any derivatives exclusively with Microchip products. + * It is your responsibility to comply with third party license terms applicable + * to your use of third party software (including open source software) that + * may accompany Microchip software. + * + * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, + * WHETHER EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, + * INCLUDING ANY IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, + * AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE + * LIABLE FOR ANY INDIRECT, SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL + * LOSS, DAMAGE, COST OR EXPENSE OF ANY KIND WHATSOEVER RELATED TO THE + * SOFTWARE, HOWEVER CAUSED, EVEN IF MICROCHIP HAS BEEN ADVISED OF THE + * POSSIBILITY OR THE DAMAGES ARE FORESEEABLE. TO THE FULLEST EXTENT + * ALLOWED BY LAW, MICROCHIP'S TOTAL LIABILITY ON ALL CLAIMS IN ANY WAY + * RELATED TO THIS SOFTWARE WILL NOT EXCEED THE AMOUNT OF FEES, IF ANY, + * THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR THIS SOFTWARE. + * + * \asf_license_stop + * + */ +#include +#include +#include + +/** + * \brief Initialize clock sources + */ +void _oscctrl_init_sources(void) +{ + void *hw = (void *)OSCCTRL; + +#if CONF_XOSC_CONFIG == 1 + hri_oscctrl_write_XOSCCTRL_reg( + hw, + OSCCTRL_XOSCCTRL_STARTUP(CONF_XOSC_STARTUP) | (0 << OSCCTRL_XOSCCTRL_AMPGC_Pos) + | OSCCTRL_XOSCCTRL_GAIN(CONF_XOSC_GAIN) | (0 << OSCCTRL_XOSCCTRL_ONDEMAND_Pos) + | (CONF_XOSC_RUNSTDBY << OSCCTRL_XOSCCTRL_RUNSTDBY_Pos) | (CONF_XOSC_SWBEN << OSCCTRL_XOSCCTRL_SWBEN_Pos) + | (CONF_XOSC_CFDEN << OSCCTRL_XOSCCTRL_CFDEN_Pos) | (CONF_XOSC_XTALEN << OSCCTRL_XOSCCTRL_XTALEN_Pos) + | (CONF_XOSC_ENABLE << OSCCTRL_XOSCCTRL_ENABLE_Pos)); + + hri_oscctrl_write_EVCTRL_reg(hw, (CONF_XOSC_CFDEO << OSCCTRL_EVCTRL_CFDEO_Pos)); +#endif + +#if CONF_OSC16M_CONFIG == 1 + hri_oscctrl_write_OSC16MCTRL_reg(hw, + (CONF_OSC16M_ONDEMAND << OSCCTRL_OSC16MCTRL_ONDEMAND_Pos) + | (CONF_OSC16M_RUNSTDBY << OSCCTRL_OSC16MCTRL_RUNSTDBY_Pos) + | (CONF_OSC16M_ENABLE << OSCCTRL_OSC16MCTRL_ENABLE_Pos) + | OSCCTRL_OSC16MCTRL_FSEL(CONF_OSC16M_FSEL)); +#endif + +#if CONF_XOSC_CONFIG == 1 +#if CONF_XOSC_ENABLE == 1 + while (!hri_oscctrl_get_STATUS_XOSCRDY_bit(hw)) + ; +#endif +#if CONF_XOSC_AMPGC == 1 + hri_oscctrl_set_XOSCCTRL_AMPGC_bit(hw); +#endif +#if CONF_XOSC_ONDEMAND == 1 + hri_oscctrl_set_XOSCCTRL_ONDEMAND_bit(hw); +#endif +#endif + +#if CONF_OSC16M_CONFIG == 1 +#if CONF_OSC16M_ENABLE == 1 + while (!hri_oscctrl_get_STATUS_OSC16MRDY_bit(hw)) + ; +#endif +#if CONF_OSC16M_ONDEMAND == 1 + hri_oscctrl_set_OSC16MCTRL_ONDEMAND_bit(hw); +#endif +#endif + (void)hw; +} + +void _oscctrl_init_referenced_generators(void) +{ + void * hw = (void *)OSCCTRL; + hri_oscctrl_dfllctrl_reg_t tmp = 0; + +#if CONF_DFLL_CONFIG == 1 +#if CONF_DFLL_OVERWRITE_CALIBRATION == 0 +#define NVM_DFLL_COARSE_POS 26 +#define NVM_DFLL_COARSE_SIZE 6 + uint32_t coarse; + coarse = *((uint32_t *)(NVMCTRL_OTP5)) >> NVM_DFLL_COARSE_POS; +#endif +#if CONF_DFLL_USBCRM != 1 && CONF_DFLL_MODE != 0 + hri_gclk_write_PCHCTRL_reg(GCLK, 0, (1 << GCLK_PCHCTRL_CHEN_Pos) | GCLK_PCHCTRL_GEN(CONF_DFLL_GCLK)); +#endif + hri_oscctrl_write_DFLLCTRL_reg(hw, OSCCTRL_DFLLCTRL_ENABLE); + while (!hri_oscctrl_get_STATUS_DFLLRDY_bit(hw)) + ; + hri_oscctrl_write_DFLLMUL_reg(hw, + OSCCTRL_DFLLMUL_CSTEP(CONF_DFLL_CSTEP) | OSCCTRL_DFLLMUL_FSTEP(CONF_DFLL_FSTEP) + | OSCCTRL_DFLLMUL_MUL(CONF_DFLL_MUL)); + while (!hri_oscctrl_get_STATUS_DFLLRDY_bit(hw)) + ; + +#if CONF_DFLL_OVERWRITE_CALIBRATION == 0 + /* FINE is set to fixed value, which defined by DFLL48M Characteristics */ + hri_oscctrl_write_DFLLVAL_reg(hw, OSCCTRL_DFLLVAL_COARSE(coarse) | OSCCTRL_DFLLVAL_FINE(512)); +#else + hri_oscctrl_write_DFLLVAL_reg(hw, OSCCTRL_DFLLVAL_COARSE(CONF_DFLL_COARSE) | OSCCTRL_DFLLVAL_FINE(CONF_DFLL_FINE)); +#endif + + tmp = (CONF_DFLL_WAITLOCK << OSCCTRL_DFLLCTRL_WAITLOCK_Pos) | (CONF_DFLL_BPLCKC << OSCCTRL_DFLLCTRL_BPLCKC_Pos) + | (CONF_DFLL_QLDIS << OSCCTRL_DFLLCTRL_QLDIS_Pos) | (CONF_DFLL_CCDIS << OSCCTRL_DFLLCTRL_CCDIS_Pos) + | (CONF_DFLL_RUNSTDBY << OSCCTRL_DFLLCTRL_RUNSTDBY_Pos) | (CONF_DFLL_USBCRM << OSCCTRL_DFLLCTRL_USBCRM_Pos) + | (CONF_DFLL_LLAW << OSCCTRL_DFLLCTRL_LLAW_Pos) | (CONF_DFLL_STABLE << OSCCTRL_DFLLCTRL_STABLE_Pos) + | (CONF_DFLL_MODE << OSCCTRL_DFLLCTRL_MODE_Pos) | (CONF_DFLL_ENABLE << OSCCTRL_DFLLCTRL_ENABLE_Pos); + hri_oscctrl_write_DFLLCTRL_reg(hw, tmp); + +#endif + +#if CONF_DPLL_CONFIG == 1 +#if CONF_DPLL_REFCLK == 2 + hri_gclk_write_PCHCTRL_reg(GCLK, 1, (1 << GCLK_PCHCTRL_CHEN_Pos) | GCLK_PCHCTRL_GEN(CONF_DPLL_GCLK)); +#endif + hri_oscctrl_write_DPLLRATIO_reg( + hw, OSCCTRL_DPLLRATIO_LDRFRAC(CONF_DPLL_LDRFRAC) | OSCCTRL_DPLLRATIO_LDR(CONF_DPLL_LDR)); + hri_oscctrl_write_DPLLCTRLB_reg( + hw, + OSCCTRL_DPLLCTRLB_DIV(CONF_DPLL_DIV) | (CONF_DPLL_LBYPASS << OSCCTRL_DPLLCTRLB_LBYPASS_Pos) + | OSCCTRL_DPLLCTRLB_LTIME(CONF_DPLL_LTIME) | OSCCTRL_DPLLCTRLB_REFCLK(CONF_DPLL_REFCLK) + | (CONF_DPLL_WUF << OSCCTRL_DPLLCTRLB_WUF_Pos) | (CONF_DPLL_LPEN << OSCCTRL_DPLLCTRLB_LPEN_Pos) + | OSCCTRL_DPLLCTRLB_FILTER(CONF_DPLL_FILTER)); + hri_oscctrl_write_DPLLPRESC_reg(hw, OSCCTRL_DPLLPRESC_PRESC(CONF_DPLL_PRESC)); + hri_oscctrl_write_DPLLCTRLA_reg(hw, + (0 << OSCCTRL_DPLLCTRLA_ONDEMAND_Pos) + | (CONF_DPLL_RUNSTDBY << OSCCTRL_DPLLCTRLA_RUNSTDBY_Pos) + | (CONF_DPLL_ENABLE << OSCCTRL_DPLLCTRLA_ENABLE_Pos)); +#endif + +#if CONF_DFLL_CONFIG == 1 + if (hri_oscctrl_get_DFLLCTRL_MODE_bit(hw)) { + hri_oscctrl_status_reg_t status_mask = OSCCTRL_STATUS_DFLLRDY | OSCCTRL_STATUS_DFLLLCKC; + + while (hri_oscctrl_get_STATUS_reg(hw, status_mask) != status_mask) + ; + } else { + while (!hri_oscctrl_get_STATUS_DFLLRDY_bit(hw)) + ; + } +#if CONF_DFLL_ONDEMAND == 1 + hri_oscctrl_set_DFLLCTRL_ONDEMAND_bit(hw); +#endif +#endif + +#if CONF_DPLL_CONFIG == 1 +#if CONF_DPLL_ENABLE == 1 + while (!(hri_oscctrl_get_DPLLSTATUS_LOCK_bit(hw) || hri_oscctrl_get_DPLLSTATUS_CLKRDY_bit(hw))) + ; +#endif +#if CONF_DPLL_ONDEMAND == 1 + hri_oscctrl_set_DPLLCTRLA_ONDEMAND_bit(hw); +#endif +#endif + +#if CONF_DFLL_CONFIG == 1 + while (hri_gclk_read_SYNCBUSY_reg(GCLK)) + ; +#endif + (void)hw, (void)tmp; +} diff --git a/watch-library/hpl/pm/hpl_pm.c b/watch-library/hpl/pm/hpl_pm.c new file mode 100644 index 00000000..d6439f1d --- /dev/null +++ b/watch-library/hpl/pm/hpl_pm.c @@ -0,0 +1,77 @@ + +/** + * \file + * + * \brief SAM Power manager + * + * Copyright (c) 2014-2018 Microchip Technology Inc. and its subsidiaries. + * + * \asf_license_start + * + * \page License + * + * Subject to your compliance with these terms, you may use Microchip + * software and any derivatives exclusively with Microchip products. + * It is your responsibility to comply with third party license terms applicable + * to your use of third party software (including open source software) that + * may accompany Microchip software. + * + * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, + * WHETHER EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, + * INCLUDING ANY IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, + * AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE + * LIABLE FOR ANY INDIRECT, SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL + * LOSS, DAMAGE, COST OR EXPENSE OF ANY KIND WHATSOEVER RELATED TO THE + * SOFTWARE, HOWEVER CAUSED, EVEN IF MICROCHIP HAS BEEN ADVISED OF THE + * POSSIBILITY OR THE DAMAGES ARE FORESEEABLE. TO THE FULLEST EXTENT + * ALLOWED BY LAW, MICROCHIP'S TOTAL LIABILITY ON ALL CLAIMS IN ANY WAY + * RELATED TO THIS SOFTWARE WILL NOT EXCEED THE AMOUNT OF FEES, IF ANY, + * THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR THIS SOFTWARE. + * + * \asf_license_stop + * + */ + +#include +#include +#include + +/** + * \brief Retrieve the reset reason + */ +enum reset_reason _get_reset_reason(void) +{ + return (enum reset_reason)hri_rstc_read_RCAUSE_reg(RSTC); +} + +/** + * \brief Set the sleep mode for the device + */ +int32_t _set_sleep_mode(const uint8_t mode) +{ + switch (mode) { + case 2: /* IDLE */ + case 4: /* STANDBY */ + case 5: /* BACKUP */ + case 6: /* OFF */ + hri_pm_write_SLEEPCFG_SLEEPMODE_bf(PM, mode); + break; + default: + return ERR_INVALID_ARG; + } + + return ERR_NONE; +} + +/** + * \brief Set performance level + */ +void _set_performance_level(const uint8_t level) +{ + if (hri_pm_get_PLCFG_PLSEL_bf(PM, PM_PLCFG_PLSEL_Msk) != level) { + hri_pm_clear_INTFLAG_reg(PM, 0xFF); + hri_pm_write_PLCFG_PLSEL_bf(PM, level); + while (!hri_pm_read_INTFLAG_reg(PM)) + ; + } +} diff --git a/watch-library/hpl/pm/hpl_pm_base.h b/watch-library/hpl/pm/hpl_pm_base.h new file mode 100644 index 00000000..5a50a914 --- /dev/null +++ b/watch-library/hpl/pm/hpl_pm_base.h @@ -0,0 +1,45 @@ +/** + * \file + * + * \brief SAM Power manager + * + * Copyright (c) 2014-2018 Microchip Technology Inc. and its subsidiaries. + * + * \asf_license_start + * + * \page License + * + * Subject to your compliance with these terms, you may use Microchip + * software and any derivatives exclusively with Microchip products. + * It is your responsibility to comply with third party license terms applicable + * to your use of third party software (including open source software) that + * may accompany Microchip software. + * + * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, + * WHETHER EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, + * INCLUDING ANY IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, + * AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE + * LIABLE FOR ANY INDIRECT, SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL + * LOSS, DAMAGE, COST OR EXPENSE OF ANY KIND WHATSOEVER RELATED TO THE + * SOFTWARE, HOWEVER CAUSED, EVEN IF MICROCHIP HAS BEEN ADVISED OF THE + * POSSIBILITY OR THE DAMAGES ARE FORESEEABLE. TO THE FULLEST EXTENT + * ALLOWED BY LAW, MICROCHIP'S TOTAL LIABILITY ON ALL CLAIMS IN ANY WAY + * RELATED TO THIS SOFTWARE WILL NOT EXCEED THE AMOUNT OF FEES, IF ANY, + * THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR THIS SOFTWARE. + * + * \asf_license_stop + */ + +#ifndef _HPL_PM_BASE_H_INCLUDED +#define _HPL_PM_BASE_H_INCLUDED + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#ifdef __cplusplus +} +#endif +#endif /* _HPL_PM_BASE_H_INCLUDED */ diff --git a/watch-library/hpl/port/hpl_gpio_base.h b/watch-library/hpl/port/hpl_gpio_base.h new file mode 100644 index 00000000..3cc1981f --- /dev/null +++ b/watch-library/hpl/port/hpl_gpio_base.h @@ -0,0 +1,170 @@ + +/** + * \file + * + * \brief SAM PORT. + * + * Copyright (c) 2014-2018 Microchip Technology Inc. and its subsidiaries. + * + * \asf_license_start + * + * \page License + * + * Subject to your compliance with these terms, you may use Microchip + * software and any derivatives exclusively with Microchip products. + * It is your responsibility to comply with third party license terms applicable + * to your use of third party software (including open source software) that + * may accompany Microchip software. + * + * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, + * WHETHER EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, + * INCLUDING ANY IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, + * AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE + * LIABLE FOR ANY INDIRECT, SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL + * LOSS, DAMAGE, COST OR EXPENSE OF ANY KIND WHATSOEVER RELATED TO THE + * SOFTWARE, HOWEVER CAUSED, EVEN IF MICROCHIP HAS BEEN ADVISED OF THE + * POSSIBILITY OR THE DAMAGES ARE FORESEEABLE. TO THE FULLEST EXTENT + * ALLOWED BY LAW, MICROCHIP'S TOTAL LIABILITY ON ALL CLAIMS IN ANY WAY + * RELATED TO THIS SOFTWARE WILL NOT EXCEED THE AMOUNT OF FEES, IF ANY, + * THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR THIS SOFTWARE. + * + * \asf_license_stop + * + */ +#include +#include +#include +#include + +/** + * \brief Set direction on port with mask + */ +static inline void _gpio_set_direction(const enum gpio_port port, const uint32_t mask, + const enum gpio_direction direction) +{ + switch (direction) { + case GPIO_DIRECTION_OFF: + hri_port_clear_DIR_reg(PORT_IOBUS, port, mask); + hri_port_write_WRCONFIG_reg(PORT, port, PORT_WRCONFIG_WRPINCFG | (mask & 0xffff)); + hri_port_write_WRCONFIG_reg( + PORT, port, PORT_WRCONFIG_HWSEL | PORT_WRCONFIG_WRPINCFG | ((mask & 0xffff0000) >> 16)); + break; + + case GPIO_DIRECTION_IN: + hri_port_clear_DIR_reg(PORT_IOBUS, port, mask); + hri_port_write_WRCONFIG_reg(PORT, port, PORT_WRCONFIG_WRPINCFG | PORT_WRCONFIG_INEN | (mask & 0xffff)); + hri_port_write_WRCONFIG_reg(PORT, + port, + PORT_WRCONFIG_HWSEL | PORT_WRCONFIG_WRPINCFG | PORT_WRCONFIG_INEN + | ((mask & 0xffff0000) >> 16)); + break; + + case GPIO_DIRECTION_OUT: + hri_port_set_DIR_reg(PORT_IOBUS, port, mask); + hri_port_write_WRCONFIG_reg(PORT, port, PORT_WRCONFIG_WRPINCFG | (mask & 0xffff)); + hri_port_write_WRCONFIG_reg( + PORT, port, PORT_WRCONFIG_HWSEL | PORT_WRCONFIG_WRPINCFG | ((mask & 0xffff0000) >> 16)); + break; + + default: + ASSERT(false); + } +} + +/** + * \brief Set output level on port with mask + */ +static inline void _gpio_set_level(const enum gpio_port port, const uint32_t mask, const bool level) +{ + if (level) { + hri_port_set_OUT_reg(PORT_IOBUS, port, mask); + } else { + hri_port_clear_OUT_reg(PORT_IOBUS, port, mask); + } +} + +/** + * \brief Change output level to the opposite with mask + */ +static inline void _gpio_toggle_level(const enum gpio_port port, const uint32_t mask) +{ + hri_port_toggle_OUT_reg(PORT_IOBUS, port, mask); +} + +/** + * \brief Get input levels on all port pins + */ +static inline uint32_t _gpio_get_level(const enum gpio_port port) +{ + uint32_t tmp; + + CRITICAL_SECTION_ENTER(); + + uint32_t dir_tmp = hri_port_read_DIR_reg(PORT_IOBUS, port); + + tmp = hri_port_read_IN_reg(PORT, port) & ~dir_tmp; + tmp |= hri_port_read_OUT_reg(PORT_IOBUS, port) & dir_tmp; + + CRITICAL_SECTION_LEAVE(); + + return tmp; +} + +/** + * \brief Set pin pull mode + */ +static inline void _gpio_set_pin_pull_mode(const enum gpio_port port, const uint8_t pin, + const enum gpio_pull_mode pull_mode) +{ + switch (pull_mode) { + case GPIO_PULL_OFF: + hri_port_clear_PINCFG_PULLEN_bit(PORT, port, pin); + break; + + case GPIO_PULL_UP: + hri_port_clear_DIR_reg(PORT_IOBUS, port, 1U << pin); + hri_port_set_PINCFG_PULLEN_bit(PORT, port, pin); + hri_port_set_OUT_reg(PORT_IOBUS, port, 1U << pin); + break; + + case GPIO_PULL_DOWN: + hri_port_clear_DIR_reg(PORT_IOBUS, port, 1U << pin); + hri_port_set_PINCFG_PULLEN_bit(PORT, port, pin); + hri_port_clear_OUT_reg(PORT_IOBUS, port, 1U << pin); + break; + + default: + ASSERT(false); + break; + } +} + +/** + * \brief Set gpio pin function + */ +static inline void _gpio_set_pin_function(const uint32_t gpio, const uint32_t function) +{ + uint8_t port = GPIO_PORT(gpio); + uint8_t pin = GPIO_PIN(gpio); + + if (function == GPIO_PIN_FUNCTION_OFF) { + hri_port_write_PINCFG_PMUXEN_bit(PORT, port, pin, false); + + } else { + hri_port_write_PINCFG_PMUXEN_bit(PORT, port, pin, true); + + if (pin & 1) { + // Odd numbered pin + hri_port_write_PMUX_PMUXO_bf(PORT, port, pin >> 1, function & 0xffff); + } else { + // Even numbered pin + hri_port_write_PMUX_PMUXE_bf(PORT, port, pin >> 1, function & 0xffff); + } + } +} + +static inline void _port_event_init() +{ + hri_port_set_EVCTRL_reg(PORT, 0, CONF_PORTA_EVCTRL); + hri_port_set_EVCTRL_reg(PORT, 1, CONF_PORTB_EVCTRL); +} diff --git a/watch-library/hpl/rtc/hpl_rtc.c b/watch-library/hpl/rtc/hpl_rtc.c new file mode 100644 index 00000000..c28ddec6 --- /dev/null +++ b/watch-library/hpl/rtc/hpl_rtc.c @@ -0,0 +1,397 @@ + +/** + * \file + * + * \brief RTC Driver + * + * Copyright (c) 2014-2018 Microchip Technology Inc. and its subsidiaries. + * + * \asf_license_start + * + * \page License + * + * Subject to your compliance with these terms, you may use Microchip + * software and any derivatives exclusively with Microchip products. + * It is your responsibility to comply with third party license terms applicable + * to your use of third party software (including open source software) that + * may accompany Microchip software. + * + * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, + * WHETHER EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, + * INCLUDING ANY IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, + * AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE + * LIABLE FOR ANY INDIRECT, SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL + * LOSS, DAMAGE, COST OR EXPENSE OF ANY KIND WHATSOEVER RELATED TO THE + * SOFTWARE, HOWEVER CAUSED, EVEN IF MICROCHIP HAS BEEN ADVISED OF THE + * POSSIBILITY OR THE DAMAGES ARE FORESEEABLE. TO THE FULLEST EXTENT + * ALLOWED BY LAW, MICROCHIP'S TOTAL LIABILITY ON ALL CLAIMS IN ANY WAY + * RELATED TO THIS SOFTWARE WILL NOT EXCEED THE AMOUNT OF FEES, IF ANY, + * THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR THIS SOFTWARE. + * + * \asf_license_stop + * + */ + +#include +#include +#include + +/*!< Pointer to hpl device */ +static struct calendar_dev *_rtc_dev = NULL; + +/** + * \brief Initializes the RTC module with given configurations. + */ +int32_t _calendar_init(struct calendar_dev *const dev) +{ + ASSERT(dev && dev->hw); + + _rtc_dev = dev; + + if (hri_rtcmode0_get_CTRLA_ENABLE_bit(dev->hw)) { +#if !CONF_RTC_INIT_RESET + return ERR_DENIED; +#else + hri_rtcmode0_clear_CTRLA_ENABLE_bit(dev->hw); + hri_rtcmode0_wait_for_sync(dev->hw, RTC_MODE0_SYNCBUSY_ENABLE); +#endif + } + hri_rtcmode0_set_CTRLA_SWRST_bit(dev->hw); + hri_rtcmode0_wait_for_sync(dev->hw, RTC_MODE0_SYNCBUSY_SWRST); + +#if CONF_RTC_EVENT_CONTROL_ENABLE == 1 + hri_rtcmode0_write_EVCTRL_reg( + dev->hw, + (CONF_RTC_PEREO0 << RTC_MODE0_EVCTRL_PEREO0_Pos) | (CONF_RTC_PEREO1 << RTC_MODE0_EVCTRL_PEREO1_Pos) + | (CONF_RTC_PEREO2 << RTC_MODE0_EVCTRL_PEREO2_Pos) | (CONF_RTC_PEREO3 << RTC_MODE0_EVCTRL_PEREO3_Pos) + | (CONF_RTC_PEREO4 << RTC_MODE0_EVCTRL_PEREO4_Pos) | (CONF_RTC_PEREO5 << RTC_MODE0_EVCTRL_PEREO5_Pos) + | (CONF_RTC_PEREO6 << RTC_MODE0_EVCTRL_PEREO6_Pos) | (CONF_RTC_PEREO7 << RTC_MODE0_EVCTRL_PEREO7_Pos) + | (CONF_RTC_COMPE0 << RTC_MODE0_EVCTRL_CMPEO_Pos) | (CONF_RTC_OVFEO << RTC_MODE0_EVCTRL_OVFEO_Pos)); +#endif + + hri_rtcmode0_write_CTRLA_reg(dev->hw, RTC_MODE0_CTRLA_PRESCALER(CONF_RTC_PRESCALER) | RTC_MODE0_CTRLA_COUNTSYNC); + + hri_rtc_write_TAMPCTRL_reg( + dev->hw, + (CONF_RTC_TAMPER_INACT_0 << RTC_TAMPCTRL_IN0ACT_Pos) | (CONF_RTC_TAMPER_INACT_1 << RTC_TAMPCTRL_IN1ACT_Pos) + | (CONF_RTC_TAMPER_INACT_2 << RTC_TAMPCTRL_IN2ACT_Pos) + | (CONF_RTC_TAMPER_INACT_3 << RTC_TAMPCTRL_IN3ACT_Pos) + | (CONF_RTC_TAMPER_INACT_4 << RTC_TAMPCTRL_IN4ACT_Pos) | (CONF_RTC_TAMP_LVL_0 << RTC_TAMPCTRL_TAMLVL0_Pos) + | (CONF_RTC_TAMP_LVL_1 << RTC_TAMPCTRL_TAMLVL1_Pos) | (CONF_RTC_TAMP_LVL_2 << RTC_TAMPCTRL_TAMLVL2_Pos) + | (CONF_RTC_TAMP_LVL_3 << RTC_TAMPCTRL_TAMLVL3_Pos) | (CONF_RTC_TAMP_LVL_4 << RTC_TAMPCTRL_TAMLVL4_Pos) + | (CONF_RTC_TAMP_DEBNC_0 << RTC_TAMPCTRL_DEBNC0_Pos) | (CONF_RTC_TAMP_DEBNC_1 << RTC_TAMPCTRL_DEBNC1_Pos) + | (CONF_RTC_TAMP_DEBNC_2 << RTC_TAMPCTRL_DEBNC2_Pos) | (CONF_RTC_TAMP_DEBNC_3 << RTC_TAMPCTRL_DEBNC3_Pos) + | (CONF_RTC_TAMP_DEBNC_4 << RTC_TAMPCTRL_DEBNC4_Pos)); + + if ((CONF_RTC_TAMPER_INACT_0 == TAMPER_MODE_ACTL) | (CONF_RTC_TAMPER_INACT_1 == TAMPER_MODE_ACTL) + | (CONF_RTC_TAMPER_INACT_2 == TAMPER_MODE_ACTL) | (CONF_RTC_TAMPER_INACT_3 == TAMPER_MODE_ACTL) + | (CONF_RTC_TAMPER_INACT_4 == TAMPER_MODE_ACTL)) { + hri_rtcmode0_set_CTRLB_RTCOUT_bit(dev->hw); + } + return ERR_NONE; +} + +/** + * \brief Deinit the RTC module + */ +int32_t _calendar_deinit(struct calendar_dev *const dev) +{ + ASSERT(dev && dev->hw); + + NVIC_DisableIRQ(RTC_IRQn); + dev->callback = NULL; + + hri_rtcmode0_clear_CTRLA_ENABLE_bit(dev->hw); + hri_rtcmode0_set_CTRLA_SWRST_bit(dev->hw); + + return ERR_NONE; +} + +/** + * \brief Enable the RTC module + */ +int32_t _calendar_enable(struct calendar_dev *const dev) +{ + ASSERT(dev && dev->hw); + + hri_rtcmode0_set_CTRLA_ENABLE_bit(dev->hw); + + return ERR_NONE; +} + +/** + * \brief Disable the RTC module + */ +int32_t _calendar_disable(struct calendar_dev *const dev) +{ + ASSERT(dev && dev->hw); + + hri_rtcmode0_clear_CTRLA_ENABLE_bit(dev->hw); + + return ERR_NONE; +} + +/** + * \brief Set the current calendar time to desired time. + */ +int32_t _calendar_set_counter(struct calendar_dev *const dev, const uint32_t counter) +{ + ASSERT(dev && dev->hw); + + hri_rtcmode0_write_COUNT_reg(dev->hw, counter); + + return ERR_NONE; +} + +/** + * \brief Get current counter + */ +uint32_t _calendar_get_counter(struct calendar_dev *const dev) +{ + ASSERT(dev && dev->hw); + + return hri_rtcmode0_read_COUNT_reg(dev->hw); +} + +/** + * \brief Set the compare for the specified value. + */ +int32_t _calendar_set_comp(struct calendar_dev *const dev, const uint32_t comp) +{ + ASSERT(dev && dev->hw); + + hri_rtcmode0_write_COMP_reg(dev->hw, 0, comp); + + return ERR_NONE; +} + +/** + * \brief Get the compare value + */ +uint32_t _calendar_get_comp(struct calendar_dev *const dev) +{ + ASSERT(dev && dev->hw); + + return hri_rtcmode0_read_COMP_reg(dev->hw, 0); +} + +/** + * \brief Find tamper is detected on specified pin + */ +bool _is_tamper_detected(struct calendar_dev *const dev, enum tamper_id tamper_id_pin) +{ + bool value; + + ASSERT(dev && dev->hw); + + value = ((hri_rtc_read_TAMPID_reg(dev->hw) >> tamper_id_pin) & 0x01); + return value; +} + +/** + * \brief Clear the Tamper ID flag + */ +int32_t _tamper_clear_tampid_flag(struct calendar_dev *const dev, enum tamper_id tamper_id_pin) +{ + ASSERT(dev && dev->hw); + + hri_rtc_write_TAMPID_reg(dev->hw, (true << tamper_id_pin)); + + return ERR_NONE; +} + +/** + * \brief Enable Tamper Debounce Asynchronous Feature + */ +int32_t _tamper_enable_debounce_asynchronous(struct calendar_dev *const dev) +{ + int32_t return_value; + + hri_rtcmode0_write_CTRLA_ENABLE_bit(dev->hw, false); + + while (hri_rtcmode0_read_SYNCBUSY_reg(dev->hw) & RTC_MODE2_CTRLA_ENABLE) { + } + + if (hri_rtcmode0_read_CTRLA_reg(dev->hw) & RTC_MODE2_CTRLA_ENABLE) { + return_value = ERR_FAILURE; + } else { + hri_rtcmode0_write_CTRLB_DEBASYNC_bit(dev->hw, true); + return_value = ERR_NONE; + while (hri_rtcmode0_read_SYNCBUSY_reg(dev->hw) & RTC_MODE2_CTRLA_ENABLE) { + } + hri_rtcmode0_write_CTRLA_ENABLE_bit(dev->hw, true); + } + + return return_value; +} + +/** + * \brief Disable Tamper Debounce Asynchronous Feature + */ +int32_t _tamper_disable_debounce_asynchronous(struct calendar_dev *const dev) +{ + int32_t return_value; + + hri_rtcmode0_write_CTRLA_ENABLE_bit(dev->hw, false); + + while (hri_rtcmode0_read_SYNCBUSY_reg(dev->hw) & RTC_MODE2_CTRLA_ENABLE) { + } + + if (hri_rtcmode0_read_CTRLA_reg(dev->hw) & RTC_MODE2_CTRLA_ENABLE) { + return_value = ERR_FAILURE; + } else { + hri_rtcmode0_write_CTRLB_DEBASYNC_bit(dev->hw, false); + return_value = ERR_NONE; + while (hri_rtcmode0_read_SYNCBUSY_reg(dev->hw) & RTC_MODE2_CTRLA_ENABLE) { + } + hri_rtcmode0_write_CTRLA_ENABLE_bit(dev->hw, true); + } + + return return_value; +} + +/** + * \brief Enable Tamper Debounce Majority Feature + */ +int32_t _tamper_enable_debounce_majority(struct calendar_dev *const dev) +{ + int32_t return_value; + + hri_rtcmode0_write_CTRLA_ENABLE_bit(dev->hw, false); + + while (hri_rtcmode0_read_SYNCBUSY_reg(dev->hw) & RTC_MODE2_CTRLA_ENABLE) { + } + + if (hri_rtcmode0_read_CTRLA_reg(dev->hw) & RTC_MODE2_CTRLA_ENABLE) { + return_value = ERR_FAILURE; + } else { + hri_rtcmode0_write_CTRLB_DEBMAJ_bit(dev->hw, true); + return_value = ERR_NONE; + + while (hri_rtcmode0_read_SYNCBUSY_reg(dev->hw) & RTC_MODE2_CTRLA_ENABLE) { + } + hri_rtcmode0_write_CTRLA_ENABLE_bit(dev->hw, true); + } + + return return_value; +} + +/** + * \brief Disable Tamper Debounce Majority Feature + */ +int32_t _tamper_disable_debounce_majority(struct calendar_dev *const dev) +{ + int32_t return_value; + + hri_rtcmode0_write_CTRLA_ENABLE_bit(dev->hw, false); + + while (hri_rtcmode0_read_SYNCBUSY_reg(dev->hw) & RTC_MODE2_CTRLA_ENABLE) { + } + + if (hri_rtcmode0_read_CTRLA_reg(dev->hw) & RTC_MODE2_CTRLA_ENABLE) { + return_value = ERR_FAILURE; + } else { + hri_rtcmode0_write_CTRLB_DEBMAJ_bit(dev->hw, false); + return_value = ERR_NONE; + + while (hri_rtcmode0_read_SYNCBUSY_reg(dev->hw) & RTC_MODE2_CTRLA_ENABLE) { + } + hri_rtcmode0_write_CTRLA_ENABLE_bit(dev->hw, true); + } + + return return_value; +} + +int32_t _tamper_register_callback(struct calendar_dev *const dev, tamper_drv_cb_t callback_tamper) +{ + ASSERT(dev && dev->hw); + + /* Check callback */ + if (callback_tamper != NULL) { + /* register the callback */ + dev->callback_tamper = callback_tamper; + + /* enable RTC_IRQn */ + NVIC_ClearPendingIRQ(RTC_IRQn); + NVIC_EnableIRQ(RTC_IRQn); + + /* enable tamper interrupt */ + hri_rtcmode0_set_INTEN_PER7_bit(dev->hw); + } else { + /* disable tamper interrupt */ + hri_rtcmode0_clear_INTEN_PER7_bit(dev->hw); + + /* disable RTC_IRQn */ + NVIC_DisableIRQ(RTC_IRQn); + } + + return ERR_NONE; +} +/** + * \brief Registers callback for the specified callback type + */ +int32_t _calendar_register_callback(struct calendar_dev *const dev, calendar_drv_cb_alarm_t callback) +{ + ASSERT(dev && dev->hw); + + /* Check callback */ + if (callback != NULL) { + /* register the callback */ + dev->callback = callback; + + /* enable RTC_IRQn */ + NVIC_ClearPendingIRQ(RTC_IRQn); + NVIC_EnableIRQ(RTC_IRQn); + + /* enable cmp */ + hri_rtcmode0_set_INTEN_CMP0_bit(dev->hw); + } else { + /* disable cmp */ + hri_rtcmode0_clear_INTEN_CMP0_bit(dev->hw); + + /* disable RTC_IRQn */ + NVIC_DisableIRQ(RTC_IRQn); + } + + return ERR_NONE; +} + +/** + * \brief RTC interrupt handler + * + * \param[in] dev The pointer to calendar device struct + */ +static void _rtc_interrupt_handler(struct calendar_dev *dev) +{ + /* Read and mask interrupt flag register */ + uint16_t interrupt_status = hri_rtcmode0_read_INTFLAG_reg(dev->hw); + uint16_t interrupt_enabled = hri_rtcmode0_read_INTEN_reg(dev->hw); + + if ((interrupt_status & interrupt_enabled) & RTC_MODE2_INTFLAG_ALARM0) { + dev->callback(dev); + + /* Clear interrupt flag */ + hri_rtcmode0_clear_interrupt_CMP0_bit(dev->hw); + } else if ((interrupt_status & interrupt_enabled) & RTC_MODE2_INTFLAG_PER7) { + dev->callback_tamper(dev); + + /* Clear interrupt flag */ + hri_rtcmode0_clear_interrupt_PER7_bit(dev->hw); + } +} +/** + * \brief Set calendar IRQ + */ +void _calendar_set_irq(struct calendar_dev *const dev) +{ + (void)dev; + NVIC_SetPendingIRQ(RTC_IRQn); +} + +/** + * \brief Rtc interrupt handler + */ +void RTC_Handler(void) +{ + _rtc_interrupt_handler(_rtc_dev); +} diff --git a/watch-library/hpl/rtc/hpl_rtc_base.h b/watch-library/hpl/rtc/hpl_rtc_base.h new file mode 100644 index 00000000..06e3bd79 --- /dev/null +++ b/watch-library/hpl/rtc/hpl_rtc_base.h @@ -0,0 +1,52 @@ +/** + * \file + * + * \brief RTC + * + * Copyright (c) 2016-2018 Microchip Technology Inc. and its subsidiaries. + * + * \asf_license_start + * + * \page License + * + * Subject to your compliance with these terms, you may use Microchip + * software and any derivatives exclusively with Microchip products. + * It is your responsibility to comply with third party license terms applicable + * to your use of third party software (including open source software) that + * may accompany Microchip software. + * + * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, + * WHETHER EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, + * INCLUDING ANY IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, + * AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE + * LIABLE FOR ANY INDIRECT, SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL + * LOSS, DAMAGE, COST OR EXPENSE OF ANY KIND WHATSOEVER RELATED TO THE + * SOFTWARE, HOWEVER CAUSED, EVEN IF MICROCHIP HAS BEEN ADVISED OF THE + * POSSIBILITY OR THE DAMAGES ARE FORESEEABLE. TO THE FULLEST EXTENT + * ALLOWED BY LAW, MICROCHIP'S TOTAL LIABILITY ON ALL CLAIMS IN ANY WAY + * RELATED TO THIS SOFTWARE WILL NOT EXCEED THE AMOUNT OF FEES, IF ANY, + * THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR THIS SOFTWARE. + * + * \asf_license_stop + */ + +#ifndef _HPL_RTC2_V200_H_INCLUDED +#define _HPL_RTC2_V200_H_INCLUDED + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief Retrieve timer helper functions + * + * \return A pointer to set of timer helper functions + */ +struct _timer_hpl_interface *_rtc_get_timer(void); + +#ifdef __cplusplus +} +#endif +#endif /* _HPL_RTC2_V200_H_INCLUDED */ diff --git a/watch-library/hpl/sercom/hpl_sercom.c b/watch-library/hpl/sercom/hpl_sercom.c new file mode 100644 index 00000000..a241e97a --- /dev/null +++ b/watch-library/hpl/sercom/hpl_sercom.c @@ -0,0 +1,2929 @@ + +/** + * \file + * + * \brief SAM Serial Communication Interface + * + * Copyright (c) 2014-2019 Microchip Technology Inc. and its subsidiaries. + * + * \asf_license_start + * + * \page License + * + * Subject to your compliance with these terms, you may use Microchip + * software and any derivatives exclusively with Microchip products. + * It is your responsibility to comply with third party license terms applicable + * to your use of third party software (including open source software) that + * may accompany Microchip software. + * + * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, + * WHETHER EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, + * INCLUDING ANY IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, + * AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE + * LIABLE FOR ANY INDIRECT, SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL + * LOSS, DAMAGE, COST OR EXPENSE OF ANY KIND WHATSOEVER RELATED TO THE + * SOFTWARE, HOWEVER CAUSED, EVEN IF MICROCHIP HAS BEEN ADVISED OF THE + * POSSIBILITY OR THE DAMAGES ARE FORESEEABLE. TO THE FULLEST EXTENT + * ALLOWED BY LAW, MICROCHIP'S TOTAL LIABILITY ON ALL CLAIMS IN ANY WAY + * RELATED TO THIS SOFTWARE WILL NOT EXCEED THE AMOUNT OF FEES, IF ANY, + * THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR THIS SOFTWARE. + * + * \asf_license_stop + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef CONF_SERCOM_0_USART_ENABLE +#define CONF_SERCOM_0_USART_ENABLE 0 +#endif +#ifndef CONF_SERCOM_1_USART_ENABLE +#define CONF_SERCOM_1_USART_ENABLE 0 +#endif +#ifndef CONF_SERCOM_2_USART_ENABLE +#define CONF_SERCOM_2_USART_ENABLE 0 +#endif +#ifndef CONF_SERCOM_3_USART_ENABLE +#define CONF_SERCOM_3_USART_ENABLE 0 +#endif +#ifndef CONF_SERCOM_4_USART_ENABLE +#define CONF_SERCOM_4_USART_ENABLE 0 +#endif +#ifndef CONF_SERCOM_5_USART_ENABLE +#define CONF_SERCOM_5_USART_ENABLE 0 +#endif +#ifndef CONF_SERCOM_6_USART_ENABLE +#define CONF_SERCOM_6_USART_ENABLE 0 +#endif +#ifndef CONF_SERCOM_7_USART_ENABLE +#define CONF_SERCOM_7_USART_ENABLE 0 +#endif + +/** Amount of SERCOM that is used as USART. */ +#define SERCOM_USART_AMOUNT \ + (CONF_SERCOM_0_USART_ENABLE + CONF_SERCOM_1_USART_ENABLE + CONF_SERCOM_2_USART_ENABLE + CONF_SERCOM_3_USART_ENABLE \ + + CONF_SERCOM_4_USART_ENABLE + CONF_SERCOM_5_USART_ENABLE + CONF_SERCOM_6_USART_ENABLE \ + + CONF_SERCOM_7_USART_ENABLE) + +/** + * \brief Macro is used to fill usart configuration structure based on + * its number + * + * \param[in] n The number of structures + */ +#define SERCOM_CONFIGURATION(n) \ + { \ + n, \ + SERCOM_USART_CTRLA_MODE(CONF_SERCOM_##n##_USART_MODE) \ + | (CONF_SERCOM_##n##_USART_RUNSTDBY << SERCOM_USART_CTRLA_RUNSTDBY_Pos) \ + | (CONF_SERCOM_##n##_USART_IBON << SERCOM_USART_CTRLA_IBON_Pos) \ + | SERCOM_USART_CTRLA_SAMPR(CONF_SERCOM_##n##_USART_SAMPR) \ + | SERCOM_USART_CTRLA_TXPO(CONF_SERCOM_##n##_USART_TXPO) \ + | SERCOM_USART_CTRLA_RXPO(CONF_SERCOM_##n##_USART_RXPO) \ + | SERCOM_USART_CTRLA_SAMPA(CONF_SERCOM_##n##_USART_SAMPA) \ + | SERCOM_USART_CTRLA_FORM(CONF_SERCOM_##n##_USART_FORM) \ + | (CONF_SERCOM_##n##_USART_CMODE << SERCOM_USART_CTRLA_CMODE_Pos) \ + | (CONF_SERCOM_##n##_USART_CPOL << SERCOM_USART_CTRLA_CPOL_Pos) \ + | (CONF_SERCOM_##n##_USART_DORD << SERCOM_USART_CTRLA_DORD_Pos), \ + SERCOM_USART_CTRLB_CHSIZE(CONF_SERCOM_##n##_USART_CHSIZE) \ + | (CONF_SERCOM_##n##_USART_SBMODE << SERCOM_USART_CTRLB_SBMODE_Pos) \ + | (CONF_SERCOM_##n##_USART_CLODEN << SERCOM_USART_CTRLB_COLDEN_Pos) \ + | (CONF_SERCOM_##n##_USART_SFDE << SERCOM_USART_CTRLB_SFDE_Pos) \ + | (CONF_SERCOM_##n##_USART_ENC << SERCOM_USART_CTRLB_ENC_Pos) \ + | (CONF_SERCOM_##n##_USART_PMODE << SERCOM_USART_CTRLB_PMODE_Pos) \ + | (CONF_SERCOM_##n##_USART_TXEN << SERCOM_USART_CTRLB_TXEN_Pos) \ + | (CONF_SERCOM_##n##_USART_RXEN << SERCOM_USART_CTRLB_RXEN_Pos), \ + (uint16_t)(CONF_SERCOM_##n##_USART_BAUD_RATE), CONF_SERCOM_##n##_USART_FRACTIONAL, \ + CONF_SERCOM_##n##_USART_RECEIVE_PULSE_LENGTH, CONF_SERCOM_##n##_USART_DEBUG_STOP_MODE, \ + } + +/** + * \brief SERCOM USART configuration type + */ +struct usart_configuration { + uint8_t number; + hri_sercomusart_ctrla_reg_t ctrl_a; + hri_sercomusart_ctrlb_reg_t ctrl_b; + hri_sercomusart_baud_reg_t baud; + uint8_t fractional; + hri_sercomusart_rxpl_reg_t rxpl; + hri_sercomusart_dbgctrl_reg_t debug_ctrl; +}; + +#if SERCOM_USART_AMOUNT < 1 +/** Dummy array to pass compiling. */ +static struct usart_configuration _usarts[1] = {{0}}; +#else +/** + * \brief Array of SERCOM USART configurations + */ +static struct usart_configuration _usarts[] = { +#if CONF_SERCOM_0_USART_ENABLE == 1 + SERCOM_CONFIGURATION(0), +#endif +#if CONF_SERCOM_1_USART_ENABLE == 1 + SERCOM_CONFIGURATION(1), +#endif +#if CONF_SERCOM_2_USART_ENABLE == 1 + SERCOM_CONFIGURATION(2), +#endif +#if CONF_SERCOM_3_USART_ENABLE == 1 + SERCOM_CONFIGURATION(3), +#endif +#if CONF_SERCOM_4_USART_ENABLE == 1 + SERCOM_CONFIGURATION(4), +#endif +#if CONF_SERCOM_5_USART_ENABLE == 1 + SERCOM_CONFIGURATION(5), +#endif +#if CONF_SERCOM_6_USART_ENABLE == 1 + SERCOM_CONFIGURATION(6), +#endif +#if CONF_SERCOM_7_USART_ENABLE == 1 + SERCOM_CONFIGURATION(7), +#endif +}; +#endif + +static uint8_t _get_sercom_index(const void *const hw); +static uint8_t _sercom_get_irq_num(const void *const hw); +static void _sercom_init_irq_param(const void *const hw, void *dev); +static uint8_t _sercom_get_hardware_index(const void *const hw); + +static int32_t _usart_init(void *const hw); +static inline void _usart_deinit(void *const hw); +static uint16_t _usart_calculate_baud_rate(const uint32_t baud, const uint32_t clock_rate, const uint8_t samples, + const enum usart_baud_rate_mode mode, const uint8_t fraction); +static void _usart_set_baud_rate(void *const hw, const uint32_t baud_rate); +static void _usart_set_data_order(void *const hw, const enum usart_data_order order); +static void _usart_set_mode(void *const hw, const enum usart_mode mode); +static void _usart_set_parity(void *const hw, const enum usart_parity parity); +static void _usart_set_stop_bits(void *const hw, const enum usart_stop_bits stop_bits); +static void _usart_set_character_size(void *const hw, const enum usart_character_size size); + +/** + * \brief Initialize synchronous SERCOM USART + */ +int32_t _usart_sync_init(struct _usart_sync_device *const device, void *const hw) +{ + ASSERT(device); + + device->hw = hw; + + return _usart_init(hw); +} + +/** + * \brief Initialize asynchronous SERCOM USART + */ +int32_t _usart_async_init(struct _usart_async_device *const device, void *const hw) +{ + int32_t init_status; + + ASSERT(device); + + init_status = _usart_init(hw); + if (init_status) { + return init_status; + } + device->hw = hw; + _sercom_init_irq_param(hw, (void *)device); + NVIC_DisableIRQ((IRQn_Type)_sercom_get_irq_num(hw)); + NVIC_ClearPendingIRQ((IRQn_Type)_sercom_get_irq_num(hw)); + NVIC_EnableIRQ((IRQn_Type)_sercom_get_irq_num(hw)); + + return ERR_NONE; +} + +/** + * \brief De-initialize SERCOM USART + */ +void _usart_sync_deinit(struct _usart_sync_device *const device) +{ + _usart_deinit(device->hw); +} + +/** + * \brief De-initialize SERCOM USART + */ +void _usart_async_deinit(struct _usart_async_device *const device) +{ + NVIC_DisableIRQ((IRQn_Type)_sercom_get_irq_num(device->hw)); + _usart_deinit(device->hw); +} + +/** + * \brief Calculate baud rate register value + */ +uint16_t _usart_sync_calculate_baud_rate(const uint32_t baud, const uint32_t clock_rate, const uint8_t samples, + const enum usart_baud_rate_mode mode, const uint8_t fraction) +{ + return _usart_calculate_baud_rate(baud, clock_rate, samples, mode, fraction); +} + +/** + * \brief Calculate baud rate register value + */ +uint16_t _usart_async_calculate_baud_rate(const uint32_t baud, const uint32_t clock_rate, const uint8_t samples, + const enum usart_baud_rate_mode mode, const uint8_t fraction) +{ + return _usart_calculate_baud_rate(baud, clock_rate, samples, mode, fraction); +} + +/** + * \brief Enable SERCOM module + */ +void _usart_sync_enable(struct _usart_sync_device *const device) +{ + hri_sercomusart_set_CTRLA_ENABLE_bit(device->hw); +} + +/** + * \brief Enable SERCOM module + */ +void _usart_async_enable(struct _usart_async_device *const device) +{ + hri_sercomusart_set_CTRLA_ENABLE_bit(device->hw); +} + +/** + * \brief Disable SERCOM module + */ +void _usart_sync_disable(struct _usart_sync_device *const device) +{ + hri_sercomusart_clear_CTRLA_ENABLE_bit(device->hw); +} + +/** + * \brief Disable SERCOM module + */ +void _usart_async_disable(struct _usart_async_device *const device) +{ + hri_sercomusart_clear_CTRLA_ENABLE_bit(device->hw); +} + +/** + * \brief Set baud rate + */ +void _usart_sync_set_baud_rate(struct _usart_sync_device *const device, const uint32_t baud_rate) +{ + _usart_set_baud_rate(device->hw, baud_rate); +} + +/** + * \brief Set baud rate + */ +void _usart_async_set_baud_rate(struct _usart_async_device *const device, const uint32_t baud_rate) +{ + _usart_set_baud_rate(device->hw, baud_rate); +} + +/** + * \brief Set data order + */ +void _usart_sync_set_data_order(struct _usart_sync_device *const device, const enum usart_data_order order) +{ + _usart_set_data_order(device->hw, order); +} + +/** + * \brief Set data order + */ +void _usart_async_set_data_order(struct _usart_async_device *const device, const enum usart_data_order order) +{ + _usart_set_data_order(device->hw, order); +} + +/** + * \brief Set mode + */ +void _usart_sync_set_mode(struct _usart_sync_device *const device, const enum usart_mode mode) +{ + _usart_set_mode(device->hw, mode); +} + +/** + * \brief Set mode + */ +void _usart_async_set_mode(struct _usart_async_device *const device, const enum usart_mode mode) +{ + _usart_set_mode(device->hw, mode); +} + +/** + * \brief Set parity + */ +void _usart_sync_set_parity(struct _usart_sync_device *const device, const enum usart_parity parity) +{ + _usart_set_parity(device->hw, parity); +} + +/** + * \brief Set parity + */ +void _usart_async_set_parity(struct _usart_async_device *const device, const enum usart_parity parity) +{ + _usart_set_parity(device->hw, parity); +} + +/** + * \brief Set stop bits mode + */ +void _usart_sync_set_stop_bits(struct _usart_sync_device *const device, const enum usart_stop_bits stop_bits) +{ + _usart_set_stop_bits(device->hw, stop_bits); +} + +/** + * \brief Set stop bits mode + */ +void _usart_async_set_stop_bits(struct _usart_async_device *const device, const enum usart_stop_bits stop_bits) +{ + _usart_set_stop_bits(device->hw, stop_bits); +} + +/** + * \brief Set character size + */ +void _usart_sync_set_character_size(struct _usart_sync_device *const device, const enum usart_character_size size) +{ + _usart_set_character_size(device->hw, size); +} + +/** + * \brief Set character size + */ +void _usart_async_set_character_size(struct _usart_async_device *const device, const enum usart_character_size size) +{ + _usart_set_character_size(device->hw, size); +} + +/** + * \brief Retrieve SERCOM usart status + */ +uint32_t _usart_sync_get_status(const struct _usart_sync_device *const device) +{ + return hri_sercomusart_read_STATUS_reg(device->hw); +} + +/** + * \brief Retrieve SERCOM usart status + */ +uint32_t _usart_async_get_status(const struct _usart_async_device *const device) +{ + return hri_sercomusart_read_STATUS_reg(device->hw); +} + +/** + * \brief Write a byte to the given SERCOM USART instance + */ +void _usart_sync_write_byte(struct _usart_sync_device *const device, uint8_t data) +{ + hri_sercomusart_write_DATA_reg(device->hw, data); +} + +/** + * \brief Write a byte to the given SERCOM USART instance + */ +void _usart_async_write_byte(struct _usart_async_device *const device, uint8_t data) +{ + hri_sercomusart_write_DATA_reg(device->hw, data); +} + +/** + * \brief Read a byte from the given SERCOM USART instance + */ +uint8_t _usart_sync_read_byte(const struct _usart_sync_device *const device) +{ + return hri_sercomusart_read_DATA_reg(device->hw); +} + +/** + * \brief Check if USART is ready to send next byte + */ +bool _usart_sync_is_ready_to_send(const struct _usart_sync_device *const device) +{ + return hri_sercomusart_get_interrupt_DRE_bit(device->hw); +} + +/** + * \brief Check if USART transmission complete + */ +bool _usart_sync_is_transmit_done(const struct _usart_sync_device *const device) +{ + return hri_sercomusart_get_interrupt_TXC_bit(device->hw); +} + +/** + * \brief Check if USART is ready to send next byte + */ +bool _usart_async_is_byte_sent(const struct _usart_async_device *const device) +{ + return hri_sercomusart_get_interrupt_DRE_bit(device->hw); +} + +/** + * \brief Check if there is data received by USART + */ +bool _usart_sync_is_byte_received(const struct _usart_sync_device *const device) +{ + return hri_sercomusart_get_interrupt_RXC_bit(device->hw); +} + +/** + * \brief Set the state of flow control pins + */ +void _usart_sync_set_flow_control_state(struct _usart_sync_device *const device, + const union usart_flow_control_state state) +{ + (void)device; + (void)state; +} + +/** + * \brief Set the state of flow control pins + */ +void _usart_async_set_flow_control_state(struct _usart_async_device *const device, + const union usart_flow_control_state state) +{ + (void)device; + (void)state; +} + +/** + * \brief Retrieve the state of flow control pins + */ +union usart_flow_control_state _usart_sync_get_flow_control_state(const struct _usart_sync_device *const device) +{ + (void)device; + union usart_flow_control_state state; + + state.value = 0; + state.bit.unavailable = 1; + return state; +} + +/** + * \brief Retrieve the state of flow control pins + */ +union usart_flow_control_state _usart_async_get_flow_control_state(const struct _usart_async_device *const device) +{ + (void)device; + union usart_flow_control_state state; + + state.value = 0; + state.bit.unavailable = 1; + return state; +} + +/** + * \brief Enable data register empty interrupt + */ +void _usart_async_enable_byte_sent_irq(struct _usart_async_device *const device) +{ + hri_sercomusart_set_INTEN_DRE_bit(device->hw); +} + +/** + * \brief Enable transmission complete interrupt + */ +void _usart_async_enable_tx_done_irq(struct _usart_async_device *const device) +{ + hri_sercomusart_set_INTEN_TXC_bit(device->hw); +} + +/** + * \brief Retrieve ordinal number of the given sercom hardware instance + */ +static uint8_t _sercom_get_hardware_index(const void *const hw) +{ +#ifdef _UNIT_TEST_ + return ((uint32_t)hw - (uint32_t)SERCOM0) / sizeof(Sercom); +#endif + + return ((uint32_t)hw - (uint32_t)SERCOM0) >> 10; +} + +/** + * \brief Retrieve ordinal number of the given SERCOM USART hardware instance + */ +uint8_t _usart_sync_get_hardware_index(const struct _usart_sync_device *const device) +{ + return _sercom_get_hardware_index(device->hw); +} + +/** + * \brief Retrieve ordinal number of the given SERCOM USART hardware instance + */ +uint8_t _usart_async_get_hardware_index(const struct _usart_async_device *const device) +{ + return _sercom_get_hardware_index(device->hw); +} + +/** + * \brief Enable/disable USART interrupt + */ +void _usart_async_set_irq_state(struct _usart_async_device *const device, const enum _usart_async_callback_type type, + const bool state) +{ + ASSERT(device); + + if (USART_ASYNC_BYTE_SENT == type || USART_ASYNC_TX_DONE == type) { + hri_sercomusart_write_INTEN_DRE_bit(device->hw, state); + hri_sercomusart_write_INTEN_TXC_bit(device->hw, state); + } else if (USART_ASYNC_RX_DONE == type) { + hri_sercomusart_write_INTEN_RXC_bit(device->hw, state); + } else if (USART_ASYNC_ERROR == type) { + hri_sercomusart_write_INTEN_ERROR_bit(device->hw, state); + } +} + +/** + * \internal Retrieve ordinal number of the given sercom hardware instance + * + * \param[in] hw The pointer to hardware instance + + * \return The ordinal number of the given sercom hardware instance + */ +static uint8_t _get_sercom_index(const void *const hw) +{ + uint8_t sercom_offset = _sercom_get_hardware_index(hw); + uint8_t i; + + for (i = 0; i < ARRAY_SIZE(_usarts); i++) { + if (_usarts[i].number == sercom_offset) { + return i; + } + } + + ASSERT(false); + return 0; +} + +/** + * \brief Init irq param with the given sercom hardware instance + */ +static void _sercom_init_irq_param(const void *const hw, void *dev) +{ +} + +/** + * \internal Initialize SERCOM USART + * + * \param[in] hw The pointer to hardware instance + * + * \return The status of initialization + */ +static int32_t _usart_init(void *const hw) +{ + uint8_t i = _get_sercom_index(hw); + + if (!hri_sercomusart_is_syncing(hw, SERCOM_USART_SYNCBUSY_SWRST)) { + uint32_t mode = _usarts[i].ctrl_a & SERCOM_USART_CTRLA_MODE_Msk; + if (hri_sercomusart_get_CTRLA_reg(hw, SERCOM_USART_CTRLA_ENABLE)) { + hri_sercomusart_clear_CTRLA_ENABLE_bit(hw); + hri_sercomusart_wait_for_sync(hw, SERCOM_USART_SYNCBUSY_ENABLE); + } + hri_sercomusart_write_CTRLA_reg(hw, SERCOM_USART_CTRLA_SWRST | mode); + } + hri_sercomusart_wait_for_sync(hw, SERCOM_USART_SYNCBUSY_SWRST); + + hri_sercomusart_write_CTRLA_reg(hw, _usarts[i].ctrl_a); + hri_sercomusart_write_CTRLB_reg(hw, _usarts[i].ctrl_b); + if ((_usarts[i].ctrl_a & SERCOM_USART_CTRLA_SAMPR(0x1)) || (_usarts[i].ctrl_a & SERCOM_USART_CTRLA_SAMPR(0x3))) { + ((Sercom *)hw)->USART.BAUD.FRAC.BAUD = _usarts[i].baud; + ((Sercom *)hw)->USART.BAUD.FRAC.FP = _usarts[i].fractional; + } else { + hri_sercomusart_write_BAUD_reg(hw, _usarts[i].baud); + } + + hri_sercomusart_write_RXPL_reg(hw, _usarts[i].rxpl); + hri_sercomusart_write_DBGCTRL_reg(hw, _usarts[i].debug_ctrl); + + return ERR_NONE; +} + +/** + * \internal De-initialize SERCOM USART + * + * \param[in] hw The pointer to hardware instance + */ +static inline void _usart_deinit(void *const hw) +{ + hri_sercomusart_clear_CTRLA_ENABLE_bit(hw); + hri_sercomusart_set_CTRLA_SWRST_bit(hw); +} + +/** + * \internal Calculate baud rate register value + * + * \param[in] baud Required baud rate + * \param[in] clock_rate SERCOM clock frequency + * \param[in] samples The number of samples + * \param[in] mode USART mode + * \param[in] fraction A fraction value + * + * \return Calculated baud rate register value + */ +static uint16_t _usart_calculate_baud_rate(const uint32_t baud, const uint32_t clock_rate, const uint8_t samples, + const enum usart_baud_rate_mode mode, const uint8_t fraction) +{ + if (USART_BAUDRATE_ASYNCH_ARITHMETIC == mode) { + return 65536 - ((uint64_t)65536 * samples * baud) / clock_rate; + } + + if (USART_BAUDRATE_ASYNCH_FRACTIONAL == mode) { + return clock_rate / baud / samples + SERCOM_USART_BAUD_FRACFP_FP(fraction); + } + + if (USART_BAUDRATE_SYNCH == mode) { + return clock_rate / baud / 2 - 1; + } + + return 0; +} + +/** + * \internal Set baud rate + * + * \param[in] device The pointer to USART device instance + * \param[in] baud_rate A baud rate to set + */ +static void _usart_set_baud_rate(void *const hw, const uint32_t baud_rate) +{ + bool enabled = hri_sercomusart_get_CTRLA_ENABLE_bit(hw); + + hri_sercomusart_clear_CTRLA_ENABLE_bit(hw); + + CRITICAL_SECTION_ENTER() + hri_sercomusart_wait_for_sync(hw, SERCOM_USART_SYNCBUSY_ENABLE); + hri_sercomusart_write_BAUD_reg(hw, baud_rate); + CRITICAL_SECTION_LEAVE() + + hri_sercomusart_write_CTRLA_ENABLE_bit(hw, enabled); +} + +/** + * \internal Set data order + * + * \param[in] device The pointer to USART device instance + * \param[in] order A data order to set + */ +static void _usart_set_data_order(void *const hw, const enum usart_data_order order) +{ + bool enabled = hri_sercomusart_get_CTRLA_ENABLE_bit(hw); + + hri_sercomusart_clear_CTRLA_ENABLE_bit(hw); + + CRITICAL_SECTION_ENTER() + hri_sercomusart_wait_for_sync(hw, SERCOM_USART_SYNCBUSY_ENABLE); + hri_sercomusart_write_CTRLA_DORD_bit(hw, order); + CRITICAL_SECTION_LEAVE() + + hri_sercomusart_write_CTRLA_ENABLE_bit(hw, enabled); +} + +/** + * \internal Set mode + * + * \param[in] device The pointer to USART device instance + * \param[in] mode A mode to set + */ +static void _usart_set_mode(void *const hw, const enum usart_mode mode) +{ + bool enabled = hri_sercomusart_get_CTRLA_ENABLE_bit(hw); + + hri_sercomusart_clear_CTRLA_ENABLE_bit(hw); + + CRITICAL_SECTION_ENTER() + hri_sercomusart_wait_for_sync(hw, SERCOM_USART_SYNCBUSY_ENABLE); + hri_sercomusart_write_CTRLA_CMODE_bit(hw, mode); + CRITICAL_SECTION_LEAVE() + + hri_sercomusart_write_CTRLA_ENABLE_bit(hw, enabled); +} + +/** + * \internal Set parity + * + * \param[in] device The pointer to USART device instance + * \param[in] parity A parity to set + */ +static void _usart_set_parity(void *const hw, const enum usart_parity parity) +{ + bool enabled = hri_sercomusart_get_CTRLA_ENABLE_bit(hw); + + hri_sercomusart_clear_CTRLA_ENABLE_bit(hw); + + CRITICAL_SECTION_ENTER() + hri_sercomusart_wait_for_sync(hw, SERCOM_USART_SYNCBUSY_ENABLE); + + if (USART_PARITY_NONE != parity) { + hri_sercomusart_set_CTRLA_FORM_bf(hw, 1); + } else { + hri_sercomusart_clear_CTRLA_FORM_bf(hw, 1); + } + + hri_sercomusart_write_CTRLB_PMODE_bit(hw, parity); + CRITICAL_SECTION_LEAVE() + + hri_sercomusart_write_CTRLA_ENABLE_bit(hw, enabled); +} + +/** + * \internal Set stop bits mode + * + * \param[in] device The pointer to USART device instance + * \param[in] stop_bits A stop bits mode to set + */ +static void _usart_set_stop_bits(void *const hw, const enum usart_stop_bits stop_bits) +{ + bool enabled = hri_sercomusart_get_CTRLA_ENABLE_bit(hw); + + hri_sercomusart_clear_CTRLA_ENABLE_bit(hw); + + CRITICAL_SECTION_ENTER() + hri_sercomusart_wait_for_sync(hw, SERCOM_USART_SYNCBUSY_ENABLE); + hri_sercomusart_write_CTRLB_SBMODE_bit(hw, stop_bits); + CRITICAL_SECTION_LEAVE() + + hri_sercomusart_write_CTRLA_ENABLE_bit(hw, enabled); +} + +/** + * \internal Set character size + * + * \param[in] device The pointer to USART device instance + * \param[in] size A character size to set + */ +static void _usart_set_character_size(void *const hw, const enum usart_character_size size) +{ + bool enabled = hri_sercomusart_get_CTRLA_ENABLE_bit(hw); + + hri_sercomusart_clear_CTRLA_ENABLE_bit(hw); + + CRITICAL_SECTION_ENTER() + hri_sercomusart_wait_for_sync(hw, SERCOM_USART_SYNCBUSY_ENABLE); + hri_sercomusart_write_CTRLB_CHSIZE_bf(hw, size); + CRITICAL_SECTION_LEAVE() + + if (enabled) { + hri_sercomusart_set_CTRLA_ENABLE_bit(hw); + } +} + + /* Sercom I2C implementation */ + +#ifndef CONF_SERCOM_0_I2CM_ENABLE +#define CONF_SERCOM_0_I2CM_ENABLE 0 +#endif +#ifndef CONF_SERCOM_1_I2CM_ENABLE +#define CONF_SERCOM_1_I2CM_ENABLE 0 +#endif +#ifndef CONF_SERCOM_2_I2CM_ENABLE +#define CONF_SERCOM_2_I2CM_ENABLE 0 +#endif +#ifndef CONF_SERCOM_3_I2CM_ENABLE +#define CONF_SERCOM_3_I2CM_ENABLE 0 +#endif +#ifndef CONF_SERCOM_4_I2CM_ENABLE +#define CONF_SERCOM_4_I2CM_ENABLE 0 +#endif +#ifndef CONF_SERCOM_5_I2CM_ENABLE +#define CONF_SERCOM_5_I2CM_ENABLE 0 +#endif +#ifndef CONF_SERCOM_6_I2CM_ENABLE +#define CONF_SERCOM_6_I2CM_ENABLE 0 +#endif +#ifndef CONF_SERCOM_7_I2CM_ENABLE +#define CONF_SERCOM_7_I2CM_ENABLE 0 +#endif + +/** Amount of SERCOM that is used as I2C Master. */ +#define SERCOM_I2CM_AMOUNT \ + (CONF_SERCOM_0_I2CM_ENABLE + CONF_SERCOM_1_I2CM_ENABLE + CONF_SERCOM_2_I2CM_ENABLE + CONF_SERCOM_3_I2CM_ENABLE \ + + CONF_SERCOM_4_I2CM_ENABLE + CONF_SERCOM_5_I2CM_ENABLE + CONF_SERCOM_6_I2CM_ENABLE + CONF_SERCOM_7_I2CM_ENABLE) + +/** + * \brief Macro is used to fill i2cm configuration structure based on + * its number + * + * \param[in] n The number of structures + */ +#define I2CM_CONFIGURATION(n) \ + { \ + (n), \ + (SERCOM_I2CM_CTRLA_MODE_I2C_MASTER) | (CONF_SERCOM_##n##_I2CM_RUNSTDBY << SERCOM_I2CM_CTRLA_RUNSTDBY_Pos) \ + | (CONF_SERCOM_##n##_I2CM_SPEED << SERCOM_I2CM_CTRLA_SPEED_Pos) \ + | (CONF_SERCOM_##n##_I2CM_MEXTTOEN << SERCOM_I2CM_CTRLA_MEXTTOEN_Pos) \ + | (CONF_SERCOM_##n##_I2CM_SEXTTOEN << SERCOM_I2CM_CTRLA_SEXTTOEN_Pos) \ + | (CONF_SERCOM_##n##_I2CM_INACTOUT << SERCOM_I2CM_CTRLA_INACTOUT_Pos) \ + | (CONF_SERCOM_##n##_I2CM_LOWTOUT << SERCOM_I2CM_CTRLA_LOWTOUTEN_Pos) \ + | (CONF_SERCOM_##n##_I2CM_SDAHOLD << SERCOM_I2CM_CTRLA_SDAHOLD_Pos), \ + SERCOM_I2CM_CTRLB_SMEN, (uint32_t)(CONF_SERCOM_##n##_I2CM_BAUD_RATE), \ + CONF_SERCOM_##n##_I2CM_DEBUG_STOP_MODE, CONF_SERCOM_##n##_I2CM_TRISE, CONF_GCLK_SERCOM##n##_CORE_FREQUENCY \ + } + +#define ERROR_FLAG (1 << 7) +#define SB_FLAG (1 << 1) +#define MB_FLAG (1 << 0) + +#define CMD_STOP 0x3 +#define I2C_IDLE 0x1 +#define I2C_SM 0x0 +#define I2C_FM 0x1 +#define I2C_HS 0x2 +#define TEN_ADDR_FRAME 0x78 +#define TEN_ADDR_MASK 0x3ff +#define SEVEN_ADDR_MASK 0x7f + +/** + * \brief SERCOM I2CM configuration type + */ +struct i2cm_configuration { + uint8_t number; + hri_sercomi2cm_ctrla_reg_t ctrl_a; + hri_sercomi2cm_ctrlb_reg_t ctrl_b; + hri_sercomi2cm_baud_reg_t baud; + hri_sercomi2cm_dbgctrl_reg_t dbgctrl; + uint16_t trise; + uint32_t clk; /* SERCOM peripheral clock frequency */ +}; + +static inline int32_t _i2c_m_enable_implementation(void *hw); +static int32_t _i2c_m_sync_init_impl(struct _i2c_m_service *const service, void *const hw); + +#if SERCOM_I2CM_AMOUNT < 1 +/** Dummy array to pass compiling. */ +static struct i2cm_configuration _i2cms[1] = {{0}}; +#else +/** + * \brief Array of SERCOM I2CM configurations + */ +static struct i2cm_configuration _i2cms[] = { +#if CONF_SERCOM_0_I2CM_ENABLE == 1 + I2CM_CONFIGURATION(0), +#endif +#if CONF_SERCOM_1_I2CM_ENABLE == 1 + I2CM_CONFIGURATION(1), +#endif +#if CONF_SERCOM_2_I2CM_ENABLE == 1 + I2CM_CONFIGURATION(2), +#endif +#if CONF_SERCOM_3_I2CM_ENABLE == 1 + I2CM_CONFIGURATION(3), +#endif +#if CONF_SERCOM_4_I2CM_ENABLE == 1 + I2CM_CONFIGURATION(4), +#endif +#if CONF_SERCOM_5_I2CM_ENABLE == 1 + I2CM_CONFIGURATION(5), +#endif +#if CONF_SERCOM_6_I2CM_ENABLE == 1 + I2CM_CONFIGURATION(6), +#endif +#if CONF_SERCOM_7_I2CM_ENABLE == 1 + I2CM_CONFIGURATION(7), +#endif +}; +#endif + +/** + * \internal Retrieve ordinal number of the given sercom hardware instance + * + * \param[in] hw The pointer to hardware instance + + * \return The ordinal number of the given sercom hardware instance + */ +static int8_t _get_i2cm_index(const void *const hw) +{ + uint8_t sercom_offset = _sercom_get_hardware_index(hw); + uint8_t i; + + for (i = 0; i < ARRAY_SIZE(_i2cms); i++) { + if (_i2cms[i].number == sercom_offset) { + return i; + } + } + + ASSERT(false); + return -1; +} + +static inline void _sercom_i2c_send_stop(void *const hw) +{ + hri_sercomi2cm_set_CTRLB_CMD_bf(hw, CMD_STOP); +} + +/** + * \brief SERCOM I2CM analyze hardware status and transfer next byte + */ +static inline int32_t _sercom_i2c_sync_analyse_flags(void *const hw, uint32_t flags, struct _i2c_m_msg *const msg) +{ + int sclsm = hri_sercomi2cm_get_CTRLA_SCLSM_bit(hw); + uint16_t status = hri_sercomi2cm_read_STATUS_reg(hw); + + if (flags & MB_FLAG) { + /* tx error */ + if (status & SERCOM_I2CM_STATUS_ARBLOST) { + hri_sercomi2cm_clear_interrupt_MB_bit(hw); + msg->flags |= I2C_M_FAIL; + msg->flags &= ~I2C_M_BUSY; + + if (status & SERCOM_I2CM_STATUS_BUSERR) { + return I2C_ERR_BUS; + } + + return I2C_ERR_BAD_ADDRESS; + } else { + if (status & SERCOM_I2CM_STATUS_RXNACK) { + + /* Slave rejects to receive more data */ + if (msg->len > 0) { + msg->flags |= I2C_M_FAIL; + } + + if (msg->flags & I2C_M_STOP) { + _sercom_i2c_send_stop(hw); + } + + msg->flags &= ~I2C_M_BUSY; + + return I2C_NACK; + } + + if (msg->flags & I2C_M_TEN) { + hri_sercomi2cm_write_ADDR_reg(hw, + ((((msg->addr & TEN_ADDR_MASK) >> 8) | TEN_ADDR_FRAME) << 1) | I2C_M_RD + | (hri_sercomi2cm_read_ADDR_reg(hw) & SERCOM_I2CM_ADDR_HS)); + msg->flags &= ~I2C_M_TEN; + + return I2C_OK; + } + + if (msg->len == 0) { + if (msg->flags & I2C_M_STOP) { + _sercom_i2c_send_stop(hw); + } + + msg->flags &= ~I2C_M_BUSY; + } else { + hri_sercomi2cm_write_DATA_reg(hw, *msg->buffer); + msg->buffer++; + msg->len--; + } + + return I2C_OK; + } + } else if (flags & SB_FLAG) { + if ((msg->len) && !(status & SERCOM_I2CM_STATUS_RXNACK)) { + msg->len--; + + /* last byte, send nack */ + if ((msg->len == 0 && !sclsm) || (msg->len == 1 && sclsm)) { + hri_sercomi2cm_set_CTRLB_ACKACT_bit(hw); + } + + if (msg->len == 0) { + if (msg->flags & I2C_M_STOP) { + hri_sercomi2cm_clear_CTRLB_SMEN_bit(hw); + _sercom_i2c_send_stop(hw); + } + + msg->flags &= ~I2C_M_BUSY; + } + + /* Accessing DATA.DATA auto-triggers I2C bus operations. + * The operation performed depends on the state of + * CTRLB.ACKACT, CTRLB.SMEN + **/ + *msg->buffer++ = hri_sercomi2cm_read_DATA_reg(hw); + } else { + hri_sercomi2cm_clear_interrupt_SB_bit(hw); + return I2C_NACK; + } + + hri_sercomi2cm_clear_interrupt_SB_bit(hw); + } + + return I2C_OK; +} + +/** + * \brief Enable the i2c master module + * + * \param[in] i2c_dev The pointer to i2c device + */ +int32_t _i2c_m_async_enable(struct _i2c_m_async_device *const i2c_dev) +{ + ASSERT(i2c_dev); + + return _i2c_m_enable_implementation(i2c_dev->hw); +} + +/** + * \brief Disable the i2c master module + * + * \param[in] i2c_dev The pointer to i2c device + */ +int32_t _i2c_m_async_disable(struct _i2c_m_async_device *const i2c_dev) +{ + void *hw = i2c_dev->hw; + + ASSERT(i2c_dev); + ASSERT(i2c_dev->hw); + + NVIC_DisableIRQ((IRQn_Type)_sercom_get_irq_num(hw)); + hri_sercomi2cm_clear_CTRLA_ENABLE_bit(hw); + + return ERR_NONE; +} + +/** + * \brief Set baudrate of master + * + * \param[in] i2c_dev The pointer to i2c device + * \param[in] clkrate The clock rate of i2c master, in KHz + * \param[in] baudrate The baud rate desired for i2c master, in KHz + */ +int32_t _i2c_m_async_set_baudrate(struct _i2c_m_async_device *const i2c_dev, uint32_t clkrate, uint32_t baudrate) +{ + uint32_t tmp; + void * hw = i2c_dev->hw; + + if (hri_sercomi2cm_get_CTRLA_ENABLE_bit(hw)) { + return ERR_DENIED; + } + + tmp = _get_i2cm_index(hw); + clkrate = _i2cms[tmp].clk / 1000; + + if (i2c_dev->service.mode == I2C_STANDARD_MODE) { + tmp = (uint32_t)((clkrate - 10 * baudrate - baudrate * clkrate * (i2c_dev->service.trise * 0.000000001)) + / (2 * baudrate)); + hri_sercomi2cm_write_BAUD_BAUD_bf(hw, tmp); + } else if (i2c_dev->service.mode == I2C_FASTMODE) { + tmp = (uint32_t)((clkrate - 10 * baudrate - baudrate * clkrate * (i2c_dev->service.trise * 0.000000001)) + / (2 * baudrate)); + hri_sercomi2cm_write_BAUD_BAUD_bf(hw, tmp); + } else if (i2c_dev->service.mode == I2C_HIGHSPEED_MODE) { + tmp = (clkrate - 2 * baudrate) / (2 * baudrate); + hri_sercomi2cm_write_BAUD_HSBAUD_bf(hw, tmp); + } else { + /* error baudrate */ + return ERR_INVALID_ARG; + } + + return ERR_NONE; +} + +/** + * \brief Retrieve IRQ number for the given hardware instance + */ +static uint8_t _sercom_get_irq_num(const void *const hw) +{ + return SERCOM0_IRQn + _sercom_get_hardware_index(hw); +} + +/** + * \brief Initialize sercom i2c module to use in async mode + * + * \param[in] i2c_dev The pointer to i2c device + */ +int32_t _i2c_m_async_init(struct _i2c_m_async_device *const i2c_dev, void *const hw) +{ + int32_t init_status; + + ASSERT(i2c_dev); + + i2c_dev->hw = hw; + + init_status = _i2c_m_sync_init_impl(&i2c_dev->service, hw); + if (init_status) { + return init_status; + } + + _sercom_init_irq_param(hw, (void *)i2c_dev); + NVIC_DisableIRQ((IRQn_Type)_sercom_get_irq_num(hw)); + NVIC_ClearPendingIRQ((IRQn_Type)_sercom_get_irq_num(hw)); + NVIC_EnableIRQ((IRQn_Type)_sercom_get_irq_num(hw)); + + return ERR_NONE; +} + +/** + * \brief Deinitialize sercom i2c module + * + * \param[in] i2c_dev The pointer to i2c device + */ +int32_t _i2c_m_async_deinit(struct _i2c_m_async_device *const i2c_dev) +{ + ASSERT(i2c_dev); + + hri_sercomi2cm_clear_CTRLA_ENABLE_bit(i2c_dev->hw); + hri_sercomi2cm_set_CTRLA_SWRST_bit(i2c_dev->hw); + + return ERR_NONE; +} + +/** + * \brief Transfer the slave address to bus, which will start the transfer + * + * \param[in] i2c_dev The pointer to i2c device + */ +static int32_t _sercom_i2c_send_address(struct _i2c_m_async_device *const i2c_dev) +{ + void * hw = i2c_dev->hw; + struct _i2c_m_msg *msg = &i2c_dev->service.msg; + int sclsm = hri_sercomi2cm_get_CTRLA_SCLSM_bit(hw); + + ASSERT(i2c_dev); + + if (msg->len == 1 && sclsm) { + hri_sercomi2cm_set_CTRLB_ACKACT_bit(hw); + } else { + hri_sercomi2cm_clear_CTRLB_ACKACT_bit(hw); + } + + /* ten bit address */ + if (msg->addr & I2C_M_TEN) { + if (msg->flags & I2C_M_RD) { + msg->flags |= I2C_M_TEN; + } + + hri_sercomi2cm_write_ADDR_reg(hw, + ((msg->addr & TEN_ADDR_MASK) << 1) | SERCOM_I2CM_ADDR_TENBITEN + | (hri_sercomi2cm_read_ADDR_reg(hw) & SERCOM_I2CM_ADDR_HS)); + } else { + hri_sercomi2cm_write_ADDR_reg(hw, + ((msg->addr & SEVEN_ADDR_MASK) << 1) | (msg->flags & I2C_M_RD ? I2C_M_RD : 0x0) + | (hri_sercomi2cm_read_ADDR_reg(hw) & SERCOM_I2CM_ADDR_HS)); + } + + return ERR_NONE; +} + +/** + * \brief Transfer data specified by msg + * + * \param[in] i2c_dev The pointer to i2c device + * \param[in] msg The pointer to i2c message + * + * \return Transfer status. + * \retval 0 Transfer success + * \retval <0 Transfer fail, return the error code + */ +int32_t _i2c_m_async_transfer(struct _i2c_m_async_device *i2c_dev, struct _i2c_m_msg *msg) +{ + int ret; + + ASSERT(i2c_dev); + ASSERT(i2c_dev->hw); + ASSERT(msg); + + if (msg->len == 0) { + return ERR_NONE; + } + + if (i2c_dev->service.msg.flags & I2C_M_BUSY) { + return ERR_BUSY; + } + + msg->flags |= I2C_M_BUSY; + i2c_dev->service.msg = *msg; + hri_sercomi2cm_set_CTRLB_SMEN_bit(i2c_dev->hw); + + ret = _sercom_i2c_send_address(i2c_dev); + + if (ret) { + i2c_dev->service.msg.flags &= ~I2C_M_BUSY; + + return ret; + } + + return ERR_NONE; +} + +/** + * \brief Set callback to be called in interrupt handler + * + * \param[in] i2c_dev The pointer to master i2c device + * \param[in] type The callback type + * \param[in] func The callback function pointer + */ +int32_t _i2c_m_async_register_callback(struct _i2c_m_async_device *const i2c_dev, enum _i2c_m_async_callback_type type, + FUNC_PTR func) +{ + switch (type) { + case I2C_M_ASYNC_DEVICE_ERROR: + i2c_dev->cb.error = (_i2c_error_cb_t)func; + break; + case I2C_M_ASYNC_DEVICE_TX_COMPLETE: + i2c_dev->cb.tx_complete = (_i2c_complete_cb_t)func; + break; + case I2C_M_ASYNC_DEVICE_RX_COMPLETE: + i2c_dev->cb.rx_complete = (_i2c_complete_cb_t)func; + break; + default: + /* error */ + break; + } + + return ERR_NONE; +} + +/** + * \brief Set stop condition on I2C + * + * \param i2c_dev Pointer to master i2c device + * + * \return Operation status + * \retval I2C_OK Operation was successfull + */ +int32_t _i2c_m_async_send_stop(struct _i2c_m_async_device *const i2c_dev) +{ + void *hw = i2c_dev->hw; + + _sercom_i2c_send_stop(hw); + + return I2C_OK; +} + +/** + * \brief Get number of bytes left in transfer buffer + * + * \param i2c_dev Pointer to i2c master device + * + * \return Bytes left in buffer + * \retval =>0 Bytes left in buffer + */ +int32_t _i2c_m_async_get_bytes_left(struct _i2c_m_async_device *const i2c_dev) +{ + if (i2c_dev->service.msg.flags & I2C_M_BUSY) { + return i2c_dev->service.msg.len; + } + + return 0; +} + +/** + * \brief Initialize sercom i2c module to use in sync mode + * + * \param[in] i2c_dev The pointer to i2c device + */ +int32_t _i2c_m_sync_init(struct _i2c_m_sync_device *const i2c_dev, void *const hw) +{ + ASSERT(i2c_dev); + + i2c_dev->hw = hw; + + return _i2c_m_sync_init_impl(&i2c_dev->service, hw); +} + +/** + * \brief Deinitialize sercom i2c module + * + * \param[in] i2c_dev The pointer to i2c device + */ +int32_t _i2c_m_sync_deinit(struct _i2c_m_sync_device *const i2c_dev) +{ + ASSERT(i2c_dev); + + hri_sercomi2cm_clear_CTRLA_ENABLE_bit(i2c_dev->hw); + hri_sercomi2cm_set_CTRLA_SWRST_bit(i2c_dev->hw); + + return ERR_NONE; +} + +/** + * \brief Enable the i2c master module + * + * \param[in] i2c_dev The pointer to i2c device + */ +int32_t _i2c_m_sync_enable(struct _i2c_m_sync_device *const i2c_dev) +{ + ASSERT(i2c_dev); + + return _i2c_m_enable_implementation(i2c_dev->hw); +} + +/** + * \brief Disable the i2c master module + * + * \param[in] i2c_dev The pointer to i2c device + */ +int32_t _i2c_m_sync_disable(struct _i2c_m_sync_device *const i2c_dev) +{ + void *hw = i2c_dev->hw; + + ASSERT(i2c_dev); + ASSERT(i2c_dev->hw); + + hri_sercomi2cm_clear_CTRLA_ENABLE_bit(hw); + + return ERR_NONE; +} + +/** + * \brief Set baudrate of master + * + * \param[in] i2c_dev The pointer to i2c device + * \param[in] clkrate The clock rate of i2c master, in KHz + * \param[in] baudrate The baud rate desired for i2c master, in KHz + */ +int32_t _i2c_m_sync_set_baudrate(struct _i2c_m_sync_device *const i2c_dev, uint32_t clkrate, uint32_t baudrate) +{ + uint32_t tmp; + void * hw = i2c_dev->hw; + + if (hri_sercomi2cm_get_CTRLA_ENABLE_bit(hw)) { + return ERR_DENIED; + } + + tmp = _get_i2cm_index(hw); + clkrate = _i2cms[tmp].clk / 1000; + + if (i2c_dev->service.mode == I2C_STANDARD_MODE) { + tmp = (uint32_t)((clkrate - 10 * baudrate - baudrate * clkrate * (i2c_dev->service.trise * 0.000000001)) + / (2 * baudrate)); + hri_sercomi2cm_write_BAUD_BAUD_bf(hw, tmp); + } else if (i2c_dev->service.mode == I2C_FASTMODE) { + tmp = (uint32_t)((clkrate - 10 * baudrate - baudrate * clkrate * (i2c_dev->service.trise * 0.000000001)) + / (2 * baudrate)); + hri_sercomi2cm_write_BAUD_BAUD_bf(hw, tmp); + } else if (i2c_dev->service.mode == I2C_HIGHSPEED_MODE) { + tmp = (clkrate - 2 * baudrate) / (2 * baudrate); + hri_sercomi2cm_write_BAUD_HSBAUD_bf(hw, tmp); + } else { + /* error baudrate */ + return ERR_INVALID_ARG; + } + + return ERR_NONE; +} + +/** + * \brief Enable/disable I2C master interrupt + */ +void _i2c_m_async_set_irq_state(struct _i2c_m_async_device *const device, const enum _i2c_m_async_callback_type type, + const bool state) +{ + if (I2C_M_ASYNC_DEVICE_TX_COMPLETE == type || I2C_M_ASYNC_DEVICE_RX_COMPLETE == type) { + hri_sercomi2cm_write_INTEN_SB_bit(device->hw, state); + hri_sercomi2cm_write_INTEN_MB_bit(device->hw, state); + } else if (I2C_M_ASYNC_DEVICE_ERROR == type) { + hri_sercomi2cm_write_INTEN_ERROR_bit(device->hw, state); + } +} + +/** + * \brief Wait for bus response + * + * \param[in] i2c_dev The pointer to i2c device + * \param[in] flags Store the hardware response + * + * \return Bus response status. + * \retval 0 Bus response status OK + * \retval <0 Bus response fail + */ +inline static int32_t _sercom_i2c_sync_wait_bus(struct _i2c_m_sync_device *const i2c_dev, uint32_t *flags) +{ + uint32_t timeout = 65535; + void * hw = i2c_dev->hw; + + do { + *flags = hri_sercomi2cm_read_INTFLAG_reg(hw); + + if (timeout-- == 0) { + return I2C_ERR_BUS; + } + } while (!(*flags & MB_FLAG) && !(*flags & SB_FLAG)); + + return I2C_OK; +} + +/** + * \brief Send the slave address to bus, which will start the transfer + * + * \param[in] i2c_dev The pointer to i2c device + */ +static int32_t _sercom_i2c_sync_send_address(struct _i2c_m_sync_device *const i2c_dev) +{ + void * hw = i2c_dev->hw; + struct _i2c_m_msg *msg = &i2c_dev->service.msg; + int sclsm = hri_sercomi2cm_get_CTRLA_SCLSM_bit(hw); + uint32_t flags; + + ASSERT(i2c_dev); + + if (msg->len == 1 && sclsm) { + hri_sercomi2cm_set_CTRLB_ACKACT_bit(hw); + } else { + hri_sercomi2cm_clear_CTRLB_ACKACT_bit(hw); + } + + /* ten bit address */ + if (msg->addr & I2C_M_TEN) { + if (msg->flags & I2C_M_RD) { + msg->flags |= I2C_M_TEN; + } + + hri_sercomi2cm_write_ADDR_reg(hw, + ((msg->addr & TEN_ADDR_MASK) << 1) | SERCOM_I2CM_ADDR_TENBITEN + | (hri_sercomi2cm_read_ADDR_reg(hw) & SERCOM_I2CM_ADDR_HS)); + } else { + hri_sercomi2cm_write_ADDR_reg(hw, + ((msg->addr & SEVEN_ADDR_MASK) << 1) | (msg->flags & I2C_M_RD ? I2C_M_RD : 0x0) + | (hri_sercomi2cm_read_ADDR_reg(hw) & SERCOM_I2CM_ADDR_HS)); + } + + _sercom_i2c_sync_wait_bus(i2c_dev, &flags); + return _sercom_i2c_sync_analyse_flags(hw, flags, msg); +} + +/** + * \brief Transfer data specified by msg + * + * \param[in] i2c_dev The pointer to i2c device + * \param[in] msg The pointer to i2c message + * + * \return Transfer status. + * \retval 0 Transfer success + * \retval <0 Transfer fail or partial fail, return the error code + */ +int32_t _i2c_m_sync_transfer(struct _i2c_m_sync_device *const i2c_dev, struct _i2c_m_msg *msg) +{ + uint32_t flags; + int ret; + void * hw = i2c_dev->hw; + + ASSERT(i2c_dev); + ASSERT(i2c_dev->hw); + ASSERT(msg); + + if (i2c_dev->service.msg.flags & I2C_M_BUSY) { + return I2C_ERR_BUSY; + } + + msg->flags |= I2C_M_BUSY; + i2c_dev->service.msg = *msg; + hri_sercomi2cm_set_CTRLB_SMEN_bit(hw); + + ret = _sercom_i2c_sync_send_address(i2c_dev); + + if (ret) { + i2c_dev->service.msg.flags &= ~I2C_M_BUSY; + + return ret; + } + + while (i2c_dev->service.msg.flags & I2C_M_BUSY) { + ret = _sercom_i2c_sync_wait_bus(i2c_dev, &flags); + + if (ret) { + if (msg->flags & I2C_M_STOP) { + _sercom_i2c_send_stop(hw); + } + + i2c_dev->service.msg.flags &= ~I2C_M_BUSY; + + return ret; + } + + ret = _sercom_i2c_sync_analyse_flags(hw, flags, &i2c_dev->service.msg); + } + + return ret; +} + +int32_t _i2c_m_sync_send_stop(struct _i2c_m_sync_device *const i2c_dev) +{ + void *hw = i2c_dev->hw; + + _sercom_i2c_send_stop(hw); + + return I2C_OK; +} + +static inline int32_t _i2c_m_enable_implementation(void *const hw) +{ + int timeout = 65535; + int timeout_attempt = 4; + + ASSERT(hw); + + /* Enable interrupts */ + hri_sercomi2cm_set_CTRLA_ENABLE_bit(hw); + + while (hri_sercomi2cm_read_STATUS_BUSSTATE_bf(hw) != I2C_IDLE) { + timeout--; + + if (timeout <= 0) { + if (--timeout_attempt) + timeout = 65535; + else + return I2C_ERR_BUSY; + hri_sercomi2cm_clear_STATUS_reg(hw, SERCOM_I2CM_STATUS_BUSSTATE(I2C_IDLE)); + } + } + return ERR_NONE; +} + +static int32_t _i2c_m_sync_init_impl(struct _i2c_m_service *const service, void *const hw) +{ + uint8_t i = _get_i2cm_index(hw); + + if (!hri_sercomi2cm_is_syncing(hw, SERCOM_I2CM_SYNCBUSY_SWRST)) { + uint32_t mode = _i2cms[i].ctrl_a & SERCOM_I2CM_CTRLA_MODE_Msk; + if (hri_sercomi2cm_get_CTRLA_reg(hw, SERCOM_I2CM_CTRLA_ENABLE)) { + hri_sercomi2cm_clear_CTRLA_ENABLE_bit(hw); + hri_sercomi2cm_wait_for_sync(hw, SERCOM_I2CM_SYNCBUSY_ENABLE); + } + hri_sercomi2cm_write_CTRLA_reg(hw, SERCOM_I2CM_CTRLA_SWRST | mode); + } + hri_sercomi2cm_wait_for_sync(hw, SERCOM_I2CM_SYNCBUSY_SWRST); + + hri_sercomi2cm_write_CTRLA_reg(hw, _i2cms[i].ctrl_a); + hri_sercomi2cm_write_CTRLB_reg(hw, _i2cms[i].ctrl_b); + hri_sercomi2cm_write_BAUD_reg(hw, _i2cms[i].baud); + + service->mode = (_i2cms[i].ctrl_a & SERCOM_I2CM_CTRLA_SPEED_Msk) >> SERCOM_I2CM_CTRLA_SPEED_Pos; + hri_sercomi2cm_write_ADDR_HS_bit(hw, service->mode < I2C_HS ? 0 : 1); + + service->trise = _i2cms[i].trise; + + return ERR_NONE; +} + + /* SERCOM I2C slave */ + +#ifndef CONF_SERCOM_0_I2CS_ENABLE +#define CONF_SERCOM_0_I2CS_ENABLE 0 +#endif +#ifndef CONF_SERCOM_1_I2CS_ENABLE +#define CONF_SERCOM_1_I2CS_ENABLE 0 +#endif +#ifndef CONF_SERCOM_2_I2CS_ENABLE +#define CONF_SERCOM_2_I2CS_ENABLE 0 +#endif +#ifndef CONF_SERCOM_3_I2CS_ENABLE +#define CONF_SERCOM_3_I2CS_ENABLE 0 +#endif +#ifndef CONF_SERCOM_4_I2CS_ENABLE +#define CONF_SERCOM_4_I2CS_ENABLE 0 +#endif +#ifndef CONF_SERCOM_5_I2CS_ENABLE +#define CONF_SERCOM_5_I2CS_ENABLE 0 +#endif +#ifndef CONF_SERCOM_6_I2CS_ENABLE +#define CONF_SERCOM_6_I2CS_ENABLE 0 +#endif +#ifndef CONF_SERCOM_7_I2CS_ENABLE +#define CONF_SERCOM_7_I2CS_ENABLE 0 +#endif + +/** Amount of SERCOM that is used as I2C Slave. */ +#define SERCOM_I2CS_AMOUNT \ + (CONF_SERCOM_0_I2CS_ENABLE + CONF_SERCOM_1_I2CS_ENABLE + CONF_SERCOM_2_I2CS_ENABLE + CONF_SERCOM_3_I2CS_ENABLE \ + + CONF_SERCOM_4_I2CS_ENABLE + CONF_SERCOM_5_I2CS_ENABLE + CONF_SERCOM_6_I2CS_ENABLE + CONF_SERCOM_7_I2CS_ENABLE) + +/** + * \brief Macro is used to fill I2C slave configuration structure based on + * its number + * + * \param[in] n The number of structures + */ +#define I2CS_CONFIGURATION(n) \ + { \ + n, \ + SERCOM_I2CM_CTRLA_MODE_I2C_SLAVE | (CONF_SERCOM_##n##_I2CS_RUNSTDBY << SERCOM_I2CS_CTRLA_RUNSTDBY_Pos) \ + | SERCOM_I2CS_CTRLA_SDAHOLD(CONF_SERCOM_##n##_I2CS_SDAHOLD) \ + | (CONF_SERCOM_##n##_I2CS_SEXTTOEN << SERCOM_I2CS_CTRLA_SEXTTOEN_Pos) \ + | (CONF_SERCOM_##n##_I2CS_SPEED << SERCOM_I2CS_CTRLA_SPEED_Pos) \ + | (CONF_SERCOM_##n##_I2CS_SCLSM << SERCOM_I2CS_CTRLA_SCLSM_Pos) \ + | (CONF_SERCOM_##n##_I2CS_LOWTOUT << SERCOM_I2CS_CTRLA_LOWTOUTEN_Pos), \ + SERCOM_I2CS_CTRLB_SMEN | SERCOM_I2CS_CTRLB_AACKEN | SERCOM_I2CS_CTRLB_AMODE(CONF_SERCOM_##n##_I2CS_AMODE), \ + (CONF_SERCOM_##n##_I2CS_GENCEN << SERCOM_I2CS_ADDR_GENCEN_Pos) \ + | SERCOM_I2CS_ADDR_ADDR(CONF_SERCOM_##n##_I2CS_ADDRESS) \ + | (CONF_SERCOM_##n##_I2CS_TENBITEN << SERCOM_I2CS_ADDR_TENBITEN_Pos) \ + | SERCOM_I2CS_ADDR_ADDRMASK(CONF_SERCOM_##n##_I2CS_ADDRESS_MASK) \ + } + +/** + * \brief Macro to check 10-bit addressing + */ +#define I2CS_7BIT_ADDRESSING_MASK 0x7F + +static int32_t _i2c_s_init(void *const hw); +static int8_t _get_i2c_s_index(const void *const hw); +static inline void _i2c_s_deinit(void *const hw); +static int32_t _i2c_s_set_address(void *const hw, const uint16_t address); + +/** + * \brief SERCOM I2C slave configuration type + */ +struct i2cs_configuration { + uint8_t number; + hri_sercomi2cs_ctrla_reg_t ctrl_a; + hri_sercomi2cs_ctrlb_reg_t ctrl_b; + hri_sercomi2cs_addr_reg_t address; +}; + +#if SERCOM_I2CS_AMOUNT < 1 +/** Dummy array for compiling. */ +static struct i2cs_configuration _i2css[1] = {{0}}; +#else +/** + * \brief Array of SERCOM I2C slave configurations + */ +static struct i2cs_configuration _i2css[] = { +#if CONF_SERCOM_0_I2CS_ENABLE == 1 + I2CS_CONFIGURATION(0), +#endif +#if CONF_SERCOM_1_I2CS_ENABLE == 1 + I2CS_CONFIGURATION(1), +#endif +#if CONF_SERCOM_2_I2CS_ENABLE == 1 + I2CS_CONFIGURATION(2), +#endif +#if CONF_SERCOM_3_I2CS_ENABLE == 1 + I2CS_CONFIGURATION(3), +#endif +#if CONF_SERCOM_4_I2CS_ENABLE == 1 + I2CS_CONFIGURATION(4), +#endif +#if CONF_SERCOM_5_I2CS_ENABLE == 1 + I2CS_CONFIGURATION(5), +#endif +#if CONF_SERCOM_6_I2CS_ENABLE == 1 + I2CS_CONFIGURATION(6), +#endif +#if CONF_SERCOM_7_I2CS_ENABLE == 1 + I2CS_CONFIGURATION(7), +#endif +}; +#endif + +/** + * \brief Initialize synchronous I2C slave + */ +int32_t _i2c_s_sync_init(struct _i2c_s_sync_device *const device, void *const hw) +{ + int32_t status; + + ASSERT(device); + + status = _i2c_s_init(hw); + if (status) { + return status; + } + device->hw = hw; + + return ERR_NONE; +} + +/** + * \brief Initialize asynchronous I2C slave + */ +int32_t _i2c_s_async_init(struct _i2c_s_async_device *const device, void *const hw) +{ + int32_t init_status; + + ASSERT(device); + + init_status = _i2c_s_init(hw); + if (init_status) { + return init_status; + } + + device->hw = hw; + _sercom_init_irq_param(hw, (void *)device); + NVIC_DisableIRQ((IRQn_Type)_sercom_get_irq_num(hw)); + NVIC_ClearPendingIRQ((IRQn_Type)_sercom_get_irq_num(hw)); + NVIC_EnableIRQ((IRQn_Type)_sercom_get_irq_num(hw)); + + // Enable Address Match and PREC interrupt by default. + hri_sercomi2cs_set_INTEN_AMATCH_bit(hw); + hri_sercomi2cs_set_INTEN_PREC_bit(hw); + + return ERR_NONE; +} + +/** + * \brief Deinitialize synchronous I2C + */ +int32_t _i2c_s_sync_deinit(struct _i2c_s_sync_device *const device) +{ + _i2c_s_deinit(device->hw); + + return ERR_NONE; +} + +/** + * \brief Deinitialize asynchronous I2C + */ +int32_t _i2c_s_async_deinit(struct _i2c_s_async_device *const device) +{ + NVIC_DisableIRQ((IRQn_Type)_sercom_get_irq_num(device->hw)); + _i2c_s_deinit(device->hw); + + return ERR_NONE; +} + +/** + * \brief Enable I2C module + */ +int32_t _i2c_s_sync_enable(struct _i2c_s_sync_device *const device) +{ + hri_sercomi2cs_set_CTRLA_ENABLE_bit(device->hw); + + return ERR_NONE; +} + +/** + * \brief Enable I2C module + */ +int32_t _i2c_s_async_enable(struct _i2c_s_async_device *const device) +{ + hri_sercomi2cs_set_CTRLA_ENABLE_bit(device->hw); + + return ERR_NONE; +} + +/** + * \brief Disable I2C module + */ +int32_t _i2c_s_sync_disable(struct _i2c_s_sync_device *const device) +{ + hri_sercomi2cs_clear_CTRLA_ENABLE_bit(device->hw); + + return ERR_NONE; +} + +/** + * \brief Disable I2C module + */ +int32_t _i2c_s_async_disable(struct _i2c_s_async_device *const device) +{ + hri_sercomi2cs_clear_CTRLA_ENABLE_bit(device->hw); + + return ERR_NONE; +} + +/** + * \brief Check if 10-bit addressing mode is on + */ +int32_t _i2c_s_sync_is_10bit_addressing_on(const struct _i2c_s_sync_device *const device) +{ + return hri_sercomi2cs_get_ADDR_TENBITEN_bit(device->hw); +} + +/** + * \brief Check if 10-bit addressing mode is on + */ +int32_t _i2c_s_async_is_10bit_addressing_on(const struct _i2c_s_async_device *const device) +{ + return hri_sercomi2cs_get_ADDR_TENBITEN_bit(device->hw); +} + +/** + * \brief Set I2C slave address + */ +int32_t _i2c_s_sync_set_address(struct _i2c_s_sync_device *const device, const uint16_t address) +{ + return _i2c_s_set_address(device->hw, address); +} + +/** + * \brief Set I2C slave address + */ +int32_t _i2c_s_async_set_address(struct _i2c_s_async_device *const device, const uint16_t address) +{ + return _i2c_s_set_address(device->hw, address); +} + +/** + * \brief Write a byte to the given I2C instance + */ +void _i2c_s_sync_write_byte(struct _i2c_s_sync_device *const device, const uint8_t data) +{ + hri_sercomi2cs_write_DATA_reg(device->hw, data); +} + +/** + * \brief Write a byte to the given I2C instance + */ +void _i2c_s_async_write_byte(struct _i2c_s_async_device *const device, const uint8_t data) +{ + hri_sercomi2cs_write_DATA_reg(device->hw, data); +} + +/** + * \brief Read a byte from the given I2C instance + */ +uint8_t _i2c_s_sync_read_byte(const struct _i2c_s_sync_device *const device) +{ + return hri_sercomi2cs_read_DATA_reg(device->hw); +} + +/** + * \brief Check if I2C is ready to send next byt + */ +bool _i2c_s_sync_is_byte_sent(const struct _i2c_s_sync_device *const device) +{ + return hri_sercomi2cs_get_interrupt_DRDY_bit(device->hw); +} + +/** + * \brief Check if there is data received by I2C + */ +bool _i2c_s_sync_is_byte_received(const struct _i2c_s_sync_device *const device) +{ + return hri_sercomi2cs_get_interrupt_DRDY_bit(device->hw); +} + +/** + * \brief Retrieve I2C slave status + */ +i2c_s_status_t _i2c_s_sync_get_status(const struct _i2c_s_sync_device *const device) +{ + return hri_sercomi2cs_read_STATUS_reg(device->hw); +} + +/** + * \brief Clear the Data Ready interrupt flag + */ +int32_t _i2c_s_sync_clear_data_ready_flag(const struct _i2c_s_sync_device *const device) +{ + hri_sercomi2cs_clear_INTFLAG_DRDY_bit(device->hw); + + return ERR_NONE; +} + +/** + * \brief Retrieve I2C slave status + */ +i2c_s_status_t _i2c_s_async_get_status(const struct _i2c_s_async_device *const device) +{ + return hri_sercomi2cs_read_STATUS_reg(device->hw); +} + +/** + * \brief Abort data transmission + */ +int32_t _i2c_s_async_abort_transmission(const struct _i2c_s_async_device *const device) +{ + hri_sercomi2cs_clear_INTEN_DRDY_bit(device->hw); + + return ERR_NONE; +} + +/** + * \brief Enable/disable I2C slave interrupt + */ +int32_t _i2c_s_async_set_irq_state(struct _i2c_s_async_device *const device, const enum _i2c_s_async_callback_type type, + const bool state) +{ + ASSERT(device); + + if (I2C_S_DEVICE_TX == type || I2C_S_DEVICE_RX_COMPLETE == type) { + hri_sercomi2cs_write_INTEN_DRDY_bit(device->hw, state); + } else if (I2C_S_DEVICE_ERROR == type) { + hri_sercomi2cs_write_INTEN_ERROR_bit(device->hw, state); + } + + return ERR_NONE; +} + +/** + * \internal Initalize i2c slave hardware + * + * \param[in] p The pointer to hardware instance + * + *\ return status of initialization + */ +static int32_t _i2c_s_init(void *const hw) +{ + int8_t i = _get_i2c_s_index(hw); + if (i == -1) { + return ERR_INVALID_ARG; + } + + if (!hri_sercomi2cs_is_syncing(hw, SERCOM_I2CS_CTRLA_SWRST)) { + uint32_t mode = _i2css[i].ctrl_a & SERCOM_I2CS_CTRLA_MODE_Msk; + if (hri_sercomi2cs_get_CTRLA_reg(hw, SERCOM_I2CS_CTRLA_ENABLE)) { + hri_sercomi2cs_clear_CTRLA_ENABLE_bit(hw); + hri_sercomi2cs_wait_for_sync(hw, SERCOM_I2CS_SYNCBUSY_ENABLE); + } + hri_sercomi2cs_write_CTRLA_reg(hw, SERCOM_I2CS_CTRLA_SWRST | mode); + } + hri_sercomi2cs_wait_for_sync(hw, SERCOM_I2CS_SYNCBUSY_SWRST); + + hri_sercomi2cs_write_CTRLA_reg(hw, _i2css[i].ctrl_a); + hri_sercomi2cs_write_CTRLB_reg(hw, _i2css[i].ctrl_b); + hri_sercomi2cs_write_ADDR_reg(hw, _i2css[i].address); + + return ERR_NONE; +} + +/** + * \internal Retrieve ordinal number of the given sercom hardware instance + * + * \param[in] hw The pointer to hardware instance + * + * \return The ordinal number of the given sercom hardware instance + */ +static int8_t _get_i2c_s_index(const void *const hw) +{ + uint8_t sercom_offset = _sercom_get_hardware_index(hw); + uint8_t i; + + for (i = 0; i < ARRAY_SIZE(_i2css); i++) { + if (_i2css[i].number == sercom_offset) { + return i; + } + } + + ASSERT(false); + return -1; +} + +/** + * \internal De-initialize i2c slave + * + * \param[in] hw The pointer to hardware instance + */ +static inline void _i2c_s_deinit(void *const hw) +{ + hri_sercomi2cs_clear_CTRLA_ENABLE_bit(hw); + hri_sercomi2cs_set_CTRLA_SWRST_bit(hw); +} + +/** + * \internal De-initialize i2c slave + * + * \param[in] hw The pointer to hardware instance + * \param[in] address Address to set + */ +static int32_t _i2c_s_set_address(void *const hw, const uint16_t address) +{ + bool enabled; + + enabled = hri_sercomi2cs_get_CTRLA_ENABLE_bit(hw); + + CRITICAL_SECTION_ENTER() + hri_sercomi2cs_clear_CTRLA_ENABLE_bit(hw); + hri_sercomi2cs_write_ADDR_ADDR_bf(hw, address); + CRITICAL_SECTION_LEAVE() + + if (enabled) { + hri_sercomi2cs_set_CTRLA_ENABLE_bit(hw); + } + + return ERR_NONE; +} + + /* Sercom SPI implementation */ + +#ifndef SERCOM_USART_CTRLA_MODE_SPI_SLAVE +#define SERCOM_USART_CTRLA_MODE_SPI_SLAVE (2 << 2) +#endif + +#define SPI_DEV_IRQ_MODE 0x8000 + +#define _SPI_CS_PORT_EXTRACT(cs) (((cs) >> 0) & 0xFF) +#define _SPI_CS_PIN_EXTRACT(cs) (((cs) >> 8) & 0xFF) + +COMPILER_PACK_SET(1) +/** Initialization configuration of registers. */ +struct sercomspi_regs_cfg { + uint32_t ctrla; + uint32_t ctrlb; + uint32_t addr; + uint8_t baud; + uint8_t dbgctrl; + uint16_t dummy_byte; + uint8_t n; +}; +COMPILER_PACK_RESET() + +/** Build configuration from header macros. */ +#define SERCOMSPI_REGS(n) \ + { \ + (((CONF_SERCOM_##n##_SPI_DORD) << SERCOM_SPI_CTRLA_DORD_Pos) \ + | (CONF_SERCOM_##n##_SPI_CPOL << SERCOM_SPI_CTRLA_CPOL_Pos) \ + | (CONF_SERCOM_##n##_SPI_CPHA << SERCOM_SPI_CTRLA_CPHA_Pos) \ + | (CONF_SERCOM_##n##_SPI_AMODE_EN ? SERCOM_SPI_CTRLA_FORM(2) : SERCOM_SPI_CTRLA_FORM(0)) \ + | SERCOM_SPI_CTRLA_DOPO(CONF_SERCOM_##n##_SPI_TXPO) | SERCOM_SPI_CTRLA_DIPO(CONF_SERCOM_##n##_SPI_RXPO) \ + | (CONF_SERCOM_##n##_SPI_IBON << SERCOM_SPI_CTRLA_IBON_Pos) \ + | (CONF_SERCOM_##n##_SPI_RUNSTDBY << SERCOM_SPI_CTRLA_RUNSTDBY_Pos) \ + | SERCOM_SPI_CTRLA_MODE(CONF_SERCOM_##n##_SPI_MODE)), /* ctrla */ \ + ((CONF_SERCOM_##n##_SPI_RXEN << SERCOM_SPI_CTRLB_RXEN_Pos) \ + | (CONF_SERCOM_##n##_SPI_MSSEN << SERCOM_SPI_CTRLB_MSSEN_Pos) \ + | (CONF_SERCOM_##n##_SPI_SSDE << SERCOM_SPI_CTRLB_SSDE_Pos) \ + | (CONF_SERCOM_##n##_SPI_PLOADEN << SERCOM_SPI_CTRLB_PLOADEN_Pos) \ + | SERCOM_SPI_CTRLB_AMODE(CONF_SERCOM_##n##_SPI_AMODE) \ + | SERCOM_SPI_CTRLB_CHSIZE(CONF_SERCOM_##n##_SPI_CHSIZE)), /* ctrlb */ \ + (SERCOM_SPI_ADDR_ADDR(CONF_SERCOM_##n##_SPI_ADDR) \ + | SERCOM_SPI_ADDR_ADDRMASK(CONF_SERCOM_##n##_SPI_ADDRMASK)), /* addr */ \ + ((uint8_t)CONF_SERCOM_##n##_SPI_BAUD_RATE), /* baud */ \ + (CONF_SERCOM_##n##_SPI_DBGSTOP << SERCOM_SPI_DBGCTRL_DBGSTOP_Pos), /* dbgctrl */ \ + CONF_SERCOM_##n##_SPI_DUMMYBYTE, /* Dummy byte for SPI master mode */ \ + n /* sercom number */ \ + } + +#ifndef CONF_SERCOM_0_SPI_ENABLE +#define CONF_SERCOM_0_SPI_ENABLE 0 +#endif +#ifndef CONF_SERCOM_1_SPI_ENABLE +#define CONF_SERCOM_1_SPI_ENABLE 0 +#endif +#ifndef CONF_SERCOM_2_SPI_ENABLE +#define CONF_SERCOM_2_SPI_ENABLE 0 +#endif +#ifndef CONF_SERCOM_3_SPI_ENABLE +#define CONF_SERCOM_3_SPI_ENABLE 0 +#endif +#ifndef CONF_SERCOM_4_SPI_ENABLE +#define CONF_SERCOM_4_SPI_ENABLE 0 +#endif +#ifndef CONF_SERCOM_5_SPI_ENABLE +#define CONF_SERCOM_5_SPI_ENABLE 0 +#endif +#ifndef CONF_SERCOM_6_SPI_ENABLE +#define CONF_SERCOM_6_SPI_ENABLE 0 +#endif +#ifndef CONF_SERCOM_7_SPI_ENABLE +#define CONF_SERCOM_7_SPI_ENABLE 0 +#endif + +/** Amount of SERCOM that is used as SPI */ +#define SERCOM_SPI_AMOUNT \ + (CONF_SERCOM_0_SPI_ENABLE + CONF_SERCOM_1_SPI_ENABLE + CONF_SERCOM_2_SPI_ENABLE + CONF_SERCOM_3_SPI_ENABLE \ + + CONF_SERCOM_4_SPI_ENABLE + CONF_SERCOM_5_SPI_ENABLE + CONF_SERCOM_6_SPI_ENABLE + CONF_SERCOM_7_SPI_ENABLE) + +#if SERCOM_SPI_AMOUNT < 1 +/** Dummy array for compiling. */ +static const struct sercomspi_regs_cfg sercomspi_regs[1] = {{0}}; +#else +/** The SERCOM SPI configurations of SERCOM that is used as SPI. */ +static const struct sercomspi_regs_cfg sercomspi_regs[] = { +#if CONF_SERCOM_0_SPI_ENABLE + SERCOMSPI_REGS(0), +#endif +#if CONF_SERCOM_1_SPI_ENABLE + SERCOMSPI_REGS(1), +#endif +#if CONF_SERCOM_2_SPI_ENABLE + SERCOMSPI_REGS(2), +#endif +#if CONF_SERCOM_3_SPI_ENABLE + SERCOMSPI_REGS(3), +#endif +#if CONF_SERCOM_4_SPI_ENABLE + SERCOMSPI_REGS(4), +#endif +#if CONF_SERCOM_5_SPI_ENABLE + SERCOMSPI_REGS(5), +#endif +#if CONF_SERCOM_6_SPI_ENABLE + SERCOMSPI_REGS(6), +#endif +#if CONF_SERCOM_7_SPI_ENABLE + SERCOMSPI_REGS(7), +#endif +}; +#endif + +/** \internal De-initialize SERCOM SPI + * + * \param[in] hw Pointer to the hardware register base. + * + * \return De-initialization status + */ +static int32_t _spi_deinit(void *const hw) +{ + hri_sercomspi_clear_CTRLA_ENABLE_bit(hw); + hri_sercomspi_set_CTRLA_SWRST_bit(hw); + + return ERR_NONE; +} + +/** \internal Enable SERCOM SPI + * + * \param[in] hw Pointer to the hardware register base. + * + * \return Enabling status + */ +static int32_t _spi_sync_enable(void *const hw) +{ + if (hri_sercomspi_is_syncing(hw, SERCOM_SPI_SYNCBUSY_SWRST)) { + return ERR_BUSY; + } + + hri_sercomspi_set_CTRLA_ENABLE_bit(hw); + + return ERR_NONE; +} + +/** \internal Enable SERCOM SPI + * + * \param[in] hw Pointer to the hardware register base. + * + * \return Enabling status + */ +static int32_t _spi_async_enable(void *const hw) +{ + _spi_sync_enable(hw); + NVIC_EnableIRQ((IRQn_Type)_sercom_get_irq_num(hw)); + + return ERR_NONE; +} + +/** \internal Disable SERCOM SPI + * + * \param[in] hw Pointer to the hardware register base. + * + * \return Disabling status + */ +static int32_t _spi_sync_disable(void *const hw) +{ + if (hri_sercomspi_is_syncing(hw, SERCOM_SPI_SYNCBUSY_SWRST)) { + return ERR_BUSY; + } + hri_sercomspi_clear_CTRLA_ENABLE_bit(hw); + + return ERR_NONE; +} + +/** \internal Disable SERCOM SPI + * + * \param[in] hw Pointer to the hardware register base. + * + * \return Disabling status + */ +static int32_t _spi_async_disable(void *const hw) +{ + _spi_sync_disable(hw); + hri_sercomspi_clear_INTEN_reg( + hw, SERCOM_SPI_INTFLAG_ERROR | SERCOM_SPI_INTFLAG_RXC | SERCOM_SPI_INTFLAG_TXC | SERCOM_SPI_INTFLAG_DRE); + NVIC_DisableIRQ((IRQn_Type)_sercom_get_irq_num(hw)); + + return ERR_NONE; +} + +/** \internal Set SERCOM SPI mode + * + * \param[in] hw Pointer to the hardware register base. + * \param[in] mode The mode to set + * + * \return Setting mode status + */ +static int32_t _spi_set_mode(void *const hw, const enum spi_transfer_mode mode) +{ + uint32_t ctrla; + + if (hri_sercomspi_is_syncing(hw, SERCOM_SPI_SYNCBUSY_SWRST | SERCOM_SPI_SYNCBUSY_ENABLE)) { + return ERR_BUSY; + } + + ctrla = hri_sercomspi_read_CTRLA_reg(hw); + ctrla &= ~(SERCOM_SPI_CTRLA_CPOL | SERCOM_SPI_CTRLA_CPHA); + ctrla |= (mode & 0x3u) << SERCOM_SPI_CTRLA_CPHA_Pos; + hri_sercomspi_write_CTRLA_reg(hw, ctrla); + + return ERR_NONE; +} + +/** \internal Set SERCOM SPI baudrate + * + * \param[in] hw Pointer to the hardware register base. + * \param[in] baud_val The baudrate to set + * + * \return Setting baudrate status + */ +static int32_t _spi_set_baudrate(void *const hw, const uint32_t baud_val) +{ + if (hri_sercomspi_is_syncing(hw, SERCOM_SPI_SYNCBUSY_SWRST)) { + return ERR_BUSY; + } + + hri_sercomspi_write_BAUD_reg(hw, baud_val); + + return ERR_NONE; +} + +/** \internal Set SERCOM SPI char size + * + * \param[in] hw Pointer to the hardware register base. + * \param[in] baud_val The baudrate to set + * \param[out] size Stored char size + * + * \return Setting char size status + */ +static int32_t _spi_set_char_size(void *const hw, const enum spi_char_size char_size, uint8_t *const size) +{ + /* Only 8-bit or 9-bit accepted */ + if (!(char_size == SPI_CHAR_SIZE_8 || char_size == SPI_CHAR_SIZE_9)) { + return ERR_INVALID_ARG; + } + + if (hri_sercomspi_is_syncing(hw, SERCOM_SPI_SYNCBUSY_SWRST | SERCOM_SPI_SYNCBUSY_CTRLB)) { + return ERR_BUSY; + } + + hri_sercomspi_write_CTRLB_CHSIZE_bf(hw, char_size); + *size = (char_size == SPI_CHAR_SIZE_8) ? 1 : 2; + + return ERR_NONE; +} + +/** \internal Set SERCOM SPI data order + * + * \param[in] hw Pointer to the hardware register base. + * \param[in] baud_val The baudrate to set + * + * \return Setting data order status + */ +static int32_t _spi_set_data_order(void *const hw, const enum spi_data_order dord) +{ + uint32_t ctrla; + + if (hri_sercomspi_is_syncing(hw, SERCOM_SPI_SYNCBUSY_SWRST)) { + return ERR_BUSY; + } + + ctrla = hri_sercomspi_read_CTRLA_reg(hw); + + if (dord == SPI_DATA_ORDER_LSB_1ST) { + ctrla |= SERCOM_SPI_CTRLA_DORD; + } else { + ctrla &= ~SERCOM_SPI_CTRLA_DORD; + } + hri_sercomspi_write_CTRLA_reg(hw, ctrla); + + return ERR_NONE; +} + +/** \brief Load SERCOM registers to init for SPI master mode + * The settings will be applied with default master mode, unsupported things + * are ignored. + * \param[in, out] hw Pointer to the hardware register base. + * \param[in] regs Pointer to register configuration values. + */ +static inline void _spi_load_regs_master(void *const hw, const struct sercomspi_regs_cfg *regs) +{ + ASSERT(hw && regs); + hri_sercomspi_write_CTRLA_reg( + hw, regs->ctrla & ~(SERCOM_SPI_CTRLA_IBON | SERCOM_SPI_CTRLA_ENABLE | SERCOM_SPI_CTRLA_SWRST)); + hri_sercomspi_write_CTRLB_reg( + hw, + (regs->ctrlb + & ~(SERCOM_SPI_CTRLB_MSSEN | SERCOM_SPI_CTRLB_AMODE_Msk | SERCOM_SPI_CTRLB_SSDE | SERCOM_SPI_CTRLB_PLOADEN)) + | (SERCOM_SPI_CTRLB_RXEN)); + hri_sercomspi_write_BAUD_reg(hw, regs->baud); + hri_sercomspi_write_DBGCTRL_reg(hw, regs->dbgctrl); +} + +/** \brief Load SERCOM registers to init for SPI slave mode + * The settings will be applied with default slave mode, unsupported things + * are ignored. + * \param[in, out] hw Pointer to the hardware register base. + * \param[in] regs Pointer to register configuration values. + */ +static inline void _spi_load_regs_slave(void *const hw, const struct sercomspi_regs_cfg *regs) +{ + ASSERT(hw && regs); + hri_sercomspi_write_CTRLA_reg( + hw, regs->ctrla & ~(SERCOM_SPI_CTRLA_IBON | SERCOM_SPI_CTRLA_ENABLE | SERCOM_SPI_CTRLA_SWRST)); + hri_sercomspi_write_CTRLB_reg(hw, + (regs->ctrlb & ~(SERCOM_SPI_CTRLB_MSSEN)) + | (SERCOM_SPI_CTRLB_RXEN | SERCOM_SPI_CTRLB_SSDE | SERCOM_SPI_CTRLB_PLOADEN)); + hri_sercomspi_write_ADDR_reg(hw, regs->addr); + hri_sercomspi_write_DBGCTRL_reg(hw, regs->dbgctrl); + while (hri_sercomspi_is_syncing(hw, 0xFFFFFFFF)) + ; +} + +/** \brief Return the pointer to register settings of specific SERCOM + * \param[in] hw_addr The hardware register base address. + * \return Pointer to register settings of specific SERCOM. + */ +static inline const struct sercomspi_regs_cfg *_spi_get_regs(const uint32_t hw_addr) +{ + uint8_t n = _sercom_get_hardware_index((const void *)hw_addr); + uint8_t i; + + for (i = 0; i < sizeof(sercomspi_regs) / sizeof(struct sercomspi_regs_cfg); i++) { + if (sercomspi_regs[i].n == n) { + return &sercomspi_regs[i]; + } + } + + return NULL; +} + +int32_t _spi_m_sync_init(struct _spi_m_sync_dev *dev, void *const hw) +{ + const struct sercomspi_regs_cfg *regs = _spi_get_regs((uint32_t)hw); + + ASSERT(dev && hw); + + if (regs == NULL) { + return ERR_INVALID_ARG; + } + + if (!hri_sercomspi_is_syncing(hw, SERCOM_SPI_SYNCBUSY_SWRST)) { + uint32_t mode = regs->ctrla & SERCOM_SPI_CTRLA_MODE_Msk; + if (hri_sercomspi_get_CTRLA_reg(hw, SERCOM_SPI_CTRLA_ENABLE)) { + hri_sercomspi_clear_CTRLA_ENABLE_bit(hw); + hri_sercomspi_wait_for_sync(hw, SERCOM_SPI_SYNCBUSY_ENABLE); + } + hri_sercomspi_write_CTRLA_reg(hw, SERCOM_SPI_CTRLA_SWRST | mode); + } + hri_sercomspi_wait_for_sync(hw, SERCOM_SPI_SYNCBUSY_SWRST); + + dev->prvt = hw; + + if ((regs->ctrla & SERCOM_SPI_CTRLA_MODE_Msk) == SERCOM_USART_CTRLA_MODE_SPI_SLAVE) { + _spi_load_regs_slave(hw, regs); + } else { + _spi_load_regs_master(hw, regs); + } + + /* Load character size from default hardware configuration */ + dev->char_size = ((regs->ctrlb & SERCOM_SPI_CTRLB_CHSIZE_Msk) == 0) ? 1 : 2; + + dev->dummy_byte = regs->dummy_byte; + + return ERR_NONE; +} + +int32_t _spi_s_sync_init(struct _spi_s_sync_dev *dev, void *const hw) +{ + return _spi_m_sync_init(dev, hw); +} + +int32_t _spi_m_async_init(struct _spi_async_dev *dev, void *const hw) +{ + struct _spi_async_dev *spid = dev; + /* Do hardware initialize. */ + int32_t rc = _spi_m_sync_init((struct _spi_m_sync_dev *)dev, hw); + + if (rc < 0) { + return rc; + } + + _sercom_init_irq_param(hw, (void *)dev); + /* Initialize callbacks: must use them */ + spid->callbacks.complete = NULL; + spid->callbacks.rx = NULL; + spid->callbacks.tx = NULL; + NVIC_DisableIRQ((IRQn_Type)_sercom_get_irq_num(hw)); + NVIC_ClearPendingIRQ((IRQn_Type)_sercom_get_irq_num(hw)); + + return ERR_NONE; +} + +int32_t _spi_s_async_init(struct _spi_s_async_dev *dev, void *const hw) +{ + return _spi_m_async_init(dev, hw); +} + +int32_t _spi_m_async_deinit(struct _spi_async_dev *dev) +{ + NVIC_DisableIRQ((IRQn_Type)_sercom_get_irq_num(dev->prvt)); + NVIC_ClearPendingIRQ((IRQn_Type)_sercom_get_irq_num(dev->prvt)); + + return _spi_deinit(dev->prvt); +} + +int32_t _spi_s_async_deinit(struct _spi_s_async_dev *dev) +{ + NVIC_DisableIRQ((IRQn_Type)_sercom_get_irq_num(dev->prvt)); + NVIC_ClearPendingIRQ((IRQn_Type)_sercom_get_irq_num(dev->prvt)); + + return _spi_deinit(dev->prvt); +} + +int32_t _spi_m_sync_deinit(struct _spi_m_sync_dev *dev) +{ + return _spi_deinit(dev->prvt); +} + +int32_t _spi_s_sync_deinit(struct _spi_s_sync_dev *dev) +{ + return _spi_deinit(dev->prvt); +} + +int32_t _spi_m_sync_enable(struct _spi_m_sync_dev *dev) +{ + ASSERT(dev && dev->prvt); + + return _spi_sync_enable(dev->prvt); +} + +int32_t _spi_s_sync_enable(struct _spi_s_sync_dev *dev) +{ + ASSERT(dev && dev->prvt); + + return _spi_sync_enable(dev->prvt); +} + +int32_t _spi_m_async_enable(struct _spi_async_dev *dev) +{ + ASSERT(dev && dev->prvt); + + return _spi_async_enable(dev->prvt); +} + +int32_t _spi_s_async_enable(struct _spi_s_async_dev *dev) +{ + ASSERT(dev && dev->prvt); + + return _spi_async_enable(dev->prvt); +} + +int32_t _spi_m_sync_disable(struct _spi_m_sync_dev *dev) +{ + ASSERT(dev && dev->prvt); + + return _spi_sync_disable(dev->prvt); +} + +int32_t _spi_s_sync_disable(struct _spi_s_sync_dev *dev) +{ + ASSERT(dev && dev->prvt); + + return _spi_sync_disable(dev->prvt); +} + +int32_t _spi_m_async_disable(struct _spi_async_dev *dev) +{ + ASSERT(dev && dev->prvt); + + return _spi_async_disable(dev->prvt); +} + +int32_t _spi_s_async_disable(struct _spi_s_async_dev *dev) +{ + ASSERT(dev && dev->prvt); + + return _spi_async_disable(dev->prvt); +} + +int32_t _spi_m_sync_set_mode(struct _spi_m_sync_dev *dev, const enum spi_transfer_mode mode) +{ + ASSERT(dev && dev->prvt); + + return _spi_set_mode(dev->prvt, mode); +} + +int32_t _spi_m_async_set_mode(struct _spi_async_dev *dev, const enum spi_transfer_mode mode) +{ + ASSERT(dev && dev->prvt); + + return _spi_set_mode(dev->prvt, mode); +} + +int32_t _spi_s_async_set_mode(struct _spi_s_async_dev *dev, const enum spi_transfer_mode mode) +{ + ASSERT(dev && dev->prvt); + + return _spi_set_mode(dev->prvt, mode); +} + +int32_t _spi_s_sync_set_mode(struct _spi_s_sync_dev *dev, const enum spi_transfer_mode mode) +{ + ASSERT(dev && dev->prvt); + + return _spi_set_mode(dev->prvt, mode); +} + +int32_t _spi_calc_baud_val(struct spi_dev *dev, const uint32_t clk, const uint32_t baud) +{ + int32_t rc; + ASSERT(dev); + + /* Not accept 0es */ + if (clk == 0 || baud == 0) { + return ERR_INVALID_ARG; + } + + /* Check baudrate range of current assigned clock */ + if (!(baud <= (clk >> 1) && baud >= (clk >> 8))) { + return ERR_INVALID_ARG; + } + + rc = ((clk >> 1) / baud) - 1; + return rc; +} + +int32_t _spi_m_sync_set_baudrate(struct _spi_m_sync_dev *dev, const uint32_t baud_val) +{ + ASSERT(dev && dev->prvt); + + return _spi_set_baudrate(dev->prvt, baud_val); +} + +int32_t _spi_m_async_set_baudrate(struct _spi_async_dev *dev, const uint32_t baud_val) +{ + ASSERT(dev && dev->prvt); + + return _spi_set_baudrate(dev->prvt, baud_val); +} + +int32_t _spi_m_sync_set_char_size(struct _spi_m_sync_dev *dev, const enum spi_char_size char_size) +{ + ASSERT(dev && dev->prvt); + + return _spi_set_char_size(dev->prvt, char_size, &dev->char_size); +} + +int32_t _spi_m_async_set_char_size(struct _spi_async_dev *dev, const enum spi_char_size char_size) +{ + ASSERT(dev && dev->prvt); + + return _spi_set_char_size(dev->prvt, char_size, &dev->char_size); +} + +int32_t _spi_s_async_set_char_size(struct _spi_s_async_dev *dev, const enum spi_char_size char_size) +{ + ASSERT(dev && dev->prvt); + + return _spi_set_char_size(dev->prvt, char_size, &dev->char_size); +} + +int32_t _spi_s_sync_set_char_size(struct _spi_s_sync_dev *dev, const enum spi_char_size char_size) +{ + ASSERT(dev && dev->prvt); + + return _spi_set_char_size(dev->prvt, char_size, &dev->char_size); +} + +int32_t _spi_m_sync_set_data_order(struct _spi_m_sync_dev *dev, const enum spi_data_order dord) +{ + ASSERT(dev && dev->prvt); + + return _spi_set_data_order(dev->prvt, dord); +} + +int32_t _spi_m_async_set_data_order(struct _spi_async_dev *dev, const enum spi_data_order dord) +{ + ASSERT(dev && dev->prvt); + + return _spi_set_data_order(dev->prvt, dord); +} + +int32_t _spi_s_async_set_data_order(struct _spi_s_async_dev *dev, const enum spi_data_order dord) +{ + ASSERT(dev && dev->prvt); + + return _spi_set_data_order(dev->prvt, dord); +} + +int32_t _spi_s_sync_set_data_order(struct _spi_s_sync_dev *dev, const enum spi_data_order dord) +{ + ASSERT(dev && dev->prvt); + + return _spi_set_data_order(dev->prvt, dord); +} + +/** Wait until SPI bus idle. */ +static inline void _spi_wait_bus_idle(void *const hw) +{ + while (!(hri_sercomspi_get_INTFLAG_reg(hw, SERCOM_SPI_INTFLAG_TXC | SERCOM_SPI_INTFLAG_DRE))) { + ; + } + hri_sercomspi_clear_INTFLAG_reg(hw, SERCOM_SPI_INTFLAG_TXC | SERCOM_SPI_INTFLAG_DRE); +} + +/** Holds run time information for message sync transaction. */ +struct _spi_trans_ctrl { + /** Pointer to transmitting data buffer. */ + uint8_t *txbuf; + /** Pointer to receiving data buffer. */ + uint8_t *rxbuf; + /** Count number of data transmitted. */ + uint32_t txcnt; + /** Count number of data received. */ + uint32_t rxcnt; + /** Data character size. */ + uint8_t char_size; +}; + +/** Check interrupt flag of RXC and update transaction runtime information. */ +static inline bool _spi_rx_check_and_receive(void *const hw, const uint32_t iflag, struct _spi_trans_ctrl *ctrl) +{ + uint32_t data; + + if (!(iflag & SERCOM_SPI_INTFLAG_RXC)) { + return false; + } + + data = hri_sercomspi_read_DATA_reg(hw); + + if (ctrl->rxbuf) { + *ctrl->rxbuf++ = (uint8_t)data; + + if (ctrl->char_size > 1) { + *ctrl->rxbuf++ = (uint8_t)(data >> 8); + } + } + + ctrl->rxcnt++; + + return true; +} + +/** Check interrupt flag of DRE and update transaction runtime information. */ +static inline void _spi_tx_check_and_send(void *const hw, const uint32_t iflag, struct _spi_trans_ctrl *ctrl, + uint16_t dummy) +{ + uint32_t data; + + if (!(SERCOM_SPI_INTFLAG_DRE & iflag)) { + return; + } + + if (ctrl->txbuf) { + data = *ctrl->txbuf++; + + if (ctrl->char_size > 1) { + data |= (*ctrl->txbuf) << 8; + ctrl->txbuf++; + } + } else { + data = dummy; + } + + ctrl->txcnt++; + hri_sercomspi_write_DATA_reg(hw, data); +} + +/** Check interrupt flag of ERROR and update transaction runtime information. */ +static inline int32_t _spi_err_check(const uint32_t iflag, void *const hw) +{ + if (SERCOM_SPI_INTFLAG_ERROR & iflag) { + hri_sercomspi_clear_STATUS_reg(hw, ~0); + hri_sercomspi_clear_INTFLAG_reg(hw, SERCOM_SPI_INTFLAG_ERROR); + return ERR_OVERFLOW; + } + + return ERR_NONE; +} + +int32_t _spi_m_sync_trans(struct _spi_m_sync_dev *dev, const struct spi_msg *msg) +{ + void * hw = dev->prvt; + int32_t rc = 0; + struct _spi_trans_ctrl ctrl = {msg->txbuf, msg->rxbuf, 0, 0, dev->char_size}; + + ASSERT(dev && hw); + + /* If settings are not applied (pending), we can not go on */ + if (hri_sercomspi_is_syncing( + hw, (SERCOM_SPI_SYNCBUSY_SWRST | SERCOM_SPI_SYNCBUSY_ENABLE | SERCOM_SPI_SYNCBUSY_CTRLB))) { + return ERR_BUSY; + } + + /* SPI must be enabled to start synchronous transfer */ + if (!hri_sercomspi_get_CTRLA_ENABLE_bit(hw)) { + return ERR_NOT_INITIALIZED; + } + + for (;;) { + uint32_t iflag = hri_sercomspi_read_INTFLAG_reg(hw); + + if (!_spi_rx_check_and_receive(hw, iflag, &ctrl)) { + /* In master mode, do not start next byte before previous byte received + * to make better output waveform */ + if (ctrl.rxcnt >= ctrl.txcnt) { + _spi_tx_check_and_send(hw, iflag, &ctrl, dev->dummy_byte); + } + } + + rc = _spi_err_check(iflag, hw); + + if (rc < 0) { + break; + } + if (ctrl.txcnt >= msg->size && ctrl.rxcnt >= msg->size) { + rc = ctrl.txcnt; + break; + } + } + /* Wait until SPI bus idle */ + _spi_wait_bus_idle(hw); + + return rc; +} + +int32_t _spi_m_async_enable_tx(struct _spi_async_dev *dev, bool state) +{ + void *hw = dev->prvt; + + ASSERT(dev && hw); + + if (state) { + hri_sercomspi_set_INTEN_DRE_bit(hw); + } else { + hri_sercomspi_clear_INTEN_DRE_bit(hw); + } + + return ERR_NONE; +} + +int32_t _spi_s_async_enable_tx(struct _spi_s_async_dev *dev, bool state) +{ + return _spi_m_async_enable_tx(dev, state); +} + +int32_t _spi_m_async_enable_rx(struct _spi_async_dev *dev, bool state) +{ + void *hw = dev->prvt; + + ASSERT(dev); + ASSERT(hw); + + if (state) { + hri_sercomspi_set_INTEN_RXC_bit(hw); + } else { + hri_sercomspi_clear_INTEN_RXC_bit(hw); + } + + return ERR_NONE; +} + +int32_t _spi_s_async_enable_rx(struct _spi_s_async_dev *dev, bool state) +{ + return _spi_m_async_enable_rx(dev, state); +} + +int32_t _spi_m_async_enable_tx_complete(struct _spi_async_dev *dev, bool state) +{ + ASSERT(dev && dev->prvt); + + if (state) { + hri_sercomspi_set_INTEN_TXC_bit(dev->prvt); + } else { + hri_sercomspi_clear_INTEN_TXC_bit(dev->prvt); + } + + return ERR_NONE; +} + +int32_t _spi_s_async_enable_ss_detect(struct _spi_s_async_dev *dev, bool state) +{ + return _spi_m_async_enable_tx_complete(dev, state); +} + +int32_t _spi_m_async_write_one(struct _spi_async_dev *dev, uint16_t data) +{ + ASSERT(dev && dev->prvt); + + hri_sercomspi_write_DATA_reg(dev->prvt, data); + + return ERR_NONE; +} + +int32_t _spi_s_async_write_one(struct _spi_s_async_dev *dev, uint16_t data) +{ + ASSERT(dev && dev->prvt); + + hri_sercomspi_write_DATA_reg(dev->prvt, data); + + return ERR_NONE; +} + +int32_t _spi_s_sync_write_one(struct _spi_s_sync_dev *dev, uint16_t data) +{ + ASSERT(dev && dev->prvt); + + hri_sercomspi_write_DATA_reg(dev->prvt, data); + + return ERR_NONE; +} + +uint16_t _spi_m_async_read_one(struct _spi_async_dev *dev) +{ + ASSERT(dev && dev->prvt); + + return hri_sercomspi_read_DATA_reg(dev->prvt); +} + +uint16_t _spi_s_async_read_one(struct _spi_s_async_dev *dev) +{ + ASSERT(dev && dev->prvt); + + return hri_sercomspi_read_DATA_reg(dev->prvt); +} + +uint16_t _spi_s_sync_read_one(struct _spi_s_sync_dev *dev) +{ + ASSERT(dev && dev->prvt); + + return hri_sercomspi_read_DATA_reg(dev->prvt); +} + +int32_t _spi_m_async_register_callback(struct _spi_async_dev *dev, const enum _spi_async_dev_cb_type cb_type, + const FUNC_PTR func) +{ + typedef void (*func_t)(void); + struct _spi_async_dev *spid = dev; + + ASSERT(dev && (cb_type < SPI_DEV_CB_N)); + + func_t *p_ls = (func_t *)&spid->callbacks; + p_ls[cb_type] = (func_t)func; + + return ERR_NONE; +} + +int32_t _spi_s_async_register_callback(struct _spi_s_async_dev *dev, const enum _spi_s_async_dev_cb_type cb_type, + const FUNC_PTR func) +{ + return _spi_m_async_register_callback(dev, cb_type, func); +} + +bool _spi_s_sync_is_tx_ready(struct _spi_s_sync_dev *dev) +{ + ASSERT(dev && dev->prvt); + + return hri_sercomi2cm_get_INTFLAG_reg(dev->prvt, SERCOM_SPI_INTFLAG_DRE); +} + +bool _spi_s_sync_is_rx_ready(struct _spi_s_sync_dev *dev) +{ + ASSERT(dev && dev->prvt); + + return hri_sercomi2cm_get_INTFLAG_reg(dev->prvt, SERCOM_SPI_INTFLAG_RXC); +} + +bool _spi_s_sync_is_ss_deactivated(struct _spi_s_sync_dev *dev) +{ + void *hw = dev->prvt; + + ASSERT(dev && hw); + + if (hri_sercomi2cm_get_INTFLAG_reg(hw, SERCOM_SPI_INTFLAG_TXC)) { + hri_sercomspi_clear_INTFLAG_reg(hw, SERCOM_SPI_INTFLAG_TXC); + return true; + } + return false; +} + +bool _spi_s_sync_is_error(struct _spi_s_sync_dev *dev) +{ + void *hw = dev->prvt; + + ASSERT(dev && hw); + + if (hri_sercomi2cm_get_INTFLAG_reg(hw, SERCOM_SPI_INTFLAG_ERROR)) { + hri_sercomspi_clear_STATUS_reg(hw, SERCOM_SPI_STATUS_BUFOVF); + hri_sercomspi_clear_INTFLAG_reg(hw, SERCOM_SPI_INTFLAG_ERROR); + return true; + } + return false; +} + +/** + * \brief Enable/disable SPI master interrupt + * + * param[in] device The pointer to SPI master device instance + * param[in] type The type of interrupt to disable/enable if applicable + * param[in] state Enable or disable + */ +void _spi_m_async_set_irq_state(struct _spi_async_dev *const device, const enum _spi_async_dev_cb_type type, + const bool state) +{ + ASSERT(device); + + if (SPI_DEV_CB_ERROR == type) { + hri_sercomspi_write_INTEN_ERROR_bit(device->prvt, state); + } +} + +/** + * \brief Enable/disable SPI slave interrupt + * + * param[in] device The pointer to SPI slave device instance + * param[in] type The type of interrupt to disable/enable if applicable + * param[in] state Enable or disable + */ +void _spi_s_async_set_irq_state(struct _spi_async_dev *const device, const enum _spi_async_dev_cb_type type, + const bool state) +{ + _spi_m_async_set_irq_state(device, type, state); +} diff --git a/watch-library/hpl/slcd/hpl_slcd.c b/watch-library/hpl/slcd/hpl_slcd.c new file mode 100644 index 00000000..f8f42bdd --- /dev/null +++ b/watch-library/hpl/slcd/hpl_slcd.c @@ -0,0 +1,336 @@ +/** + * \file + * + * \brief SLCD Segment Liquid Crystal Display Controller(Sync) functionality + * Implementation. + * + * Copyright (c) 2015-2018 Microchip Technology Inc. and its subsidiaries. + * + * \asf_license_start + * + * \page License + * + * Subject to your compliance with these terms, you may use Microchip + * software and any derivatives exclusively with Microchip products. + * It is your responsibility to comply with third party license terms applicable + * to your use of third party software (including open source software) that + * may accompany Microchip software. + * + * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, + * WHETHER EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, + * INCLUDING ANY IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, + * AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE + * LIABLE FOR ANY INDIRECT, SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL + * LOSS, DAMAGE, COST OR EXPENSE OF ANY KIND WHATSOEVER RELATED TO THE + * SOFTWARE, HOWEVER CAUSED, EVEN IF MICROCHIP HAS BEEN ADVISED OF THE + * POSSIBILITY OR THE DAMAGES ARE FORESEEABLE. TO THE FULLEST EXTENT + * ALLOWED BY LAW, MICROCHIP'S TOTAL LIABILITY ON ALL CLAIMS IN ANY WAY + * RELATED TO THIS SOFTWARE WILL NOT EXCEED THE AMOUNT OF FEES, IF ANY, + * THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR THIS SOFTWARE. + * + * \asf_license_stop + * + */ + +#include +#include +#include +#include +#include + +static int32_t _slcd_sync_set_segment(struct _slcd_sync_device *dev, const uint32_t com, const uint32_t seg, + const bool on); + +/** + * \brief SLCD configuration type + */ +struct slcd_configuration { + hri_slcd_ctrla_reg_t ctrla; /*!< Control A Register */ + hri_slcd_ctrlb_reg_t ctrlb; /*!< Control B Register */ + hri_slcd_ctrlc_reg_t ctrlc; /*!< Control C Register */ + hri_slcd_ctrld_reg_t ctrld; /*!< Control D Register */ +}; + +/** + * \brief Array of AC configurations + */ +static struct slcd_configuration _slcd + = {SLCD_CTRLA_DUTY(CONF_SLCD_COM_NUM) | CONF_SLCD_WMOD << SLCD_CTRLA_WMOD_Pos + | CONF_SLCD_RUNSTDBY << SLCD_CTRLA_RUNSTDBY_Pos | SLCD_CTRLA_PRESC(CONF_SLCD_PRESC) + | SLCD_CTRLA_CKDIV(CONF_SLCD_CKDIV) | SLCD_CTRLA_BIAS(CONF_SLCD_BIAS) + | CONF_SLCD_XVLCD << SLCD_CTRLA_XVLCD_Pos | SLCD_CTRLA_PRF(CONF_SLCD_PRF) | SLCD_CTRLA_RRF(CONF_SLCD_RRF), + CONF_SLCD_BBEN << SLCD_CTRLB_BBEN_Pos | SLCD_CTRLB_BBD(CONF_SLCD_BBD - 1), + SLCD_CTRLC_CTST(CONF_SLCD_CONTRAST_ADJUST), + SLCD_CTRLD_DISPEN}; +static const struct slcd_char_setting cm_setting[] = SLCD_CHAR_SETTING_TABLE; +static const struct slcd_char_mapping cm7_lut[] = SLCD_SEG7_LUT; +static const struct slcd_char_mapping cm14_lut[] = SLCD_SEG14_LUT; +/** + * \brief Initialize SLCD Device Descriptor + */ +int32_t _slcd_sync_init(struct _slcd_sync_device *dev, void *const hw) +{ + if (!hri_slcd_is_syncing(hw, SLCD_SYNCBUSY_SWRST)) { + if (hri_slcd_get_CTRLA_ENABLE_bit(hw)) { + hri_slcd_clear_CTRLA_ENABLE_bit(hw); + hri_slcd_wait_for_sync(hw, SLCD_SYNCBUSY_ENABLE); + } + hri_slcd_write_CTRLA_reg(hw, SLCD_CTRLA_SWRST); + } + hri_slcd_wait_for_sync(hw, SLCD_SYNCBUSY_SWRST); + + dev->hw = hw; + hri_slcd_write_CTRLA_reg(hw, _slcd.ctrla); + hri_slcd_write_CTRLB_reg(hw, _slcd.ctrlb); + hri_slcd_write_CTRLC_reg(hw, _slcd.ctrlc); + hri_slcd_write_CTRLD_reg(hw, _slcd.ctrld); + hri_slcd_write_LPENL_reg(hw, CONF_SLCD_LPENL); + hri_slcd_write_LPENH_reg(hw, CONF_SLCD_LPENH); + hri_slcd_write_SDATAL0_reg(hw, 0); + hri_slcd_write_SDATAH0_reg(hw, 0); + hri_slcd_write_SDATAL1_reg(hw, 0); + hri_slcd_write_SDATAH1_reg(hw, 0); + hri_slcd_write_SDATAL2_reg(hw, 0); + hri_slcd_write_SDATAH2_reg(hw, 0); + hri_slcd_write_SDATAL3_reg(hw, 0); + hri_slcd_write_SDATAH3_reg(hw, 0); + hri_slcd_write_SDATAL4_reg(hw, 0); + hri_slcd_write_SDATAH4_reg(hw, 0); + hri_slcd_write_SDATAL5_reg(hw, 0); + hri_slcd_write_SDATAH5_reg(hw, 0); + hri_slcd_write_SDATAL6_reg(hw, 0); + hri_slcd_write_SDATAH6_reg(hw, 0); + hri_slcd_write_SDATAL7_reg(hw, 0); + hri_slcd_write_SDATAH7_reg(hw, 0); + hri_slcd_set_BCFG_MODE_bit(dev->hw); + return ERR_NONE; +} + +/** + * \brief DeInitialize SLCD Device Descriptor + */ +int32_t _slcd_sync_deinit(struct _slcd_sync_device *dev) +{ + hri_slcd_clear_CTRLA_ENABLE_bit(dev->hw); + hri_slcd_wait_for_sync(dev->hw, SLCD_SYNCBUSY_ENABLE); + hri_slcd_set_CTRLA_SWRST_bit(dev->hw); + dev->hw = NULL; + + return ERR_NONE; +} + +/** + * \brief Enable SLCD driver + * + * \param[in] dev SLCD device descriptor to be enabled + */ +int32_t _slcd_sync_enable(struct _slcd_sync_device *dev) +{ + hri_slcd_set_CTRLA_ENABLE_bit(dev->hw); + return ERR_NONE; +} + +/** + * \brief Disable SLCD driver + */ +int32_t _slcd_sync_disable(struct _slcd_sync_device *dev) +{ + hri_slcd_clear_CTRLA_ENABLE_bit(dev->hw); + return ERR_NONE; +} + +/** + * \brief Turn on a Segment + */ +int32_t _slcd_sync_seg_on(struct _slcd_sync_device *dev, uint32_t seg) +{ + return _slcd_sync_set_segment(dev, SLCD_COMNUM(seg), SLCD_SEGNUM(seg), true); +} + +/** + * \brief Turn off a Segment + */ +int32_t _slcd_sync_seg_off(struct _slcd_sync_device *dev, uint32_t seg) +{ + return _slcd_sync_set_segment(dev, SLCD_COMNUM(seg), SLCD_SEGNUM(seg), false); +} + +/** + * \brief Blink a Segment + */ +int32_t _slcd_sync_seg_blink(struct _slcd_sync_device *dev, uint32_t seg, const uint32_t period) +{ + if ((SLCD_COMNUM(seg) >= CONF_SLCD_COM_NUM) || (SLCD_SEGNUM(seg) >= CONF_SLCD_SEG_NUM)) { + return ERR_INVALID_ARG; + } + /* COM[0..7], Seg[0,1] support blink */ + if (SLCD_SEGNUM(seg) >= 2) { + return ERR_INVALID_ARG; + } + /* Verify period */ + if (period > SLCD_FC_MAX_MS || period < SLCD_FC_MIN_MS) { + return ERR_INVALID_ARG; + } + /* Set Period, use Frame Counter 0 for blink */ + hri_slcd_clear_CTRLD_FC0EN_bit(dev->hw); + hri_slcd_wait_for_sync(dev->hw, SLCD_SYNCBUSY_CTRLD); + if (period <= SLCD_FC_BYPASS_MAX_MS) { + hri_slcd_set_FC0_reg(dev->hw, SLCD_FC0_PB | ((period / (1000 / SLCD_FRAME_FREQUENCY)) - 1)); + } else { + hri_slcd_set_FC0_reg(dev->hw, (((period / (1000 / SLCD_FRAME_FREQUENCY)) / 8 - 1))); + } + hri_slcd_set_CTRLD_FC0EN_bit(dev->hw); + + /* Set Blink Segments */ + _slcd_sync_set_segment(dev, SLCD_COMNUM(seg), SLCD_SEGNUM(seg), true); + hri_slcd_clear_CTRLD_BLINK_bit(dev->hw); + + hri_slcd_clear_CTRLA_ENABLE_bit(dev->hw); + hri_slcd_wait_for_sync(dev->hw, SLCD_SYNCBUSY_ENABLE); + + /* Update BCFG */ + if (SLCD_SEGNUM(seg) == 0) { + hri_slcd_set_BCFG_BSS0_bf(dev->hw, 1 << SLCD_COMNUM(seg)); + } else { + hri_slcd_set_BCFG_BSS1_bf(dev->hw, 1 << SLCD_COMNUM(seg)); + } + hri_slcd_set_CTRLA_ENABLE_bit(dev->hw); + hri_slcd_set_CTRLD_BLINK_bit(dev->hw); + + return ERR_NONE; +} + +/** + * \brief Displays a character + */ +int32_t _slcd_sync_write_char(struct _slcd_sync_device *dev, const uint8_t character, uint32_t index) +{ + uint32_t i; + uint32_t data = ~0; + if (cm_setting[index].size == 7) { + for (i = 0; i> 2; i++) { + if (cm7_lut[i].character == character) { + data = cm7_lut[i].mapping; + break; + } + } + } else if (cm_setting[index].size == 14) { + for (i = 0; i> 2; i++) { + if (cm14_lut[i].character == character) { + data = cm14_lut[i].mapping; + break; + } + } + } + if (data == 0xFFFFFFFF) { + return ERR_INVALID_ARG; + } + + hri_slcd_write_CMCFG_NSEG_bf(dev->hw, cm_setting[index].nseg); + hri_slcd_write_CMINDEX_CINDEX_bf(dev->hw, cm_setting[index].com_index); + hri_slcd_write_CMINDEX_SINDEX_bf(dev->hw, cm_setting[index].seg_index); + + if (cm_setting[index].size == 7) { + hri_slcd_write_CMDMASK_reg(dev->hw, SEG7_MASK); + } else if (cm_setting[index].size == 14) { + hri_slcd_write_CMDMASK_reg(dev->hw, SEG14_MASK); + } + while (hri_slcd_get_STATUS_CMWRBUSY_bit(dev->hw)) + ; + hri_slcd_write_CMDATA_reg(dev->hw, data); + + return ERR_NONE; +} + +/** + * \brief Start animation play by a segment array + */ +int32_t _slcd_sync_start_animation(struct _slcd_sync_device *dev, const uint32_t segs[], uint32_t len, + const uint32_t period) +{ + uint32_t i; + uint32_t csrlen = 0; + if (len > 16) { + return ERR_INVALID_ARG; + } + /* COM[0..7], Seg[2,3] support animation */ + for (i = 0; i < len; i++) { + if ((SLCD_SEGNUM(segs[i]) != 2 && SLCD_SEGNUM(segs[i]) != 3)) { + return ERR_INVALID_ARG; + } + } + /* Verify period */ + if (period > SLCD_FC_MAX_MS || period < SLCD_FC_MIN_MS) { + return ERR_INVALID_ARG; + } + /* Set Period */ + _slcd_sync_set_animation_period(dev, period); + + /* Set animation segments */ + hri_slcd_clear_CTRLA_ENABLE_bit(dev->hw); + hri_slcd_clear_CTRLD_CSREN_bit(dev->hw); + + hri_slcd_wait_for_sync(dev->hw, SLCD_SYNCBUSY_ENABLE | SLCD_SYNCBUSY_CTRLD); + hri_slcd_set_CSRCFG_FCS_bf(dev->hw, 1); + hri_slcd_write_CSRCFG_DATA_bf(dev->hw, 0); + for (i = 0; i < len; i++) { + hri_slcd_set_CSRCFG_DATA_bf(dev->hw, (1 << ((SLCD_COMNUM(segs[i]) * 2) + (SLCD_SEGNUM(segs[i]) - 2)))); + if (((SLCD_COMNUM(segs[i]) * 2) + (SLCD_SEGNUM(segs[i]) - 2)) > csrlen) { + csrlen = (SLCD_COMNUM(segs[i]) * 2) + (SLCD_SEGNUM(segs[i]) - 2); + } + } + hri_slcd_set_CSRCFG_SIZE_bf(dev->hw, csrlen + 1); + hri_slcd_set_BCFG_MODE_bit(dev->hw); + hri_slcd_set_CTRLD_CSREN_bit(dev->hw); + hri_slcd_set_CTRLA_ENABLE_bit(dev->hw); + + return ERR_NONE; +} + +/** + * \brief Stop animation play by a segment array + */ +int32_t _slcd_sync_stop_animation(struct _slcd_sync_device *dev, const uint32_t segs[], uint32_t len) +{ + /* Not used because of the current version is not supported, Reserved */ + (void)segs; + (void)len; + hri_slcd_wait_for_sync(dev->hw, SLCD_SYNCBUSY_CTRLD); + hri_slcd_clear_CTRLD_CSREN_bit(dev->hw); + return ERR_NONE; +} + +/** + * \brief Set animation Frequency + */ +int32_t _slcd_sync_set_animation_period(struct _slcd_sync_device *dev, const uint32_t period) +{ + hri_slcd_clear_CTRLD_FC1EN_bit(dev->hw); + hri_slcd_wait_for_sync(dev->hw, SLCD_SYNCBUSY_CTRLD); + /* Use Frame Counter 1 for blink */ + if (period <= SLCD_FC_BYPASS_MAX_MS) { + hri_slcd_set_FC1_reg(dev->hw, SLCD_FC1_PB | ((period / (1000 / SLCD_FRAME_FREQUENCY)) - 1)); + } else { + hri_slcd_set_FC1_reg(dev->hw, (((period / (1000 / SLCD_FRAME_FREQUENCY)) / 8 - 1))); + } + hri_slcd_set_CTRLD_FC1EN_bit(dev->hw); + return ERR_NONE; +} + +static int32_t _slcd_sync_set_segment(struct _slcd_sync_device *dev, const uint32_t com, const uint32_t seg, + const bool on) +{ + if ((SLCD_COMNUM(seg) >= CONF_SLCD_COM_NUM) || (SLCD_SEGNUM(seg) >= CONF_SLCD_SEG_NUM)) { + return ERR_INVALID_ARG; + } + /* Use register instead hri interface to optimization code */ + if (on) { + ((uint32_t *)&(((Slcd *)dev->hw)->SDATAL0))[(com * 2) + (seg >> 5)] + |= (seg < 32) ? (1 << seg) : (1 << (seg >> 5)); + } else { + ((uint32_t *)&(((Slcd *)dev->hw)->SDATAL0))[(com * 2) + (seg >> 5)] + &= ~((seg < 32) ? (1 << seg) : (1 << (seg >> 5))); + } + + return ERR_NONE; +} diff --git a/watch-library/hpl/slcd/hpl_slcd_cm.h b/watch-library/hpl/slcd/hpl_slcd_cm.h new file mode 100644 index 00000000..66dbde93 --- /dev/null +++ b/watch-library/hpl/slcd/hpl_slcd_cm.h @@ -0,0 +1,59 @@ +/** + * \file + * + * \brief SLCD Character Mapping declaration. + * + * Copyright (c) 2015-2018 Microchip Technology Inc. and its subsidiaries. + * + * \asf_license_start + * + * \page License + * + * Subject to your compliance with these terms, you may use Microchip + * software and any derivatives exclusively with Microchip products. + * It is your responsibility to comply with third party license terms applicable + * to your use of third party software (including open source software) that + * may accompany Microchip software. + * + * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, + * WHETHER EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, + * INCLUDING ANY IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, + * AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE + * LIABLE FOR ANY INDIRECT, SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL + * LOSS, DAMAGE, COST OR EXPENSE OF ANY KIND WHATSOEVER RELATED TO THE + * SOFTWARE, HOWEVER CAUSED, EVEN IF MICROCHIP HAS BEEN ADVISED OF THE + * POSSIBILITY OR THE DAMAGES ARE FORESEEABLE. TO THE FULLEST EXTENT + * ALLOWED BY LAW, MICROCHIP'S TOTAL LIABILITY ON ALL CLAIMS IN ANY WAY + * RELATED TO THIS SOFTWARE WILL NOT EXCEED THE AMOUNT OF FEES, IF ANY, + * THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR THIS SOFTWARE. + * + * \asf_license_stop + * + */ + +#ifndef HPL_SLCD_CM_H_INCLUDED +#define HPL_SLCD_CM_H_INCLUDED + +#include + +/* Character Mapping Struct */ +struct slcd_char_mapping { + uint32_t character : 8; /*!< ASCII character */ + uint32_t mapping : 24; /*!< Mapping value */ +}; + +/* SLCD Character settting Struct */ +struct slcd_char_setting { + uint8_t com_index; /*!< Common terminal index, start from 0 */ + uint8_t seg_index; /*!< Segment terminal index, start from 0 */ + uint8_t nseg; /*!< Number of Segment, this field is used to + indentify which segments line will be + used. For example, if char mapping from + COM1/SEG2 and nseg=2,size=7, then + COM1/SEG2, COM1/SEG3, COM2/SEG2, + COM2/SEG3, COM3/SEG2, COM3/SEG3, + COM4/SEG2 will be used for mapping + */ + uint8_t size; /*!< char size, typical is 7/14/16 */ +}; +#endif /* HPL_SLCD_CM_H_INCLUDED */ diff --git a/watch-library/hpl/slcd/hpl_slcd_cm_14_seg_mapping.h b/watch-library/hpl/slcd/hpl_slcd_cm_14_seg_mapping.h new file mode 100644 index 00000000..65777d6d --- /dev/null +++ b/watch-library/hpl/slcd/hpl_slcd_cm_14_seg_mapping.h @@ -0,0 +1,104 @@ +#include + +/** + * character segments position index + * + * For a 14-segments character, each segment has an unique position index. + * The segment layout and position index value is shown as below. + * The symbol '-', '|', '/', '\' represent the SLCD character segment, and the + * number represent each segment's position index, which equals to macro + * SEG14_0 to SEG14_7. + * The character lookup mapping table use those position index + * (SEG14_0..SEG14_14) to compose some visible arabic numerals, letters or some + * specific ASCII. + * For example char '1' can be represented by use position index 1,2. + * (SEG7_1 | SEG7_2). + * More predefined character lookup can be found at below "14-segment character + * lookup mapping table" + * + * -0 + * |5 \6 |7 /8 |1 + * -9 -10 + * |4 /11 |12 \13 |2 + * -3 + */ + +/** + * Character segment position remapping setting + * + * An SLCD screen typically use several hardware segments to display a + * character(for example from COM3/SEG0 to COM4/SEG7). + * The lowest COM and SEG index is 0 (COM3/SEG0 index is 0). + * The "hardware character segments index" maybe different with the "character + * segments position index". The remapping setting allow application to adjust + * those sequence. + * + */ +#define SEG14_0 (0x1 << CONF_SLCD_CM_14SEGS_0_SETTING) +#define SEG14_1 (0x1 << CONF_SLCD_CM_14SEGS_1_SETTING) +#define SEG14_2 (0x1 << CONF_SLCD_CM_14SEGS_2_SETTING) +#define SEG14_3 (0x1 << CONF_SLCD_CM_14SEGS_3_SETTING) +#define SEG14_4 (0x1 << CONF_SLCD_CM_14SEGS_4_SETTING) +#define SEG14_5 (0x1 << CONF_SLCD_CM_14SEGS_5_SETTING) +#define SEG14_6 (0x1 << CONF_SLCD_CM_14SEGS_6_SETTING) +#define SEG14_7 (0x1 << CONF_SLCD_CM_14SEGS_7_SETTING) +#define SEG14_8 (0x1 << CONF_SLCD_CM_14SEGS_8_SETTING) +#define SEG14_9 (0x1 << CONF_SLCD_CM_14SEGS_9_SETTING) +#define SEG14_10 (0x1 << CONF_SLCD_CM_14SEGS_10_SETTING) +#define SEG14_11 (0x1 << CONF_SLCD_CM_14SEGS_11_SETTING) +#define SEG14_12 (0x1 << CONF_SLCD_CM_14SEGS_12_SETTING) +#define SEG14_13 (0x1 << CONF_SLCD_CM_14SEGS_13_SETTING) + +/** + * 17-segments character mask value + */ +#define SEG14_MASK \ + (0xFFFFFF \ + & ~(SEG14_0 | SEG14_1 | SEG14_2 | SEG14_3 | SEG14_4 | SEG14_5 | SEG14_6 | SEG14_7 | SEG14_8 | SEG14_9 | SEG14_10 \ + | SEG14_11 | SEG14_12 | SEG14_13)) + +/** + * 14-segment character lookup mapping table + * struct slcd_char_mapping; + */ +#define SLCD_SEG14_LUT \ + { \ + {'0', SEG14_0 | SEG14_1 | SEG14_2 | SEG14_3 | SEG14_4 | SEG14_5 | SEG14_8 | SEG14_11}, \ + {'1', SEG14_1 | SEG14_2}, {'2', SEG14_0 | SEG14_1 | SEG14_3 | SEG14_4 | SEG14_9 | SEG14_10}, \ + {'3', SEG14_0 | SEG14_1 | SEG14_2 | SEG14_3 | SEG14_9 | SEG14_10}, \ + {'4', SEG14_1 | SEG14_2 | SEG14_5 | SEG14_9 | SEG14_10}, \ + {'5', SEG14_0 | SEG14_2 | SEG14_3 | SEG14_5 | SEG14_9 | SEG14_10}, \ + {'6', SEG14_0 | SEG14_2 | SEG14_3 | SEG14_4 | SEG14_5 | SEG14_9 | SEG14_10}, \ + {'7', SEG14_0 | SEG14_1 | SEG14_2}, \ + {'8', SEG14_0 | SEG14_1 | SEG14_2 | SEG14_3 | SEG14_4 | SEG14_5 | SEG14_9 | SEG14_10}, \ + {'9', SEG14_0 | SEG14_1 | SEG14_2 | SEG14_3 | SEG14_5 | SEG14_9 | SEG14_10}, \ + {'a', SEG14_0 | SEG14_1 | SEG14_2 | SEG14_4 | SEG14_5 | SEG14_9 | SEG14_10}, \ + {'b', SEG14_0 | SEG14_1 | SEG14_2 | SEG14_3 | SEG14_7 | SEG14_10 | SEG14_12}, \ + {'c', SEG14_0 | SEG14_3 | SEG14_4 | SEG14_5}, \ + {'d', SEG14_0 | SEG14_1 | SEG14_2 | SEG14_3 | SEG14_7 | SEG14_12}, \ + {'e', SEG14_0 | SEG14_3 | SEG14_4 | SEG14_5 | SEG14_9 | SEG14_10}, \ + {'f', SEG14_0 | SEG14_4 | SEG14_5 | SEG14_9 | SEG14_10}, \ + {'g', SEG14_0 | SEG14_2 | SEG14_3 | SEG14_4 | SEG14_5 | SEG14_10}, \ + {'h', SEG14_1 | SEG14_2 | SEG14_4 | SEG14_5 | SEG14_9 | SEG14_10}, \ + {'i', SEG14_1 | SEG14_2 | SEG14_4 | SEG14_5 | SEG14_9 | SEG14_10}, \ + {'j', SEG14_1 | SEG14_2 | SEG14_3 | SEG14_4}, {'k', SEG14_4 | SEG14_5 | SEG14_8 | SEG14_9 | SEG14_13}, \ + {'l', SEG14_3 | SEG14_4 | SEG14_5}, {'m', SEG14_1 | SEG14_2 | SEG14_4 | SEG14_5 | SEG14_6 | SEG14_8}, \ + {'n', SEG14_1 | SEG14_2 | SEG14_4 | SEG14_5 | SEG14_6 | SEG14_13}, \ + {'o', SEG14_0 | SEG14_1 | SEG14_2 | SEG14_3 | SEG14_4 | SEG14_5}, \ + {'p', SEG14_0 | SEG14_1 | SEG14_4 | SEG14_5 | SEG14_9 | SEG14_10}, \ + {'q', SEG14_0 | SEG14_1 | SEG14_2 | SEG14_3 | SEG14_4 | SEG14_5 | SEG14_13}, \ + {'r', SEG14_0 | SEG14_1 | SEG14_4 | SEG14_5 | SEG14_9 | SEG14_10 | SEG14_13}, \ + {'s', SEG14_0 | SEG14_3 | SEG14_5 | SEG14_9 | SEG14_13}, {'t', SEG14_0 | SEG14_7 | SEG14_12}, \ + {'u', SEG14_1 | SEG14_2 | SEG14_3 | SEG14_4 | SEG14_5}, {'v', SEG14_4 | SEG14_5 | SEG14_8 | SEG14_11}, \ + {'w', SEG14_1 | SEG14_2 | SEG14_4 | SEG14_5 | SEG14_11 | SEG14_13}, \ + {'x', SEG14_6 | SEG14_8 | SEG14_11 | SEG14_13}, \ + {'y', SEG14_1 | SEG14_2 | SEG14_3 | SEG14_5 | SEG14_9 | SEG14_10}, \ + {'z', SEG14_0 | SEG14_3 | SEG14_8 | SEG14_11}, {'-', SEG14_9 | SEG14_10}, \ + {'+', SEG14_7 | SEG14_9 | SEG14_10 | SEG14_12}, {'/', SEG14_8 | SEG14_11}, \ + {'=', SEG14_3 | SEG14_9 | SEG14_10}, \ + {'#', SEG14_1 | SEG14_2 | SEG14_3 | SEG14_7 | SEG14_9 | SEG14_10 | SEG14_12}, \ + {'*', SEG14_6 | SEG14_8 | SEG14_11 | SEG14_13}, {'\'', SEG14_13}, {')', SEG14_6 | SEG14_11}, \ + {'(', SEG14_8 | SEG14_13}, {'@', SEG14_0 | SEG14_1 | SEG14_2 | SEG14_3 | SEG14_4 | SEG14_9 | SEG14_13}, \ + {'$', SEG14_0 | SEG14_2 | SEG14_3 | SEG14_5 | SEG14_7 | SEG14_9 | SEG14_10 | SEG14_12}, \ + {'%', SEG14_2 | SEG14_5 | SEG14_8 | SEG14_11}, {'\\', SEG14_6 | SEG14_13}, {'_', SEG14_3}, {0, 0}, \ + } diff --git a/watch-library/hpl/slcd/hpl_slcd_cm_7_seg_mapping.h b/watch-library/hpl/slcd/hpl_slcd_cm_7_seg_mapping.h new file mode 100644 index 00000000..eae344c8 --- /dev/null +++ b/watch-library/hpl/slcd/hpl_slcd_cm_7_seg_mapping.h @@ -0,0 +1,68 @@ +#include + +/** + * character segments position index + * + * For a 7 segments character, each segment has an unique position index. + * The segment layout and position index value is shown as below. + * The symbol '-', '|' represent the SLCD character segment, and the + * number represent each segment's position index, which equals to macro + * SEG7_0 to SEG7_7. + * The character lookup mapping table use those position index (SEG7_0..SEG7_7) + * to compose some visible arabic numerals, letters or some specific ASCII. + * For example char '0' can be represented by use position index 0,1,2,3,4,5. + * (SEG7_0 | SEG7_1 | SEG7_2 | SEG7_3 | SEG7_4 | SEG7_5). + * More predefined character lookup can be found at below "7-segments character + * lookup mapping table" + * -0 + * |5 |1 + * -6 + * |4 |2 + * -3 + */ + +/** + * Character segment position remapping setting + * + * An SLCD screen typically use several hardware segments to display a + * character(for example from COM3/SEG0 to COM3/SEG7). + * The lowest COM and SEG index is 0 (COM3/SEG0 index is 0). + * The "hardware character segments index" maybe different with the "character + * segments position index". The remapping setting allow application to adjust + * those sequence. + * + */ +#define SEG7_0 (0x1 << CONF_SLCD_CM_7SEGS_0_SETTING) +#define SEG7_1 (0x1 << CONF_SLCD_CM_7SEGS_1_SETTING) +#define SEG7_2 (0x1 << CONF_SLCD_CM_7SEGS_2_SETTING) +#define SEG7_3 (0x1 << CONF_SLCD_CM_7SEGS_3_SETTING) +#define SEG7_4 (0x1 << CONF_SLCD_CM_7SEGS_4_SETTING) +#define SEG7_5 (0x1 << CONF_SLCD_CM_7SEGS_5_SETTING) +#define SEG7_6 (0x1 << CONF_SLCD_CM_7SEGS_6_SETTING) + +/** + * 7-segments character mask value + */ +#define SEG7_MASK (0xFFFFFF & ~(SEG7_0 | SEG7_1 | SEG7_2 | SEG7_3 | SEG7_4 | SEG7_5 | SEG7_6)) + +/** + * 7-segments character lookup mapping table. + * + * Array value of slcd_char_mapping struct, application can add or remove + * item from it. + */ +#define SLCD_SEG7_LUT \ + { \ + {0, 0}, {'0', SEG7_0 | SEG7_1 | SEG7_2 | SEG7_3 | SEG7_4 | SEG7_5}, {'1', SEG7_1 | SEG7_2}, \ + {'2', SEG7_0 | SEG7_1 | SEG7_3 | SEG7_4 | SEG7_6}, {'3', SEG7_0 | SEG7_1 | SEG7_2 | SEG7_3 | SEG7_6}, \ + {'4', SEG7_1 | SEG7_2 | SEG7_5 | SEG7_6}, {'5', SEG7_0 | SEG7_2 | SEG7_3 | SEG7_5 | SEG7_6}, \ + {'6', SEG7_0 | SEG7_2 | SEG7_3 | SEG7_4 | SEG7_5 | SEG7_6}, {'7', SEG7_0 | SEG7_1 | SEG7_2}, \ + {'8', SEG7_0 | SEG7_1 | SEG7_2 | SEG7_3 | SEG7_4 | SEG7_5 | SEG7_6}, \ + {'9', SEG7_0 | SEG7_1 | SEG7_2 | SEG7_5 | SEG7_6}, \ + {'a', SEG7_0 | SEG7_1 | SEG7_2 | SEG7_4 | SEG7_5 | SEG7_6}, \ + {'b', SEG7_2 | SEG7_3 | SEG7_4 | SEG7_5 | SEG7_6}, {'c', SEG7_0 | SEG7_3 | SEG7_4 | SEG7_5}, \ + {'d', SEG7_1 | SEG7_2 | SEG7_3 | SEG7_4 | SEG7_6}, {'e', SEG7_0 | SEG7_3 | SEG7_4 | SEG7_5 | SEG7_6}, \ + { \ + 'f', SEG7_0 | SEG7_4 | SEG7_5 | SEG7_6 \ + } \ + } diff --git a/watch-library/hpl/systick/hpl_systick.c b/watch-library/hpl/systick/hpl_systick.c new file mode 100644 index 00000000..3caf6746 --- /dev/null +++ b/watch-library/hpl/systick/hpl_systick.c @@ -0,0 +1,103 @@ + +/** + * \file + * + * \brief SysTick related functionality implementation. + * + * Copyright (c) 2014-2018 Microchip Technology Inc. and its subsidiaries. + * + * \asf_license_start + * + * \page License + * + * Subject to your compliance with these terms, you may use Microchip + * software and any derivatives exclusively with Microchip products. + * It is your responsibility to comply with third party license terms applicable + * to your use of third party software (including open source software) that + * may accompany Microchip software. + * + * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, + * WHETHER EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, + * INCLUDING ANY IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, + * AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE + * LIABLE FOR ANY INDIRECT, SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL + * LOSS, DAMAGE, COST OR EXPENSE OF ANY KIND WHATSOEVER RELATED TO THE + * SOFTWARE, HOWEVER CAUSED, EVEN IF MICROCHIP HAS BEEN ADVISED OF THE + * POSSIBILITY OR THE DAMAGES ARE FORESEEABLE. TO THE FULLEST EXTENT + * ALLOWED BY LAW, MICROCHIP'S TOTAL LIABILITY ON ALL CLAIMS IN ANY WAY + * RELATED TO THIS SOFTWARE WILL NOT EXCEED THE AMOUNT OF FEES, IF ANY, + * THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR THIS SOFTWARE. + * + * \asf_license_stop + * + */ + +#include +#include + +/** + * \brief Initialize system time module + */ +void _system_time_init(void *const hw) +{ + (void)hw; + SysTick->LOAD = (0xFFFFFF << SysTick_LOAD_RELOAD_Pos); + SysTick->CTRL = (1 << SysTick_CTRL_ENABLE_Pos) | (CONF_SYSTICK_TICKINT << SysTick_CTRL_TICKINT_Pos) + | (1 << SysTick_CTRL_CLKSOURCE_Pos); +} +/** + * \brief Initialize delay functionality + */ +void _delay_init(void *const hw) +{ + _system_time_init(hw); +} + +/** + * \brief De-initialize system time module + */ +void _system_time_deinit(void *const hw) +{ + (void)hw; + SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk; +} + +/** + * \brief Get system time + */ +system_time_t _system_time_get(const void *const hw) +{ + (void)hw; + return (system_time_t)SysTick->VAL; +} + +/** + * \brief Get maximum possible system time + */ +system_time_t _system_time_get_max_time_value(const void *const hw) +{ + (void)hw; + return 0xFFFFFF; +} +/** + * \brief Delay loop to delay n number of cycles + */ +void _delay_cycles(void *const hw, uint32_t cycles) +{ + (void)hw; + uint8_t n = cycles >> 24; + uint32_t buf = cycles; + + while (n--) { + SysTick->LOAD = 0xFFFFFF; + SysTick->VAL = 0xFFFFFF; + while (!(SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk)) + ; + buf -= 0xFFFFFF; + } + + SysTick->LOAD = buf; + SysTick->VAL = buf; + while (!(SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk)) + ; +} diff --git a/watch-library/hpl/tc/hpl_tc.c b/watch-library/hpl/tc/hpl_tc.c new file mode 100644 index 00000000..38fa8938 --- /dev/null +++ b/watch-library/hpl/tc/hpl_tc.c @@ -0,0 +1,374 @@ + +/** + * \file + * + * \brief SAM TC + * + * Copyright (c) 2014-2018 Microchip Technology Inc. and its subsidiaries. + * + * \asf_license_start + * + * \page License + * + * Subject to your compliance with these terms, you may use Microchip + * software and any derivatives exclusively with Microchip products. + * It is your responsibility to comply with third party license terms applicable + * to your use of third party software (including open source software) that + * may accompany Microchip software. + * + * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, + * WHETHER EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, + * INCLUDING ANY IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, + * AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE + * LIABLE FOR ANY INDIRECT, SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL + * LOSS, DAMAGE, COST OR EXPENSE OF ANY KIND WHATSOEVER RELATED TO THE + * SOFTWARE, HOWEVER CAUSED, EVEN IF MICROCHIP HAS BEEN ADVISED OF THE + * POSSIBILITY OR THE DAMAGES ARE FORESEEABLE. TO THE FULLEST EXTENT + * ALLOWED BY LAW, MICROCHIP'S TOTAL LIABILITY ON ALL CLAIMS IN ANY WAY + * RELATED TO THIS SOFTWARE WILL NOT EXCEED THE AMOUNT OF FEES, IF ANY, + * THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR THIS SOFTWARE. + * + * \asf_license_stop + * + */ + +#include +#include +#include +#include +#include +#include + +#ifndef CONF_TC0_ENABLE +#define CONF_TC0_ENABLE 0 +#endif +#ifndef CONF_TC1_ENABLE +#define CONF_TC1_ENABLE 0 +#endif +#ifndef CONF_TC2_ENABLE +#define CONF_TC2_ENABLE 0 +#endif +#ifndef CONF_TC3_ENABLE +#define CONF_TC3_ENABLE 0 +#endif +#ifndef CONF_TC4_ENABLE +#define CONF_TC4_ENABLE 0 +#endif +#ifndef CONF_TC5_ENABLE +#define CONF_TC5_ENABLE 0 +#endif +#ifndef CONF_TC6_ENABLE +#define CONF_TC6_ENABLE 0 +#endif +#ifndef CONF_TC7_ENABLE +#define CONF_TC7_ENABLE 0 +#endif + +/** + * \brief Macro is used to fill usart configuration structure based on its + * number + * + * \param[in] n The number of structures + */ +#define TC_CONFIGURATION(n) \ + { \ + n, TC##n##_IRQn, \ + TC_CTRLA_MODE(CONF_TC##n##_MODE) | TC_CTRLA_PRESCSYNC(CONF_TC##n##_PRESCSYNC) \ + | (CONF_TC##n##_RUNSTDBY << TC_CTRLA_RUNSTDBY_Pos) | (CONF_TC##n##_ONDEMAND << TC_CTRLA_ONDEMAND_Pos) \ + | TC_CTRLA_PRESCALER(CONF_TC##n##_PRESCALER) | (CONF_TC##n##_ALOCK << TC_CTRLA_ALOCK_Pos), \ + (CONF_TC##n##_OVFEO << TC_EVCTRL_OVFEO_Pos) | (CONF_TC##n##_TCEI << TC_EVCTRL_TCEI_Pos) \ + | (CONF_TC##n##_TCINV << TC_EVCTRL_TCINV_Pos) | (CONF_TC##n##_EVACT << TC_EVCTRL_EVACT_Pos) \ + | (CONF_TC##n##_MCEO0 << TC_EVCTRL_MCEO0_Pos) | (CONF_TC##n##_MCEO1 << TC_EVCTRL_MCEO1_Pos), \ + (CONF_TC##n##_DBGRUN << TC_DBGCTRL_DBGRUN_Pos), CONF_TC##n##_PER, CONF_TC##n##_CC0, CONF_TC##n##_CC1, \ + } +/** + * \brief TC configuration type + */ +struct tc_configuration { + uint8_t number; + IRQn_Type irq; + hri_tc_ctrla_reg_t ctrl_a; + hri_tc_evctrl_reg_t event_ctrl; + hri_tc_dbgctrl_reg_t dbg_ctrl; + hri_tccount8_per_reg_t per; + hri_tccount32_cc_reg_t cc0; + hri_tccount32_cc_reg_t cc1; +}; + +/** + * \brief Array of TC configurations + */ +static struct tc_configuration _tcs[] = { +#if CONF_TC0_ENABLE == 1 + TC_CONFIGURATION(0), +#endif +#if CONF_TC1_ENABLE == 1 + TC_CONFIGURATION(1), +#endif +#if CONF_TC2_ENABLE == 1 + TC_CONFIGURATION(2), +#endif +#if CONF_TC3_ENABLE == 1 + TC_CONFIGURATION(3), +#endif +#if CONF_TC4_ENABLE == 1 + TC_CONFIGURATION(4), +#endif +#if CONF_TC5_ENABLE == 1 + TC_CONFIGURATION(5), +#endif +#if CONF_TC6_ENABLE == 1 + TC_CONFIGURATION(6), +#endif +#if CONF_TC7_ENABLE == 1 + TC_CONFIGURATION(7), +#endif +}; +/** + * \brief Set of pointer to hal_pwm helper functions + */ +static struct _pwm_hpl_interface _tc_pwm_functions = { + _tc_pwm_init, + _tc_pwm_deinit, + _tc_start_pwm, + _tc_stop_pwm, + _tc_set_pwm_param, + _tc_is_pwm_enabled, + _tc_pwm_get_period, + _tc_pwm_get_duty, + _tc_pwm_set_irq_state, +}; + +static struct _pwm_device *_tc3_dev = NULL; + +static int8_t get_tc_index(const void *const hw); +static void _tc_init_irq_param(const void *const hw, void *dev); +static inline uint8_t _get_hardware_offset(const void *const hw); +/** + * \brief Initialize TC for PWM mode + */ +int32_t _tc_pwm_init(struct _pwm_device *const device, void *const hw) +{ + int8_t i = get_tc_index(hw); + device->hw = hw; + + if (!hri_tc_is_syncing(hw, TC_SYNCBUSY_SWRST)) { + if (hri_tc_get_CTRLA_reg(hw, TC_CTRLA_ENABLE)) { + hri_tc_clear_CTRLA_ENABLE_bit(hw); + hri_tc_wait_for_sync(hw, TC_SYNCBUSY_ENABLE); + } + hri_tc_write_CTRLA_reg(hw, TC_CTRLA_SWRST); + } + hri_tc_wait_for_sync(hw, TC_SYNCBUSY_SWRST); + + hri_tc_write_CTRLA_reg(hw, _tcs[i].ctrl_a); + hri_tc_write_DBGCTRL_reg(hw, _tcs[i].dbg_ctrl); + hri_tc_write_EVCTRL_reg(hw, _tcs[i].event_ctrl); + hri_tc_write_WAVE_reg(hw, TC_WAVE_WAVEGEN_MPWM_Val); + + if ((_tcs[i].ctrl_a & TC_CTRLA_MODE_Msk) == TC_CTRLA_MODE_COUNT32) { + hri_tccount32_write_CC_reg(hw, 0, _tcs[i].cc0); + hri_tccount32_write_CC_reg(hw, 1, _tcs[i].cc1); + } else if ((_tcs[i].ctrl_a & TC_CTRLA_MODE_Msk) == TC_CTRLA_MODE_COUNT16) { + hri_tccount16_write_CC_reg(hw, 0, (uint16_t)_tcs[i].cc0); + hri_tccount16_write_CC_reg(hw, 1, (uint16_t)_tcs[i].cc1); + } else { + /* 8-bit resolution is not accepted by duty cycle control */ + return -1; + } + + _tc_init_irq_param(hw, (void *)device); + NVIC_DisableIRQ(_tcs[i].irq); + NVIC_ClearPendingIRQ(_tcs[i].irq); + NVIC_EnableIRQ(_tcs[i].irq); + + return 0; +} +/** + * \brief De-initialize TC for PWM mode + */ +void _tc_pwm_deinit(struct _pwm_device *const device) +{ + void *const hw = device->hw; + int8_t i = get_tc_index(hw); + ASSERT(ARRAY_SIZE(_tcs)); + + NVIC_DisableIRQ(_tcs[i].irq); + + hri_tc_clear_CTRLA_ENABLE_bit(hw); + hri_tc_set_CTRLA_SWRST_bit(hw); +} +/** + * \brief Start PWM + */ +void _tc_start_pwm(struct _pwm_device *const device) +{ + hri_tc_set_CTRLA_ENABLE_bit(device->hw); +} +/** + * \brief Stop PWM + */ +void _tc_stop_pwm(struct _pwm_device *const device) +{ + hri_tc_clear_CTRLA_ENABLE_bit(device->hw); +} +/** + * \brief Set PWM parameter + */ +void _tc_set_pwm_param(struct _pwm_device *const device, const pwm_period_t period, const pwm_period_t duty_cycle) +{ + void *const hw = device->hw; + int8_t i = get_tc_index(hw); + + _tcs[i].cc0 = period; + _tcs[i].cc1 = duty_cycle; + if ((_tcs[i].ctrl_a & TC_CTRLA_MODE_Msk) == TC_CTRLA_MODE_COUNT32) { + hri_tccount32_write_CC_reg(hw, 0, _tcs[i].cc0); + hri_tccount32_write_CC_reg(hw, 1, _tcs[i].cc1); + } else { + hri_tccount16_write_CC_reg(hw, 0, _tcs[i].cc0); + hri_tccount16_write_CC_reg(hw, 1, _tcs[i].cc1); + } +} +/** + * \brief Get pwm waveform period value + */ +pwm_period_t _tc_pwm_get_period(const struct _pwm_device *const device) +{ + void *const hw = device->hw; + int8_t i = get_tc_index(hw); + + if ((_tcs[i].ctrl_a & TC_CTRLA_MODE_Msk) == TC_CTRLA_MODE_COUNT32) { + return (pwm_period_t)(hri_tccount32_read_CC_reg(hw, 0)); + } else { + return (pwm_period_t)(hri_tccount16_read_CC_reg(hw, 0)); + } +} +/** + * \brief Get pwm waveform duty cycle + */ +uint32_t _tc_pwm_get_duty(const struct _pwm_device *const device) +{ + void *const hw = device->hw; + int8_t i = get_tc_index(hw); + uint32_t per; + uint32_t duty_cycle; + + if ((_tcs[i].ctrl_a & TC_CTRLA_MODE_Msk) == TC_CTRLA_MODE_COUNT32) { + per = hri_tccount32_read_CC_reg(hw, 0); + duty_cycle = hri_tccount32_read_CC_reg(hw, 1); + } else { + per = hri_tccount16_read_CC_reg(hw, 0); + duty_cycle = hri_tccount16_read_CC_reg(hw, 1); + } + + return ((duty_cycle * 1000) / per); +} +/** + * \brief Check if PWM is running + */ +bool _tc_is_pwm_enabled(const struct _pwm_device *const device) +{ + return hri_tc_get_CTRLA_ENABLE_bit(device->hw); +} +/** + * \brief Enable/disable PWM interrupt + */ +void _tc_pwm_set_irq_state(struct _pwm_device *const device, const enum _pwm_callback_type type, const bool disable) +{ + ASSERT(device); + + if (PWM_DEVICE_PERIOD_CB == type) { + hri_tc_write_INTEN_OVF_bit(device->hw, disable); + } else if (PWM_DEVICE_ERROR_CB == type) { + hri_tc_write_INTEN_ERR_bit(device->hw, disable); + } +} + +/** + * \brief Retrieve timer helper functions + */ +struct _timer_hpl_interface *_tc_get_timer(void) +{ + return NULL; +} + +/** + * \brief Retrieve pwm helper functions + */ +struct _pwm_hpl_interface *_tc_get_pwm(void) +{ + return &_tc_pwm_functions; +} +/** + * \internal TC interrupt handler for PWM + * + * \param[in] instance TC instance number + */ +static void tc_pwm_interrupt_handler(struct _pwm_device *device) +{ + void *const hw = device->hw; + + if (hri_tc_get_interrupt_OVF_bit(hw)) { + hri_tc_clear_interrupt_OVF_bit(hw); + if (NULL != device->callback.pwm_period_cb) { + device->callback.pwm_period_cb(device); + } + } + if (hri_tc_get_INTEN_ERR_bit(hw)) { + hri_tc_clear_interrupt_ERR_bit(hw); + if (NULL != device->callback.pwm_error_cb) { + device->callback.pwm_error_cb(device); + } + } +} + +/** + * \brief TC interrupt handler + */ +void TC3_Handler(void) +{ + tc_pwm_interrupt_handler(_tc3_dev); +} + +/** + * \internal Retrieve TC index + * + * \param[in] hw The pointer to hardware instance + * + * \return The index of TC configuration + */ +static int8_t get_tc_index(const void *const hw) +{ + uint8_t index = _get_hardware_offset(hw); + uint8_t i; + + for (i = 0; i < ARRAY_SIZE(_tcs); i++) { + if (_tcs[i].number == index) { + return i; + } + } + + ASSERT(false); + return -1; +} + +/** + * \brief Init irq param with the given tc hardware instance + */ +static void _tc_init_irq_param(const void *const hw, void *dev) +{ + if (hw == TC3) { + _tc3_dev = (struct _pwm_device *)dev; + } +} + +/** + * \internal Retrieve TC hardware index + * + * \param[in] hw The pointer to hardware instance + */ +static inline uint8_t _get_hardware_offset(const void *const hw) +{ + return (((uint32_t)hw - (uint32_t)TC0) >> 10); +} diff --git a/watch-library/hpl/tc/hpl_tc_base.h b/watch-library/hpl/tc/hpl_tc_base.h new file mode 100644 index 00000000..d602ae66 --- /dev/null +++ b/watch-library/hpl/tc/hpl_tc_base.h @@ -0,0 +1,160 @@ +/** + * \file + * + * \brief SAM Timer/Counter + * + * Copyright (c) 2014-2018 Microchip Technology Inc. and its subsidiaries. + * + * \asf_license_start + * + * \page License + * + * Subject to your compliance with these terms, you may use Microchip + * software and any derivatives exclusively with Microchip products. + * It is your responsibility to comply with third party license terms applicable + * to your use of third party software (including open source software) that + * may accompany Microchip software. + * + * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, + * WHETHER EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, + * INCLUDING ANY IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, + * AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE + * LIABLE FOR ANY INDIRECT, SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL + * LOSS, DAMAGE, COST OR EXPENSE OF ANY KIND WHATSOEVER RELATED TO THE + * SOFTWARE, HOWEVER CAUSED, EVEN IF MICROCHIP HAS BEEN ADVISED OF THE + * POSSIBILITY OR THE DAMAGES ARE FORESEEABLE. TO THE FULLEST EXTENT + * ALLOWED BY LAW, MICROCHIP'S TOTAL LIABILITY ON ALL CLAIMS IN ANY WAY + * RELATED TO THIS SOFTWARE WILL NOT EXCEED THE AMOUNT OF FEES, IF ANY, + * THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR THIS SOFTWARE. + * + * \asf_license_stop + */ + +#ifndef _HPL_TC_BASE_H_INCLUDED +#define _HPL_TC_BASE_H_INCLUDED + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \addtogroup tc_group TC Hardware Proxy Layer + * + * \section tc_hpl_rev Revision History + * - v0.0.0.1 Initial Commit + * + *@{ + */ + +/** + * \name HPL functions + */ +//@{ + +/** + * \brief Retrieve timer helper functions + * + * \return A pointer to set of timer helper functions + */ +struct _timer_hpl_interface *_tc_get_timer(void); +/** + * \brief Initialize TC for PWM + * + * This function does low level TC configuration. + * + * \param[in] device The pointer to TC device instance + * \param[in] hw The pointer to hardware instance + * + * \return Initialization status. + */ +int32_t _tc_pwm_init(struct _pwm_device *const device, void *const hw); + +/** + * \brief De-initialize TC for PWM + * + * \param[in] device The pointer to TC device instance + */ +void _tc_pwm_deinit(struct _pwm_device *const device); + +/** + * \brief Retrieve offset of the given tc hardware instance + * + * \param[in] device The pointer to TC device instance + * + * \return The offset of the given tc hardware instance + */ +uint8_t _tc_pwm_get_hardware_offset(const struct _pwm_device *const device); + +/** + * \brief Start PWM + * + * \param[in] device The pointer to TC device instance + */ +void _tc_start_pwm(struct _pwm_device *const device); + +/** + * \brief Stop PWM + * + * \param[in] device The pointer to TC device instance + */ +void _tc_stop_pwm(struct _pwm_device *const device); + +/** + * \brief Check if PWM is running + * + * \param[in] device The pointer to TC device instance + * + * \return Check status. + * \retval true The given timer is running + * \retval false The given timer is not running + */ +bool _tc_is_pwm_enabled(const struct _pwm_device *const device); + +/** + * \brief Set PWM parameter + * \param[in] device The pointer to TC device instance + * \param[in] period Total period of one PWM cycle. + * \param[in] duty_cycle Period of PWM first half during one cycle. + */ +void _tc_set_pwm_param(struct _pwm_device *const device, const pwm_period_t period, const pwm_period_t duty_cycle); + +/** + * \brief Get pwm waveform period value + * \param[in] device The pointer to TC device instance + * \return Period value. + */ +pwm_period_t _tc_pwm_get_period(const struct _pwm_device *const device); + +/** + * \brief Get pwm waveform duty cycle value + * \param[in] device The pointer to TC device instance + * \return Duty cycle value + */ +uint32_t _tc_pwm_get_duty(const struct _pwm_device *const device); + +/** + * \brief Enable/disable PWM interrupt + * + * param[in] device The pointer to PWM device instance + * param[in] type The type of interrupt to disable/enable if applicable + * param[in] disable Enable or disable + */ +void _tc_pwm_set_irq_state(struct _pwm_device *const device, const enum _pwm_callback_type type, const bool disable); + +/** + * \brief Retrieve pwm helper functions + * + * \return A pointer to set of pwm helper functions + */ +struct _pwm_hpl_interface *_tc_get_pwm(void); + +//@} +/**@}*/ + +#ifdef __cplusplus +} +#endif +#endif /* _HPL_TC_BASE_H_INCLUDED */ diff --git a/watch-library/hpl/tcc/hpl_tcc.c b/watch-library/hpl/tcc/hpl_tcc.c new file mode 100644 index 00000000..52fc6f3c --- /dev/null +++ b/watch-library/hpl/tcc/hpl_tcc.c @@ -0,0 +1,364 @@ +/** + * \file + * + * \brief SAM TCC + * + * Copyright (c) 2014-2019 Microchip Technology Inc. and its subsidiaries. + * + * \asf_license_start + * + * \page License + * + * Subject to your compliance with these terms, you may use Microchip + * software and any derivatives exclusively with Microchip products. + * It is your responsibility to comply with third party license terms applicable + * to your use of third party software (including open source software) that + * may accompany Microchip software. + * + * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, + * WHETHER EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, + * INCLUDING ANY IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, + * AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE + * LIABLE FOR ANY INDIRECT, SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL + * LOSS, DAMAGE, COST OR EXPENSE OF ANY KIND WHATSOEVER RELATED TO THE + * SOFTWARE, HOWEVER CAUSED, EVEN IF MICROCHIP HAS BEEN ADVISED OF THE + * POSSIBILITY OR THE DAMAGES ARE FORESEEABLE. TO THE FULLEST EXTENT + * ALLOWED BY LAW, MICROCHIP'S TOTAL LIABILITY ON ALL CLAIMS IN ANY WAY + * RELATED TO THIS SOFTWARE WILL NOT EXCEED THE AMOUNT OF FEES, IF ANY, + * THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR THIS SOFTWARE. + * + * \asf_license_stop + * + */ + +#include +#include +#include +#include +#include +#include +#include + +/** + * \brief TCC configuration type + */ +struct tcc_cfg { + void * hw; /*!< instance of TCC */ + IRQn_Type irq; + hri_tcc_ctrla_reg_t ctrl_a; + hri_tcc_ctrlbset_reg_t ctrl_b; + hri_tcc_dbgctrl_reg_t dbg_ctrl; + hri_tcc_evctrl_reg_t event_ctrl; + hri_tcc_cc_reg_t cc0; + hri_tcc_cc_reg_t cc1; + hri_tcc_cc_reg_t cc2; + hri_tcc_cc_reg_t cc3; + hri_tcc_per_reg_t per; +}; +/** + * \brief pwm configuration type + */ +struct tcc_pwm_cfg { + void * hw; /*!< instance of TCC */ + IRQn_Type irq; + uint8_t sel_ch; + uint32_t period; + uint32_t duty_cycle; + uint32_t wave; +}; + +/** + * \internal Retrieve configuration + * + * \param[in] hw The pointer of TCC base address + * + * \return The configuration + */ +static struct tcc_cfg *_get_tcc_cfg(void *hw); + +/** + * \brief Array of TCC configurations + */ +static struct tcc_cfg _cfgs[1] = { + {(void *)TCC0, + TCC0_IRQn, + CONF_TCC0_CTRLA, + CONF_TCC0_CTRLB, + CONF_TCC0_DBGCTRL, + CONF_TCC0_EVCTRL, + CONF_TCC0_CC0, + CONF_TCC0_CC1, + CONF_TCC0_CC2, + CONF_TCC0_CC3, + CONF_TCC0_PER}, +}; + +/** + * \internal Retrieve configuration + * + * \param[in] hw The pointer of TCC base address + * + * \return The configuration + */ +static struct tcc_pwm_cfg *_get_tcc_pwm_cfg(void *hw); + +/** + * \brief Array of PWM configurations + */ +static struct tcc_pwm_cfg _cfgs_pwm[1] = { + {(void *)TCC0, + TCC0_IRQn, + CONF_TCC0_SEL_CH, + CONF_TCC0_PER_REG, + CONF_TCC0_CCX_REG, + (CONF_TCC0_WAVEGEN << TCC_WAVE_WAVEGEN_Pos)}, +}; +/* Renamed access REG name PERB -> PERBUF */ +#define hri_tcc_write_PERB_reg hri_tcc_write_PERBUF_reg +#define hri_tcc_read_PERB_reg hri_tcc_read_PERBUF_reg + +/** Renamed access REG name CCB -> CCBUF */ +#define hri_tcc_write_CCB_reg hri_tcc_write_CCBUF_reg +#define hri_tcc_read_CCB_reg hri_tcc_read_CCBUF_reg + +static struct _pwm_device *_tcc0_dev = NULL; + +/** + * \brief Set of pointer to hal_pwm helper functions + */ +static struct _pwm_hpl_interface _tcc_pwm_functions = { + _tcc_pwm_init, + _tcc_pwm_deinit, + _tcc_start_pwm, + _tcc_stop_pwm, + _tcc_set_pwm_param, + _tcc_is_pwm_enabled, + _tcc_pwm_get_period, + _tcc_pwm_get_duty, + _tcc_pwm_set_irq_state, +}; + +/** + * \brief Init irq param with the given tcc hardware instance + */ +static void _tcc_init_irq_param(const void *const hw, void *dev) +{ + if (hw == TCC0) { + _tcc0_dev = (struct _pwm_device *)dev; + } +} +/** + * \brief Initialize TCC for PWM mode + */ +int32_t _tcc_pwm_init(struct _pwm_device *const device, void *const hw) +{ + struct tcc_cfg *cfg = _get_tcc_cfg(hw); + if (cfg == NULL) { + return ERR_NOT_FOUND; + } + struct tcc_pwm_cfg *cfg_pwm = _get_tcc_pwm_cfg(hw); + if (cfg_pwm == NULL) { + return ERR_NOT_FOUND; + } + + device->hw = hw; + + if (!hri_tcc_is_syncing(hw, TCC_SYNCBUSY_SWRST)) { + if (hri_tcc_get_CTRLA_reg(hw, TCC_CTRLA_ENABLE)) { + hri_tcc_clear_CTRLA_ENABLE_bit(hw); + hri_tcc_wait_for_sync(hw, TCC_SYNCBUSY_ENABLE); + } + hri_tcc_write_CTRLA_reg(hw, TCC_CTRLA_SWRST); + } + hri_tcc_wait_for_sync(hw, TCC_SYNCBUSY_SWRST); + + hri_tcc_write_CTRLA_reg(hw, cfg->ctrl_a); + hri_tcc_set_CTRLB_reg(hw, cfg->ctrl_b); + hri_tcc_write_DBGCTRL_reg(hw, cfg->dbg_ctrl); + hri_tcc_write_EVCTRL_reg(hw, cfg->event_ctrl); + + hri_tcc_write_WAVE_reg(hw, cfg_pwm->wave); + hri_tcc_write_PER_reg(hw, cfg_pwm->period); + cfg->per = cfg_pwm->period; + switch (cfg_pwm->sel_ch) { + case 0: + cfg->cc0 = cfg_pwm->duty_cycle; + hri_tcc_write_CC_reg(hw, 0, cfg->cc0); + break; + case 1: + cfg->cc1 = cfg_pwm->duty_cycle; + hri_tcc_write_CC_reg(hw, 1, cfg->cc1); + break; + case 2: + cfg->cc2 = cfg_pwm->duty_cycle; + hri_tcc_write_CC_reg(hw, 2, cfg->cc2); + break; + case 3: + cfg->cc3 = cfg_pwm->duty_cycle; + hri_tcc_write_CC_reg(hw, 3, cfg->cc3); + break; + default: + return ERR_NO_RESOURCE; + break; + } + hri_tcc_clear_CTRLB_LUPD_bit(hw); + + _tcc_init_irq_param(hw, (void *)device); + NVIC_DisableIRQ((IRQn_Type)cfg_pwm->irq); + NVIC_ClearPendingIRQ((IRQn_Type)cfg_pwm->irq); + NVIC_EnableIRQ((IRQn_Type)cfg_pwm->irq); + + return ERR_NONE; +} +/** + * \brief De-initialize TCC for PWM mode + */ +void _tcc_pwm_deinit(struct _pwm_device *const device) +{ + void *const hw = device->hw; + struct tcc_pwm_cfg *cfg_pwm = _get_tcc_pwm_cfg(hw); + if (cfg_pwm != NULL) { + NVIC_DisableIRQ((IRQn_Type)cfg_pwm->irq); + hri_tcc_clear_CTRLA_ENABLE_bit(hw); + hri_tcc_set_CTRLA_SWRST_bit(hw); + } +} +/** + * \brief Start PWM + */ +void _tcc_start_pwm(struct _pwm_device *const device) +{ + hri_tcc_set_CTRLA_ENABLE_bit(device->hw); +} +/** + * \brief Stop PWM + */ +void _tcc_stop_pwm(struct _pwm_device *const device) +{ + hri_tcc_clear_CTRLA_ENABLE_bit(device->hw); +} +/** + * \brief Set PWM parameter + */ +void _tcc_set_pwm_param(struct _pwm_device *const device, const pwm_period_t period, const pwm_period_t duty_cycle) +{ + void *const hw = device->hw; + struct tcc_pwm_cfg *cfg_pwm = _get_tcc_pwm_cfg(hw); + if (cfg_pwm != NULL) { + hri_tcc_write_PERB_reg(hw, period); + hri_tcc_write_CCB_reg(hw, cfg_pwm->sel_ch, duty_cycle); + ; + } +} +/** + * \brief Get pwm waveform period value + */ +pwm_period_t _tcc_pwm_get_period(const struct _pwm_device *const device) +{ + return (pwm_period_t)(hri_tcc_read_PERB_reg(device->hw)); +} +/** + * \brief Get pwm waveform duty cycle + */ +uint32_t _tcc_pwm_get_duty(const struct _pwm_device *const device) +{ + void *const hw = device->hw; + struct tcc_pwm_cfg *cfg_pwm = _get_tcc_pwm_cfg(hw); + if (cfg_pwm == NULL) { + return ERR_NOT_FOUND; + } + uint32_t per = hri_tcc_read_PERB_reg(hw); + uint32_t duty_cycle = hri_tcc_read_CCB_reg(hw, cfg_pwm->sel_ch); + + return ((duty_cycle * 1000) / per); +} +/** + * \brief Check if PWM is running + */ +bool _tcc_is_pwm_enabled(const struct _pwm_device *const device) +{ + return hri_tcc_get_CTRLA_ENABLE_bit(device->hw); +} +/** + * \brief Enable/disable PWM interrupt + */ +void _tcc_pwm_set_irq_state(struct _pwm_device *const device, const enum _pwm_callback_type type, const bool disable) +{ + ASSERT(device); + + if (PWM_DEVICE_PERIOD_CB == type) { + hri_tcc_write_INTEN_OVF_bit(device->hw, disable); + } else if (PWM_DEVICE_ERROR_CB == type) { + hri_tcc_write_INTEN_ERR_bit(device->hw, disable); + } +} + +/** + * \brief Retrieve timer helper functions + */ +struct _timer_hpl_interface *_tcc_get_timer(void) +{ + return NULL; +} + +/** + * \brief Retrieve pwm helper functions + */ +struct _pwm_hpl_interface *_tcc_get_pwm(void) +{ + return &_tcc_pwm_functions; +} +/** + * \internal TC interrupt handler for PWM + * + * \param[in] instance TC instance number + */ +static void tcc_pwm_interrupt_handler(struct _pwm_device *device) +{ + void *const hw = device->hw; + + if (hri_tcc_get_interrupt_OVF_bit(hw)) { + hri_tcc_clear_interrupt_OVF_bit(hw); + if (NULL != device->callback.pwm_period_cb) { + device->callback.pwm_period_cb(device); + } + } + if (hri_tcc_get_INTEN_ERR_bit(hw)) { + hri_tcc_clear_interrupt_ERR_bit(hw); + if (NULL != device->callback.pwm_error_cb) { + device->callback.pwm_error_cb(device); + } + } +} + +/** + * \brief TCC interrupt handler + */ +void TCC0_Handler(void) +{ + tcc_pwm_interrupt_handler(_tcc0_dev); +} + +static struct tcc_cfg *_get_tcc_cfg(void *hw) +{ + uint8_t i; + + for (i = 0; i < ARRAY_SIZE(_cfgs); i++) { + if (_cfgs[i].hw == hw) { + return &(_cfgs[i]); + } + } + return NULL; +} + +static struct tcc_pwm_cfg *_get_tcc_pwm_cfg(void *hw) +{ + uint8_t i; + + for (i = 0; i < ARRAY_SIZE(_cfgs_pwm); i++) { + if (_cfgs_pwm[i].hw == hw) { + return &(_cfgs_pwm[i]); + } + } + return NULL; +} diff --git a/watch-library/hpl/tcc/hpl_tcc.h b/watch-library/hpl/tcc/hpl_tcc.h new file mode 100644 index 00000000..6ccf04f3 --- /dev/null +++ b/watch-library/hpl/tcc/hpl_tcc.h @@ -0,0 +1,160 @@ +/** + * \file + * + * \brief SAM Timer/Counter for Control Applications + * + * Copyright (c) 2014-2018 Microchip Technology Inc. and its subsidiaries. + * + * \asf_license_start + * + * \page License + * + * Subject to your compliance with these terms, you may use Microchip + * software and any derivatives exclusively with Microchip products. + * It is your responsibility to comply with third party license terms applicable + * to your use of third party software (including open source software) that + * may accompany Microchip software. + * + * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, + * WHETHER EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, + * INCLUDING ANY IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, + * AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE + * LIABLE FOR ANY INDIRECT, SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL + * LOSS, DAMAGE, COST OR EXPENSE OF ANY KIND WHATSOEVER RELATED TO THE + * SOFTWARE, HOWEVER CAUSED, EVEN IF MICROCHIP HAS BEEN ADVISED OF THE + * POSSIBILITY OR THE DAMAGES ARE FORESEEABLE. TO THE FULLEST EXTENT + * ALLOWED BY LAW, MICROCHIP'S TOTAL LIABILITY ON ALL CLAIMS IN ANY WAY + * RELATED TO THIS SOFTWARE WILL NOT EXCEED THE AMOUNT OF FEES, IF ANY, + * THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR THIS SOFTWARE. + * + * \asf_license_stop + */ + +#ifndef _HPL_TCC_V101_BASE_H_INCLUDED +#define _HPL_TCC_V101_BASE_H_INCLUDED + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \addtogroup tcc_group TCC Low Level Driver Helpers + * + * \section tcc_helpers_rev Revision History + * - v0.0.0.1 Initial Commit + * + *@{ + */ + +/** + * \name HPL functions + */ +//@{ + +/** + * \brief Retrieve timer helper functions + * + * \return A pointer to set of timer helper functions + */ +struct _timer_hpl_interface *_tcc_get_timer(void); +/** + * \brief Initialize TCC for PWM + * + * This function does low level TCC configuration. + * + * \param[in] device The pointer to PWM device instance + * \param[in] hw The pointer to hardware instance + * + * \return Initialization status. + */ +int32_t _tcc_pwm_init(struct _pwm_device *const device, void *const hw); + +/** + * \brief De-initialize TCC for PWM + * + * \param[in] device The pointer to TCC device instance + */ +void _tcc_pwm_deinit(struct _pwm_device *const device); + +/** + * \brief Retrieve offset of the given tcc hardware instance + * + * \param[in] device The pointer to TCC device instance + * + * \return The offset of the given tcc hardware instance + */ +uint8_t _tcc_pwm_get_hardware_offset(const struct _pwm_device *const device); + +/** + * \brief Start PWM + * + * \param[in] device The pointer to TCC device instance + */ +void _tcc_start_pwm(struct _pwm_device *const device); + +/** + * \brief Stop PWM + * + * \param[in] device The pointer to TCC device instance + */ +void _tcc_stop_pwm(struct _pwm_device *const device); + +/** + * \brief Check if PWM is running + * + * \param[in] device The pointer to TCC device instance + * + * \return Check status. + * \retval true The given pwm is running + * \retval false The given pwm is not running + */ +bool _tcc_is_pwm_enabled(const struct _pwm_device *const device); + +/** + * \brief Set PWM parameter + * \param[in] device The pointer to TCC device instance + * \param[in] period Total period of one PWM cycle. + * \param[in] duty_cycle Period of PWM first half during one cycle. + */ +void _tcc_set_pwm_param(struct _pwm_device *const device, const pwm_period_t period, const pwm_period_t duty_cycle); + +/** + * \brief Get pwm waveform period value + * \param[in] device The pointer to TCC device instance + * \return Period value. + */ +pwm_period_t _tcc_pwm_get_period(const struct _pwm_device *const device); + +/** + * \brief Get pwm waveform duty cycle value + * \param[in] device The pointer to TCC device instance + * \return Duty cycle value + */ +uint32_t _tcc_pwm_get_duty(const struct _pwm_device *const device); + +/** + * \brief Enable/disable PWM interrupt + * + * param[in] device The pointer to PWM device instance + * param[in] type The type of interrupt to disable/enable if applicable + * param[in] disable Enable or disable + */ +void _tcc_pwm_set_irq_state(struct _pwm_device *const device, const enum _pwm_callback_type type, const bool disable); + +/** + * \brief Retrieve pwm helper functions + * + * \return A pointer to set of pwm helper functions + */ +struct _pwm_hpl_interface *_tcc_get_pwm(void); + +//@} +/**@}*/ + +#ifdef __cplusplus +} +#endif +#endif /* _HPL_TCC_V101_BASE_H_INCLUDED */ -- cgit v1.2.3