/* Reflow Oven Controller * * Copyright (C) 2020 Mario Hüttel * * This file is part of the Reflow Oven Controller Project. * * The reflow oven controller is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * The Reflow Oven Control Firmware is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with the reflow oven controller project. * If not, see . */ /** * @addtogroup safety-adc * @{ */ #include #include #include #include #include #include static const uint8_t safety_adc_channels[SAFETY_ADC_NUM_OF_CHANNELS] = {SAFETY_ADC_CHANNELS}; /** * @brief Safety ADC conversion complete. Set in interrupt. */ static volatile uint8_t IN_SECTION(.ccm.bss) safety_adc_conversion_complete; /** * @brief Safety ADC has been started. It will perform all specified conversions and * set @ref safety_adc_conversion_complete afterwards */ static volatile uint8_t IN_SECTION(.ccm.bss) safety_adc_triggered; /** * @brief Safety ADC conversion storage. This is filled by DMA. * @note Do not move this to CCM RAM as the DMA won't be able to access it. */ static volatile uint16_t safety_adc_conversions[SAFETY_ADC_NUM_OF_CHANNELS]; void safety_adc_init(void) { enum hw_revision hw_rev; int i; hw_rev = get_pcb_hardware_version(); rcc_manager_enable_clock(&RCC->APB2ENR, BITMASK_TO_BITNO(SAFETY_ADC_ADC_RCC_MASK)); rcc_manager_enable_clock(&RCC->AHB1ENR, BITMASK_TO_BITNO(RCC_AHB1ENR_DMA2EN)); if (hw_rev != HW_REV_V1_2) { rcc_manager_enable_clock(&RCC->AHB1ENR, BITMASK_TO_BITNO(SAFETY_ADC_SUPPLY_VOLTAGE_MONITOR_PORT_RCC_MASK)); SAFETY_ADC_SUPPLY_VOLTAGE_MONITOR_PORT->MODER &= MODER_DELETE(SAFETY_ADC_SUPPLY_VOLTAGE_MONITOR_PIN); SAFETY_ADC_SUPPLY_VOLTAGE_MONITOR_PORT->MODER |= ANALOG(SAFETY_ADC_SUPPLY_VOLTAGE_MONITOR_PIN); } /* Enable temperature and VREFINT measurement */ ADC->CCR |= ADC_CCR_TSVREFE; /* Set sample time for channels 16 and 17 and 15 */ SAFETY_ADC_ADC_PERIPHERAL->SMPR1 |= ADC_SMPR1_SMP17 | ADC_SMPR1_SMP16 | ADC_SMPR1_SMP15; /* Standard sequence. Measure all channels in one sequence */ SAFETY_ADC_ADC_PERIPHERAL->SQR1 = (SAFETY_ADC_NUM_OF_CHANNELS - 1) << 20; SAFETY_ADC_ADC_PERIPHERAL->SQR2 = 0UL; SAFETY_ADC_ADC_PERIPHERAL->SQR3 = 0UL; for (i = 0; i < SAFETY_ADC_NUM_OF_CHANNELS; i++) { switch (i) { case 0 ... 5: SAFETY_ADC_ADC_PERIPHERAL->SQR3 |= safety_adc_channels[i] << (i * 5); break; case 6 ... 11: SAFETY_ADC_ADC_PERIPHERAL->SQR2 |= safety_adc_channels[i] << ((i-6) * 5); break; case 12 ... 15: SAFETY_ADC_ADC_PERIPHERAL->SQR1 |= safety_adc_channels[i] << ((i-12) * 5); break; } } safety_adc_conversion_complete = 0; safety_adc_triggered = 0; /* Setup the DMA to move the data */ DMA2_Stream4->PAR = (uint32_t)&SAFETY_ADC_ADC_PERIPHERAL->DR; DMA2_Stream4->M0AR = (uint32_t)safety_adc_conversions; DMA2_Stream4->NDTR = SAFETY_ADC_NUM_OF_CHANNELS; DMA2_Stream4->CR = DMA_SxCR_PL_0 | DMA_SxCR_MSIZE_0 | DMA_SxCR_PSIZE_0 | DMA_SxCR_MINC | DMA_SxCR_CIRC | DMA_SxCR_TCIE | DMA_SxCR_EN; NVIC_EnableIRQ(DMA2_Stream4_IRQn); /* Enable ADC */ SAFETY_ADC_ADC_PERIPHERAL->CR1 |= ADC_CR1_SCAN; SAFETY_ADC_ADC_PERIPHERAL->CR2 = ADC_CR2_ADON | ADC_CR2_DMA | ADC_CR2_DDS; safety_controller_set_crc_monitor(ERR_CRC_MON_SAFETY_ADC, SAFETY_CRC_MON_SAFETY_ADC_PW); } void safety_adc_deinit(void) { SAFETY_ADC_ADC_PERIPHERAL->CR1 = 0UL; SAFETY_ADC_ADC_PERIPHERAL->CR2 = 0UL; SAFETY_ADC_ADC_PERIPHERAL->SMPR1 = 0UL; rcc_manager_disable_clock(&RCC->APB1ENR, BITMASK_TO_BITNO(RCC_APB2ENR_ADC2EN)); DMA2_Stream4->CR = 0; rcc_manager_disable_clock(&RCC->AHB1ENR, BITMASK_TO_BITNO(RCC_AHB1ENR_DMA2EN)); safety_controller_set_crc_monitor(ERR_CRC_MON_SAFETY_ADC, SAFETY_CRC_MON_SAFETY_ADC_PW); } float safety_adc_convert_channel(enum safety_adc_meas_channel channel, uint16_t analog_value) { float converted_val; enum hw_revision hw_rev; switch (channel) { case SAFETY_ADC_MEAS_TEMP: converted_val = (((float)analog_value / 4096.0f * 2500.0f - SAFETY_ADC_TEMP_NOM_MV) / SAFETY_ADC_TEMP_MV_SLOPE) + SAFETY_ADC_TEMP_NOM; break; case SAFETY_ADC_MEAS_VREF: converted_val = (SAFETY_ADC_INT_REF_MV * 4096.0f) / (float)analog_value; break; case SAFETY_ADC_MEAS_SUPPLY: hw_rev = get_pcb_hardware_version(); if (hw_rev >= HW_REV_V1_3) converted_val = ((float)analog_value) / 4096.0f * 2500.0f * 2.0f; else converted_val = 3300.0f; break; default: /* Generate NaN value as default return */ converted_val = 0.0f / 0.0f; break; } return converted_val; } int safety_adc_poll_result(void) { if (safety_adc_triggered) return 0; if (safety_adc_conversion_complete) return 1; else return -1; } const uint16_t *safety_adc_get_values(void) { safety_adc_conversion_complete = 0; return (const uint16_t *)safety_adc_conversions; } void safety_adc_trigger_meas(void) { safety_adc_conversion_complete = 0; SAFETY_ADC_ADC_PERIPHERAL->CR1 |= ADC_CR1_SCAN; SAFETY_ADC_ADC_PERIPHERAL->CR2 |= ADC_CR2_ADON; SAFETY_ADC_ADC_PERIPHERAL->CR2 |= ADC_CR2_SWSTART; safety_adc_triggered = 1; } void DMA2_Stream4_IRQHandler(void) { uint32_t hisr; hisr = DMA2->HISR & 0x3F; DMA2->HIFCR = hisr; if (hisr & DMA_HISR_TCIF4) { safety_adc_triggered = 0; safety_adc_conversion_complete = 1; } } /** @} */