diff --git a/include/core/io/ADC.hpp b/include/core/io/ADC.hpp index 19223308..b43f7f87 100644 --- a/include/core/io/ADC.hpp +++ b/include/core/io/ADC.hpp @@ -6,9 +6,10 @@ namespace core::io { // Forward declarations: -// The different pins are hardware specific. Forware declarationsto allow +// The different pins are hardware specific. Forward declarations to allow // at compilation time the decision of which pins should be used. enum class Pin; +enum class ADCPeriph; class ADC { @@ -18,7 +19,7 @@ class ADC { * * @param[in] pin The pin to setup for ADC */ - ADC(Pin pin); + ADC(Pin pin, ADCPeriph adcPeriph); /** * Reads the current voltage in volts on the ADC @@ -46,6 +47,8 @@ class ADC { protected: /// The pin the ADC is attached to Pin pin; + /// The internal ADC being used + ADCPeriph adcPeriph; }; } // namespace core::io diff --git a/include/core/io/pin.hpp b/include/core/io/pin.hpp index 251e0472..57db1d73 100644 --- a/include/core/io/pin.hpp +++ b/include/core/io/pin.hpp @@ -14,6 +14,8 @@ namespace core::io { * these values. */ enum class Pin { + INVALID = -1, // THIS INTENTIONALLY DOES NOT POINT TO A PIN. Used as a default value, so the default value is + // no longer PA_O (a real pin) PA_0 = 0x00, PA_1 = 0x01, PA_2 = 0x02, diff --git a/include/core/io/platform/f3xx/ADCf3xx.hpp b/include/core/io/platform/f3xx/ADCf3xx.hpp index 33a332ef..783a7d47 100644 --- a/include/core/io/platform/f3xx/ADCf3xx.hpp +++ b/include/core/io/platform/f3xx/ADCf3xx.hpp @@ -8,6 +8,10 @@ namespace core::io { +enum class ADCPeriph { + ONE +}; + class ADCf3xx : public ADC { public: /** @@ -15,7 +19,7 @@ class ADCf3xx : public ADC { * * @param[in] pin The pin to setup for ADC */ - ADCf3xx(Pin pin); + ADCf3xx(Pin pin, ADCPeriph adcPeriph); float read(); @@ -41,6 +45,17 @@ class ADCf3xx : public ADC { static uint16_t buffer[MAX_CHANNELS]; static DMA_HandleTypeDef halDMA; + /** + * Bit packed struct to contain the channel along with the ADC peripherals the channel supports + * + * adc1: 1 bit. Support for ADC1 peripheral. 1 for supported, 0 for not supported. + * channel: 5 bits. The STM32 ADC channel value with said supported ADC peripherals + */ + struct Channel_Support { + uint8_t adc1 : 1; + uint8_t channel : 5; + }; + /** * Initialize the HAL ADC handler. This should only have to be run once */ @@ -58,6 +73,15 @@ class ADCf3xx : public ADC { * was added to the ADC starting at 1 */ void addChannel(uint8_t rank); + + /** + * Check if the channel that is being initialized supports the ADC peripheral that it is being initialized on. + * + * @param periph the ADC peripheral being used + * @param channelStruct the struct of the channel with supports to test + * @return true if channel is supported by the ADC peripheral, false otherwise + */ + static bool checkSupport(ADCPeriph periph, Channel_Support channelStruct); }; } // namespace core::io diff --git a/include/core/io/platform/f4xx/ADCf4xx.hpp b/include/core/io/platform/f4xx/ADCf4xx.hpp index f62a8a0a..b9f50acc 100644 --- a/include/core/io/platform/f4xx/ADCf4xx.hpp +++ b/include/core/io/platform/f4xx/ADCf4xx.hpp @@ -8,14 +8,21 @@ namespace core::io { +enum class ADCPeriph { + ONE, + TWO, + THREE +}; + class ADCf4xx : public ADC { public: /** * Setup the given pin for ADC usage * * @param[in] pin The pin to setup for ADC + * @param[in] adcPeriph The ADC peripheral being used */ - ADCf4xx(Pin pin); + ADCf4xx(Pin pin, ADCPeriph adcPeriph); float read(); @@ -25,40 +32,118 @@ class ADCf4xx : public ADC { private: // Max number of channels supported by the ADC - static constexpr uint8_t MAX_CHANNELS = 15; + static constexpr uint8_t MAX_CHANNELS = 16; + // Number of supported ADC Peripherals + static constexpr uint8_t NUM_ADCS = 3; // Positive reference voltage of the ADC. Needs to be updated based on the hardware configuration static constexpr float VREF_POS = 3.3; // Max value for a 12 bit ADC reading (2^12 - 1) static constexpr uint32_t MAX_RAW = 4095; - /// This is static since the STM32F3xx only had a single ADC which - /// supports multiple channels, so I made this one only use a single ADC. - /// The F446re has 3 12 bit ADC's. Currently not able to use the other 2. - /// The ADC will be initialized once then each channel will be added on. - static ADC_HandleTypeDef halADC; - /// Static list of all channels supported by the ADC - static Pin channels[MAX_CHANNELS]; - /// Buffer for DMA where each spot represents the value read in from a - /// channel - static uint16_t buffer[MAX_CHANNELS]; - static DMA_HandleTypeDef halDMA; + // Flag to indicate if the timer has been initialized + static bool timerInit; + // Timer handle for TIM8, used to configure and control the timer instance + static TIM_HandleTypeDef htim8; + + /** + * Structure to represent the state of an ADC instance. + * + * This structure holds the configuration and current state for an ADC instance, including the + * HAL handle for the ADC and DMA, channel configurations, and data buffers for readings. + * + * halADC: HAL handle for configuring and controlling the ADC peripheral. + * rank: The ADC channel rank in the sequence (starts at 1). + * isADCInit: Flag to indicate whether the ADC has been initialized. + * channels: Array of pins mapped to ADC channels. Initialized to all Pin::INVALID. + * The array size is defined by MAX_CHANNELS. + * buffer: Array to store ADC values for each channel, indexed according to the channels array. + * Each entry corresponds to a specific ADC channel. + * halDMA: HAL handle for configuring and controlling DMA for the ADC. + */ + struct ADC_State { + ADC_HandleTypeDef halADC = {0}; + uint8_t rank = 1; + bool isADCInit = false; + Pin channels[MAX_CHANNELS] = {Pin::INVALID, + Pin::INVALID, + Pin::INVALID, + Pin::INVALID, + Pin::INVALID, + Pin::INVALID, + Pin::INVALID, + Pin::INVALID, + Pin::INVALID, + Pin::INVALID, + Pin::INVALID, + Pin::INVALID, + Pin::INVALID, + Pin::INVALID, + Pin::INVALID, + Pin::INVALID}; + uint16_t buffer[MAX_CHANNELS] = {0}; + DMA_HandleTypeDef halDMA = {0}; + }; /** - * Initialize the HAL ADC handler. This should only have to be run once + * Bit packed struct to contain the channel along with the ADC peripherals the channel supports + * + * adc1: 1 bit. Support for ADC1 peripheral. 1 for supported, 0 for not supported. + * adc2: 1 bit. Support for ADC2 peripheral. 1 for supported, 0 for not supported. + * adc3: 1 bit. Support for ADC3 peripheral. 1 for supported, 0 for not supported. + * channel: 5 bits. The STM32 ADC channel value with said supported ADC peripherals + */ + struct Channel_Support { + uint8_t adc1 : 1; + uint8_t adc2 : 1; + uint8_t adc3 : 1; + uint8_t channel : 5; + }; + + // Array of all ADC peripheral states + static ADC_State adcArray[NUM_ADCS]; + // The ADC peripheral state of the current object + ADC_State& adcState; + // The number ADC peripheral which is being used in the current object + uint8_t adcNum; + + /** + * Check if the channel that is being initialized supports the ADC peripheral that it is being initialized on. + * + * @param periph the ADC peripheral being used + * @param channelStruct the struct of the channel with supports to test + * @return true if channel is supported by the ADC peripheral, false otherwise + */ + static bool checkSupport(ADCPeriph periph, Channel_Support channelStruct); + + /** + * Return the ADC number that is in use + * + * @return The adc number that is being used in this specific object + */ + static uint8_t getADCNum(ADCPeriph periph); + + /** + * Initialize the HAL ADC handler */ void initADC(uint8_t num_channels); /** - * Initialize the HAL DMA for the ADC, should only have to be run once + * Initialize the HAL DMA for the ADC */ void initDMA(); /** - * Adds an ADC channel to the HAL ADC device. + * Add an ADC channel to the HAL ADC device. * * @param rank The "rank" which represents the order in which the channel * was added to the ADC starting at 1 */ void addChannel(uint8_t rank); + + /** + * Initialize Timer 8 to send Update Events, which the ADC listens for to do a conversion + * aka controls ADC conversion frequency + */ + static void initTimer(); }; } // namespace core::io diff --git a/include/core/manager.hpp b/include/core/manager.hpp index 26311677..2b6bd231 100644 --- a/include/core/manager.hpp +++ b/include/core/manager.hpp @@ -147,14 +147,14 @@ namespace core::io { * @param[in] pin The pin to use with the ADC */ #ifdef ADC_SUPPORTED -template +template ADC& getADC() { #ifdef STM32F4xx - static ADCf4xx adc(pin); + static ADCf4xx adc(pin, adcPeriph); return adc; #endif #ifdef STM32F3xx - static ADCf3xx adc(pin); + static ADCf3xx adc(pin, adcPeriph); return adc; #endif } diff --git a/samples/adc/multi_adc.cpp b/samples/adc/multi_adc.cpp index d7e1fb01..8bec279a 100644 --- a/samples/adc/multi_adc.cpp +++ b/samples/adc/multi_adc.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include namespace io = core::io; @@ -17,23 +18,30 @@ int main() { io::UART& uart = io::getUART(9600); + // Set up the logger to catch errors in ADC creation + core::log::LOGGER.setUART(&uart); + core::log::LOGGER.setLogLevel(core::log::Logger::LogLevel::INFO); + uart.printf("Starting ADC test\r\n"); time::wait(500); - io::ADC& adc0 = io::getADC(); - io::ADC& adc1 = io::getADC(); + io::ADC& adc0 = io::getADC(); + io::ADC& adc1 = io::getADC(); while (1) { - uart.printf("--------------------\r\n"); - uart.printf("ADC0 : %d mV\r\n", static_cast(adc0.read() * 1000)); - uart.printf("ADC0: %d%%\r\n", static_cast(adc0.readPercentage() * 100)); - uart.printf("ADC0 raw: %d\r\n\r\n", adc0.readRaw()); - - uart.printf("ADC1 : %d mV\r\n", static_cast(adc1.read() * 1000)); - uart.printf("ADC1: %d%%\r\n", static_cast(adc1.readPercentage() * 100)); - uart.printf("ADC1 raw: %d\r\n", adc1.readRaw()); - uart.printf("--------------------\r\n\r\n"); + core::log::LOGGER.log(core::log::Logger::LogLevel::INFO, "--------------------"); + core::log::LOGGER.log( + core::log::Logger::LogLevel::INFO, "ADC0 : %d mV", static_cast(adc0.read() * 1000)); + core::log::LOGGER.log( + core::log::Logger::LogLevel::INFO, "ADC0: %d%%", static_cast(adc0.readPercentage() * 100)); + core::log::LOGGER.log(core::log::Logger::LogLevel::INFO, "ADC0 raw: %d", adc0.readRaw()); + core::log::LOGGER.log( + core::log::Logger::LogLevel::INFO, "ADC1 : %d mV", static_cast(adc1.read() * 1000)); + core::log::LOGGER.log( + core::log::Logger::LogLevel::INFO, "ADC1: %d%%", static_cast(adc1.readPercentage() * 100)); + core::log::LOGGER.log(core::log::Logger::LogLevel::INFO, "ADC1 raw: %d", adc1.readRaw()); + core::log::LOGGER.log(core::log::Logger::LogLevel::INFO, "--------------------\r\n"); time::wait(500); } } diff --git a/samples/adc/single_adc.cpp b/samples/adc/single_adc.cpp index 72ad3304..10d96fc8 100644 --- a/samples/adc/single_adc.cpp +++ b/samples/adc/single_adc.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include namespace io = core::io; @@ -17,18 +18,24 @@ int main() { io::UART& uart = io::getUART(9600); + // Set up the logger to catch errors in ADC creation + core::log::LOGGER.setUART(&uart); + core::log::LOGGER.setLogLevel(core::log::Logger::LogLevel::INFO); + uart.printf("Starting ADC test\r\n"); time::wait(500); - io::ADC& adc0 = io::getADC(); + io::ADC& adc0 = io::getADC(); while (1) { - uart.printf("--------------------\r\n"); - uart.printf("ADC0 : %d mV\r\n", static_cast(adc0.read() * 1000)); - uart.printf("ADC0: %d%%\r\n", static_cast(adc0.readPercentage() * 100)); - uart.printf("ADC0 raw: %d\r\n\r\n", adc0.readRaw()); - uart.printf("--------------------\r\n\r\n"); + core::log::LOGGER.log(core::log::Logger::LogLevel::INFO, "--------------------"); + core::log::LOGGER.log( + core::log::Logger::LogLevel::INFO, "ADC0 : %d mV", static_cast(adc0.read() * 1000)); + core::log::LOGGER.log( + core::log::Logger::LogLevel::INFO, "ADC0: %d%%", static_cast(adc0.readPercentage() * 100)); + core::log::LOGGER.log(core::log::Logger::LogLevel::INFO, "ADC0 raw: %d", adc0.readRaw()); + core::log::LOGGER.log(core::log::Logger::LogLevel::INFO, "--------------------\r\n"); time::wait(500); } } diff --git a/src/core/io/ADC.cpp b/src/core/io/ADC.cpp index b23dae1a..a58918cf 100644 --- a/src/core/io/ADC.cpp +++ b/src/core/io/ADC.cpp @@ -2,8 +2,6 @@ namespace core::io { -ADC::ADC(Pin pin) { - this->pin = pin; -} +ADC::ADC(Pin pin, ADCPeriph adcPeriph) : pin(pin), adcPeriph(adcPeriph) {} } // namespace core::io diff --git a/src/core/io/platform/f3xx/ADCf3xx.cpp b/src/core/io/platform/f3xx/ADCf3xx.cpp index 266bb696..dbf8d8f4 100644 --- a/src/core/io/platform/f3xx/ADCf3xx.cpp +++ b/src/core/io/platform/f3xx/ADCf3xx.cpp @@ -7,21 +7,19 @@ #include #include #include +#include +namespace core::io { namespace { -/// This is made as a global variable so that it is accessible in the -// interrupt. +/// This is made as a global variable so that it is accessible in the interrupt. DMA_HandleTypeDef* dmaHandle; ADC_HandleTypeDef* adcHandle; -} // namespace - extern "C" void DMA1_Channel1_IRQHandler(void) { HAL_DMA_IRQHandler(dmaHandle); HAL_ADC_IRQHandler(adcHandle); } - -namespace core::io { +} // namespace // Init static member variables ADC_HandleTypeDef ADCf3xx::halADC = {0}; @@ -29,7 +27,7 @@ Pin ADCf3xx::channels[MAX_CHANNELS]; uint16_t ADCf3xx::buffer[MAX_CHANNELS]; DMA_HandleTypeDef ADCf3xx::halDMA = {0}; -ADCf3xx::ADCf3xx(Pin pin) : ADC(pin) { +ADCf3xx::ADCf3xx(Pin pin, ADCPeriph adcPeriph) : ADC(pin, adcPeriph) { // Flag representing if the ADC has been configured yet static bool halADCisInit = false; // "Rank" represents the order in which the channels are added @@ -38,6 +36,7 @@ ADCf3xx::ADCf3xx(Pin pin) : ADC(pin) { // Maximum number of ADC channels have already been added if (rank == MAX_CHANNELS) { + log::LOGGER.log(log::Logger::LogLevel::ERROR, "ADC1 ALREADY HAS MAX NUMBER OF CHANNELS!!"); return; } @@ -69,7 +68,8 @@ float ADCf3xx::read() { } uint32_t ADCf3xx::readRaw() { - // Search through list of channels to determine which DMA buffer index to use + // Search through list of channels to determine which DMA buffer index to + // use uint8_t channelNum = 0; while (channels[channelNum] != pin) channelNum++; @@ -108,9 +108,6 @@ void ADCf3xx::initADC(uint8_t num_channels) { } void ADCf3xx::initDMA() { - // HAL_ADC_Stop(&halADC); - - // TODO: Add some way of selecting the next available DMA channel // Ideally we would have a "DMA" class dedicated to DMA resource allocation. halDMA.Instance = DMA1_Channel1; halDMA.Init.Direction = DMA_PERIPH_TO_MEMORY; @@ -137,60 +134,70 @@ void ADCf3xx::addChannel(uint8_t rank) { GPIOf3xx::gpioStateInit(&gpioInit, myPins, numOfPins, GPIO_MODE_ANALOG, GPIO_NOPULL, GPIO_SPEED_FREQ_HIGH); ADC_ChannelConfTypeDef adcChannel; + Channel_Support channelStruct; switch (pin) { case Pin::PA_0: - adcChannel.Channel = ADC_CHANNEL_1; + channelStruct = {.adc1 = 1, .channel = static_cast(ADC_CHANNEL_1)}; break; case Pin::PA_1: - adcChannel.Channel = ADC_CHANNEL_2; + channelStruct = {.adc1 = 1, .channel = static_cast(ADC_CHANNEL_2)}; break; case Pin::PA_2: - adcChannel.Channel = ADC_CHANNEL_3; + channelStruct = {.adc1 = 1, .channel = static_cast(ADC_CHANNEL_3)}; break; case Pin::PA_3: - adcChannel.Channel = ADC_CHANNEL_4; + channelStruct = {.adc1 = 1, .channel = static_cast(ADC_CHANNEL_4)}; break; case Pin::PA_4: - adcChannel.Channel = ADC_CHANNEL_5; + channelStruct = {.adc1 = 1, .channel = static_cast(ADC_CHANNEL_5)}; break; case Pin::PC_0: - adcChannel.Channel = ADC_CHANNEL_6; + channelStruct = {.adc1 = 1, .channel = static_cast(ADC_CHANNEL_6)}; break; case Pin::PC_1: - adcChannel.Channel = ADC_CHANNEL_7; + channelStruct = {.adc1 = 1, .channel = static_cast(ADC_CHANNEL_7)}; break; case Pin::PC_2: - adcChannel.Channel = ADC_CHANNEL_8; + channelStruct = {.adc1 = 1, .channel = static_cast(ADC_CHANNEL_8)}; break; case Pin::PC_3: - adcChannel.Channel = ADC_CHANNEL_9; + channelStruct = {.adc1 = 1, .channel = static_cast(ADC_CHANNEL_9)}; break; case Pin::PA_6: - adcChannel.Channel = ADC_CHANNEL_10; + channelStruct = {.adc1 = 1, .channel = static_cast(ADC_CHANNEL_10)}; break; case Pin::PB_0: - adcChannel.Channel = ADC_CHANNEL_11; + channelStruct = {.adc1 = 1, .channel = static_cast(ADC_CHANNEL_11)}; break; case Pin::PB_1: - adcChannel.Channel = ADC_CHANNEL_12; + channelStruct = {.adc1 = 1, .channel = static_cast(ADC_CHANNEL_12)}; break; case Pin::PB_13: - adcChannel.Channel = ADC_CHANNEL_13; + channelStruct = {.adc1 = 1, .channel = static_cast(ADC_CHANNEL_13)}; break; case Pin::PB_11: - adcChannel.Channel = ADC_CHANNEL_14; + channelStruct = {.adc1 = 1, .channel = static_cast(ADC_CHANNEL_14)}; break; case Pin::PA_7: - adcChannel.Channel = ADC_CHANNEL_15; + channelStruct = {.adc1 = 1, .channel = static_cast(ADC_CHANNEL_15)}; break; default: + channelStruct = {}; // sets all variables to 0 + log::LOGGER.log(log::Logger::LogLevel::ERROR, "INVALID PIN 0x%x!!", pin); break; // Should never get here } // Subtract 1 because rank starts at 1 channels[rank - 1] = pin; + // This checks if the pin being used supports the ADC being used + if (checkSupport(adcPeriph, channelStruct)) { + adcChannel.Channel = channelStruct.channel; + } else { + log::LOGGER.log(log::Logger::LogLevel::ERROR, "DOES NOT SUPPORT PIN 0x%x!!", pin); + } + adcChannel.Rank = rank; adcChannel.SamplingTime = ADC_SAMPLETIME_601CYCLES_5; adcChannel.Offset = 0; @@ -201,4 +208,12 @@ void ADCf3xx::addChannel(uint8_t rank) { HAL_ADC_ConfigChannel(&halADC, &adcChannel); } +bool ADCf3xx::checkSupport(ADCPeriph periph, Channel_Support channelStruct) { + // In c++, non-zero values (like 1) are true, and 0 is false, so no comparison is needed. + switch (periph) { + case ADCPeriph::ONE: + return channelStruct.adc1; + } +} + } // namespace core::io diff --git a/src/core/io/platform/f4xx/ADCf4xx.cpp b/src/core/io/platform/f4xx/ADCf4xx.cpp index 3ec066ed..fb86f589 100644 --- a/src/core/io/platform/f4xx/ADCf4xx.cpp +++ b/src/core/io/platform/f4xx/ADCf4xx.cpp @@ -1,16 +1,11 @@ -/** - * DISCLAIMER: THIS MIGHT BREAK AT ANY POINT AND/OR NOT WORK FOR CERTAIN PURPOSES - * +/* * This DMA ADC is different than f3xx DMA ADC! - * This DMA ADC does NOT use the interrupts, as when interrupts were enabled it - * would constantly interrupt, not letting the print statements or anything - * else happen. - * Tried to fix this by changing the sampletime and clock prescaler values, but - * nothing worked. - * This was "fixed" by commenting out the NVIC_EnableIRQ, stopping the interrupt - * from ever being enabled. + * f4xx DMA ADC uses a timer to trigger ADC conversions. Timer frequency is 1kHz. * - * For commit from before code was removed, refer to commit 81624521a8b2c4b66480193e88cf32782aaee84d. + * Timers were added to slow down ADC DMA interrupts, as when they ADC DMA is allowed to convert constantly, + * the interrupts stopped the program from doing anything else besides interrupt calls. + * + * WARNING: DOES NOT WORK ON PINS THAT ARE ALREADY IN USE (LIKE UART PINS) */ #include @@ -18,47 +13,85 @@ #include #include #include +#include +#include namespace core::io { +namespace { +/// This is made as a static variable so that it is accessible in the interrupt. +DMA_HandleTypeDef* dmaHandle[3]; +ADC_HandleTypeDef* adcHandle[3]; + +/** + * This function handles DMA2 stream0 global interrupt. (For ADC 1) + */ +extern "C" void DMA2_Stream0_IRQHandler(void) { + HAL_DMA_IRQHandler(dmaHandle[0]); + HAL_ADC_IRQHandler(adcHandle[0]); +} -// Init static member variables -ADC_HandleTypeDef ADCf4xx::halADC = {0}; -Pin ADCf4xx::channels[MAX_CHANNELS]; -uint16_t ADCf4xx::buffer[MAX_CHANNELS]; -DMA_HandleTypeDef ADCf4xx::halDMA = {0}; +/** + * This function handles DMA2 stream2 global interrupt. (For ADC 2) + */ +extern "C" void DMA2_Stream2_IRQHandler(void) { + HAL_DMA_IRQHandler(dmaHandle[1]); + HAL_ADC_IRQHandler(adcHandle[1]); +} + +/** + * This function handles DMA2 stream1 global interrupt. (For ADC 3) + * This is correct, stream 1 for ADC 3. STM is odd and makes things confusing + */ +extern "C" void DMA2_Stream1_IRQHandler(void) { + HAL_DMA_IRQHandler(dmaHandle[2]); + HAL_ADC_IRQHandler(adcHandle[2]); +} +} // namespace -ADCf4xx::ADCf4xx(Pin pin) : ADC(pin) { - // Flag representing if the ADC has been configured yet - static bool halADCisInit = false; +constexpr uint8_t ADC1_SLOT = 0; +constexpr uint8_t ADC2_SLOT = 1; +constexpr uint8_t ADC3_SLOT = 2; - // "Rank" represents the order in which the channels are added - // Also represents the total number of added channels - static uint8_t rank = 1; +ADCf4xx::ADC_State core::io::ADCf4xx::adcArray[NUM_ADCS]; +bool core::io::ADCf4xx::timerInit = false; +TIM_HandleTypeDef core::io::ADCf4xx::htim8; - // Maximum number of ADC channels have already been added - if (rank == MAX_CHANNELS) { +ADCf4xx::ADCf4xx(Pin pin, ADCPeriph adcPeriph) + : ADC(pin, adcPeriph), adcState(ADCf4xx::adcArray[getADCNum(adcPeriph)]), adcNum(getADCNum(adcPeriph)) { + if (adcState.rank == MAX_CHANNELS) { + log::LOGGER.log(log::Logger::LogLevel::ERROR, "ADC %d ALREADY HAS MAX NUMBER OF CHANNELS!!", (adcNum + 1)); return; } - // Initialization of the HAL ADC should only take place once since there is - // only one ADC device which has multiple channels supported. - if (halADCisInit) { - HAL_ADC_Stop_DMA(&halADC); - HAL_DMA_DeInit(&halDMA); + if (ADCf4xx::timerInit) { + HAL_TIM_Base_DeInit(&ADCf4xx::htim8); // Stop Timer8 (Trigger Source For ADC's) + ADCf4xx::timerInit = false; + } + + if (adcState.isADCInit) { + HAL_ADC_Stop_DMA(&adcState.halADC); + HAL_DMA_DeInit(&adcState.halDMA); } else { __HAL_RCC_DMA2_CLK_ENABLE(); - halADCisInit = true; + adcState.isADCInit = true; } - addChannel(rank); - - initADC(rank); - initDMA(); + initADC(adcState.rank); + addChannel(adcState.rank); - HAL_ADC_Start_DMA(&halADC, reinterpret_cast(&buffer[0]), rank); + dmaHandle[adcNum] = &this->ADCf4xx::adcArray[adcNum].halDMA; + adcHandle[adcNum] = &this->ADCf4xx::adcArray[adcNum].halADC; - rank++; + if (!ADCf4xx::timerInit) { + __HAL_RCC_TIM8_CLK_ENABLE(); + initTimer(); + HAL_TIM_Base_Start(&ADCf4xx::htim8); // Start Timer8 (Trigger Source For ADC's) + ADCf4xx::timerInit = true; + } + + HAL_ADC_Start_DMA(&adcState.halADC, reinterpret_cast(&adcState.buffer[0]), adcState.rank); + adcState.rank++; } float ADCf4xx::read() { @@ -67,12 +100,12 @@ float ADCf4xx::read() { } uint32_t ADCf4xx::readRaw() { - // Search through list of channels to determine which DMA buffer index to - // use uint8_t channelNum = 0; - while (channels[channelNum] != pin) + while (adcState.channels[channelNum] != pin) { channelNum++; - return buffer[channelNum]; + } + + return adcState.buffer[channelNum]; } float ADCf4xx::readPercentage() { @@ -84,109 +117,224 @@ void ADCf4xx::initADC(uint8_t num_channels) { /** Configure the global features of the ADC (Clock, Resolution, Data * Alignment and number of conversion) */ - halADC.Instance = ADC1; - halADC.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV2; - halADC.Init.Resolution = ADC_RESOLUTION_12B; - halADC.Init.ScanConvMode = ENABLE; - halADC.Init.ContinuousConvMode = ENABLE; - halADC.Init.DiscontinuousConvMode = DISABLE; - halADC.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE; - halADC.Init.ExternalTrigConv = ADC_SOFTWARE_START; - halADC.Init.DataAlign = ADC_DATAALIGN_RIGHT; - halADC.Init.NbrOfConversion = num_channels; - halADC.Init.DMAContinuousRequests = ENABLE; - halADC.Init.EOCSelection = ADC_EOC_SEQ_CONV; - - __HAL_RCC_ADC1_CLK_ENABLE(); - HAL_ADC_Init(&halADC); + ADC_HandleTypeDef* halADC = &adcState.halADC; + // Set instance to the ADC peripheral being using + halADC->Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV2; + halADC->Init.Resolution = ADC_RESOLUTION_12B; + halADC->Init.ScanConvMode = ENABLE; + halADC->Init.ContinuousConvMode = DISABLE; + halADC->Init.DiscontinuousConvMode = DISABLE; + halADC->Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING; + // Sets conversions to be done when timer 8 sends an Update Trigger + halADC->Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T8_TRGO; + halADC->Init.DataAlign = ADC_DATAALIGN_RIGHT; + halADC->Init.NbrOfConversion = num_channels; + halADC->Init.DMAContinuousRequests = ENABLE; + halADC->Init.EOCSelection = ADC_EOC_SINGLE_CONV; + + switch (adcNum) { + case ADC1_SLOT: + halADC->Instance = ADC1; + __HAL_RCC_ADC1_CLK_ENABLE(); + break; + case ADC2_SLOT: + halADC->Instance = ADC2; + __HAL_RCC_ADC2_CLK_ENABLE(); + break; + case ADC3_SLOT: + halADC->Instance = ADC3; + __HAL_RCC_ADC3_CLK_ENABLE(); + break; + default: + log::LOGGER.log(log::Logger::LogLevel::ERROR, "INVALID ADC NUMBER!!"); + return; // Should never get here + } + HAL_ADC_Init(halADC); } void ADCf4xx::initDMA() { - halDMA.Instance = DMA2_Stream0; - halDMA.Init.Direction = DMA_PERIPH_TO_MEMORY; - halDMA.Init.PeriphInc = DMA_PINC_DISABLE; - halDMA.Init.MemInc = DMA_MINC_ENABLE; - halDMA.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD; - halDMA.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD; - halDMA.Init.Mode = DMA_CIRCULAR; - halDMA.Init.Priority = DMA_PRIORITY_VERY_HIGH; - halDMA.Init.FIFOMode = DMA_FIFOMODE_DISABLE; - - HAL_DMA_Init(&halDMA); - __HAL_LINKDMA(&halADC, DMA_Handle, halDMA); + DMA_HandleTypeDef* dma = &adcState.halDMA; + // Set DMA instance to proper config settings + switch (adcNum) { + case ADC1_SLOT: + dma->Instance = DMA2_Stream0; + dma->Init.Channel = DMA_CHANNEL_0; + break; + case ADC2_SLOT: + dma->Instance = DMA2_Stream2; + dma->Init.Channel = DMA_CHANNEL_1; + break; + case ADC3_SLOT: + dma->Instance = DMA2_Stream1; + dma->Init.Channel = DMA_CHANNEL_2; + break; + default: + log::LOGGER.log(log::Logger::LogLevel::ERROR, "INVALID ADC NUMBER!!"); + return; // Should never get here + } + dma->Init.Direction = DMA_PERIPH_TO_MEMORY; + dma->Init.PeriphInc = DMA_PINC_DISABLE; + dma->Init.MemInc = DMA_MINC_ENABLE; + dma->Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD; + dma->Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD; + dma->Init.Mode = DMA_CIRCULAR; + dma->Init.Priority = DMA_PRIORITY_VERY_HIGH; + dma->Init.FIFOMode = DMA_FIFOMODE_DISABLE; + dma->Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL; + dma->Init.MemBurst = DMA_MBURST_SINGLE; + dma->Init.PeriphBurst = DMA_PBURST_SINGLE; + + HAL_DMA_Init(dma); + + switch (adcNum) { + case ADC1_SLOT: + HAL_NVIC_SetPriority(DMA2_Stream0_IRQn, platform::ADC_INTERRUPT_PRIORITY, 0); + HAL_NVIC_EnableIRQ(DMA2_Stream0_IRQn); + break; + case ADC2_SLOT: + HAL_NVIC_SetPriority(DMA2_Stream2_IRQn, platform::ADC_INTERRUPT_PRIORITY, 0); + HAL_NVIC_EnableIRQ(DMA2_Stream2_IRQn); + break; + case ADC3_SLOT: + HAL_NVIC_SetPriority(DMA2_Stream1_IRQn, platform::ADC_INTERRUPT_PRIORITY, 0); + HAL_NVIC_EnableIRQ(DMA2_Stream1_IRQn); + break; + default: + log::LOGGER.log(log::Logger::LogLevel::ERROR, "INVALID ADC NUMBER!!"); + return; // Should never get here + } + __HAL_LINKDMA(&adcState.halADC, DMA_Handle, *dma); } void ADCf4xx::addChannel(uint8_t rank) { GPIO_InitTypeDef gpioInit; - Pin myPins[] = {pin}; uint8_t numOfPins = 1; + Pin pins[] = {pin}; - GPIOf4xx::gpioStateInit(&gpioInit, myPins, numOfPins, GPIO_MODE_ANALOG, GPIO_NOPULL, GPIO_SPEED_FREQ_HIGH); + GPIOf4xx::gpioStateInit(&gpioInit, pins, numOfPins, GPIO_MODE_ANALOG, GPIO_NOPULL, GPIO_SPEED_FREQ_HIGH); ADC_ChannelConfTypeDef adcChannel; - + Channel_Support channelStruct; + // Combines the ADC channel with the ADC peripherals it supports into a struct, avoiding having multi-layered switch + // statements switch (pin) { case Pin::PA_0: - adcChannel.Channel = ADC_CHANNEL_0; + channelStruct = {.adc1 = 1, .adc2 = 1, .adc3 = 1, .channel = static_cast(ADC_CHANNEL_0)}; break; case Pin::PA_1: - adcChannel.Channel = ADC_CHANNEL_1; + channelStruct = {.adc1 = 1, .adc2 = 1, .adc3 = 1, .channel = static_cast(ADC_CHANNEL_1)}; break; case Pin::PA_2: - adcChannel.Channel = ADC_CHANNEL_2; + channelStruct = {.adc1 = 1, .adc2 = 1, .adc3 = 1, .channel = static_cast(ADC_CHANNEL_2)}; break; case Pin::PA_3: - adcChannel.Channel = ADC_CHANNEL_3; + channelStruct = {.adc1 = 1, .adc2 = 1, .adc3 = 1, .channel = static_cast(ADC_CHANNEL_3)}; break; case Pin::PA_4: - adcChannel.Channel = ADC_CHANNEL_4; + channelStruct = {.adc1 = 1, .adc2 = 1, .adc3 = 0, .channel = static_cast(ADC_CHANNEL_4)}; break; case Pin::PA_5: - adcChannel.Channel = ADC_CHANNEL_5; + channelStruct = {.adc1 = 1, .adc2 = 1, .adc3 = 0, .channel = static_cast(ADC_CHANNEL_5)}; break; case Pin::PA_6: - adcChannel.Channel = ADC_CHANNEL_6; + channelStruct = {.adc1 = 1, .adc2 = 1, .adc3 = 0, .channel = static_cast(ADC_CHANNEL_6)}; break; case Pin::PA_7: - adcChannel.Channel = ADC_CHANNEL_7; + channelStruct = {.adc1 = 1, .adc2 = 1, .adc3 = 0, .channel = static_cast(ADC_CHANNEL_7)}; break; case Pin::PB_0: - adcChannel.Channel = ADC_CHANNEL_8; + channelStruct = {.adc1 = 1, .adc2 = 1, .adc3 = 0, .channel = static_cast(ADC_CHANNEL_8)}; break; case Pin::PB_1: - adcChannel.Channel = ADC_CHANNEL_9; + channelStruct = {.adc1 = 1, .adc2 = 1, .adc3 = 0, .channel = static_cast(ADC_CHANNEL_9)}; break; case Pin::PC_0: - adcChannel.Channel = ADC_CHANNEL_10; + channelStruct = {.adc1 = 1, .adc2 = 1, .adc3 = 1, .channel = static_cast(ADC_CHANNEL_10)}; break; case Pin::PC_1: - adcChannel.Channel = ADC_CHANNEL_11; + channelStruct = {.adc1 = 1, .adc2 = 1, .adc3 = 1, .channel = static_cast(ADC_CHANNEL_11)}; break; case Pin::PC_2: - adcChannel.Channel = ADC_CHANNEL_12; + channelStruct = {.adc1 = 1, .adc2 = 1, .adc3 = 1, .channel = static_cast(ADC_CHANNEL_12)}; break; case Pin::PC_3: - adcChannel.Channel = ADC_CHANNEL_13; + channelStruct = {.adc1 = 1, .adc2 = 1, .adc3 = 1, .channel = static_cast(ADC_CHANNEL_13)}; break; case Pin::PC_4: - adcChannel.Channel = ADC_CHANNEL_14; + channelStruct = {.adc1 = 1, .adc2 = 1, .adc3 = 0, .channel = static_cast(ADC_CHANNEL_14)}; break; case Pin::PC_5: - adcChannel.Channel = ADC_CHANNEL_15; + channelStruct = {.adc1 = 1, .adc2 = 1, .adc3 = 0, .channel = static_cast(ADC_CHANNEL_15)}; break; default: + channelStruct = {}; // sets all values to 0 + log::LOGGER.log(log::Logger::LogLevel::ERROR, "INVALID PIN 0x%x!!", pin); break; // Should never get here } + // This checks if the pin being used supports the ADC being used + if (checkSupport(adcPeriph, channelStruct)) { + adcChannel.Channel = channelStruct.channel; + } else { + log::LOGGER.log(log::Logger::LogLevel::ERROR, "ADC %d DOES NOT SUPPORT PIN 0x%x!!", (adcNum + 1), pin); + } + // Subtract 1 because rank starts at 1 - channels[rank - 1] = pin; + adcState.channels[rank - 1] = pin; adcChannel.Rank = rank; - adcChannel.SamplingTime = ADC_SAMPLETIME_480CYCLES; + adcChannel.SamplingTime = ADC_SAMPLETIME_3CYCLES; adcChannel.Offset = 0; - adcChannel.Offset = 0x000; - HAL_ADC_ConfigChannel(&halADC, &adcChannel); + HAL_ADC_ConfigChannel(&adcState.halADC, &adcChannel); +} + +bool ADCf4xx::checkSupport(ADCPeriph periph, Channel_Support channelStruct) { + // In c++, non-zero values (like 1) are true, and 0 is false, so no comparison is needed. + switch (periph) { + case ADCPeriph::ONE: + return channelStruct.adc1; + case ADCPeriph::TWO: + return channelStruct.adc2; + case ADCPeriph::THREE: + return channelStruct.adc3; + } +} + +uint8_t ADCf4xx::getADCNum(ADCPeriph periph) { + switch (periph) { + case ADCPeriph::ONE: + return ADC1_SLOT; + case ADCPeriph::TWO: + return ADC2_SLOT; + case ADCPeriph::THREE: + return ADC3_SLOT; + } +} + +/* + * This method initializes timer 8 with a Trigger Output of "Update Trigger", with a timer frequency of 1kHz. + * The timer does not specifically tell the exact ADC to do a conversion, it just sends a general Update Trigger every + * cycle. The ADC's are listening for this Update Trigger from timer 8, which is set in the ADC initialization. + */ +void ADCf4xx::initTimer() { + TIM_ClockConfigTypeDef sClockSourceConfig = {0}; + TIM_MasterConfigTypeDef sMasterConfig = {0}; + + ADCf4xx::htim8.Instance = TIM8; + ADCf4xx::htim8.Init.Prescaler = 0; + ADCf4xx::htim8.Init.CounterMode = TIM_COUNTERMODE_UP; + ADCf4xx::htim8.Init.Period = (SystemCoreClock / 1000) - 1; + ADCf4xx::htim8.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; + ADCf4xx::htim8.Init.RepetitionCounter = 0; + ADCf4xx::htim8.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE; + HAL_TIM_Base_Init(&ADCf4xx::htim8); + sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL; + HAL_TIM_ConfigClockSource(&ADCf4xx::htim8, &sClockSourceConfig); + + sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE; + sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE; + HAL_TIMEx_MasterConfigSynchronization(&ADCf4xx::htim8, &sMasterConfig); } } // namespace core::io