From df732149ddcbb356fa900da94098b86eb5693ca4 Mon Sep 17 00:00:00 2001 From: DELL4 Date: Wed, 1 Mar 2023 19:41:49 +0100 Subject: [PATCH] initial commit --- .gitignore | 3 + .idea/.gitignore | 8 ++ .idea/misc.xml | 4 + .idea/modules.xml | 8 ++ .idea/untitled.iml | 2 + .idea/vcs.xml | 6 + CMakeLists.txt | 33 ++++++ include/README | 39 +++++++ lib/README | 46 ++++++++ platformio.ini | 20 ++++ src/main.cpp | 176 ++++++++++++++++++++++++++++ test/README | 11 ++ todo/main_dma.cpp | 280 +++++++++++++++++++++++++++++++++++++++++++++ 13 files changed, 636 insertions(+) create mode 100644 .gitignore create mode 100644 .idea/.gitignore create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/untitled.iml create mode 100644 .idea/vcs.xml create mode 100644 CMakeLists.txt create mode 100644 include/README create mode 100644 lib/README create mode 100644 platformio.ini create mode 100644 src/main.cpp create mode 100644 test/README create mode 100644 todo/main_dma.cpp diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3fe18ad --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.pio +CMakeListsPrivate.txt +cmake-build-*/ diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..79b3c94 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..aeb7613 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/untitled.iml b/.idea/untitled.iml new file mode 100644 index 0000000..f08604b --- /dev/null +++ b/.idea/untitled.iml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..83da035 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,33 @@ +# !!! WARNING !!! AUTO-GENERATED FILE, PLEASE DO NOT MODIFY IT AND USE +# https://docs.platformio.org/page/projectconf/section_env_build.html#build-flags +# +# If you need to override existing CMake configuration or add extra, +# please create `CMakeListsUser.txt` in the root of project. +# The `CMakeListsUser.txt` will not be overwritten by PlatformIO. + +cmake_minimum_required(VERSION 3.13) +set(CMAKE_SYSTEM_NAME Generic) +set(CMAKE_C_COMPILER_WORKS 1) +set(CMAKE_CXX_COMPILER_WORKS 1) + +project("untitled" C CXX) + +include(CMakeListsPrivate.txt) + +if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/CMakeListsUser.txt) +include(CMakeListsUser.txt) +endif() + +add_custom_target( + Production ALL + COMMAND platformio -c clion run "$<$>:-e${CMAKE_BUILD_TYPE}>" + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} +) + +add_custom_target( + Debug ALL + COMMAND platformio -c clion debug "$<$>:-e${CMAKE_BUILD_TYPE}>" + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} +) + +add_executable(Z_DUMMY_TARGET ${SRC_LIST} todo/main_dma.cpp) diff --git a/include/README b/include/README new file mode 100644 index 0000000..194dcd4 --- /dev/null +++ b/include/README @@ -0,0 +1,39 @@ + +This directory is intended for project header files. + +A header file is a file containing C declarations and macro definitions +to be shared between several project source files. You request the use of a +header file in your project source file (C, C++, etc) located in `src` folder +by including it, with the C preprocessing directive `#include'. + +```src/main.c + +#include "header.h" + +int main (void) +{ + ... +} +``` + +Including a header file produces the same results as copying the header file +into each source file that needs it. Such copying would be time-consuming +and error-prone. With a header file, the related declarations appear +in only one place. If they need to be changed, they can be changed in one +place, and programs that include the header file will automatically use the +new version when next recompiled. The header file eliminates the labor of +finding and changing all the copies as well as the risk that a failure to +find one copy will result in inconsistencies within a program. + +In C, the usual convention is to give header files names that end with `.h'. +It is most portable to use only letters, digits, dashes, and underscores in +header file names, and at most one dot. + +Read more about using header files in official GCC documentation: + +* Include Syntax +* Include Operation +* Once-Only Headers +* Computed Includes + +https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html diff --git a/lib/README b/lib/README new file mode 100644 index 0000000..6debab1 --- /dev/null +++ b/lib/README @@ -0,0 +1,46 @@ + +This directory is intended for project specific (private) libraries. +PlatformIO will compile them to static libraries and link into executable file. + +The source code of each library should be placed in a an own separate directory +("lib/your_library_name/[here are source files]"). + +For example, see a structure of the following two libraries `Foo` and `Bar`: + +|--lib +| | +| |--Bar +| | |--docs +| | |--examples +| | |--src +| | |- Bar.c +| | |- Bar.h +| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html +| | +| |--Foo +| | |- Foo.c +| | |- Foo.h +| | +| |- README --> THIS FILE +| +|- platformio.ini +|--src + |- main.c + +and a contents of `src/main.c`: +``` +#include +#include + +int main (void) +{ + ... +} + +``` + +PlatformIO Library Dependency Finder will find automatically dependent +libraries scanning project source files. + +More information about PlatformIO Library Dependency Finder +- https://docs.platformio.org/page/librarymanager/ldf.html diff --git a/platformio.ini b/platformio.ini new file mode 100644 index 0000000..e1bfc0a --- /dev/null +++ b/platformio.ini @@ -0,0 +1,20 @@ +; PlatformIO Project Configuration File +; +; Build options: build flags, source filter +; Upload options: custom upload port, speed and extra flags +; Library options: dependencies, extra library storages +; Advanced options: extra scripting +; +; Please visit documentation for the other options and examples +; https://docs.platformio.org/page/projectconf.html + +[env:bluepill_f103c8_128k] +platform = ststm32 +board = bluepill_f103c8_128k +framework = arduino +debug_tool = stlink +upload_protocol = stlink +build_flags = + -ggdb + -D USBCON + -D USBD_USE_CDC diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..5c9a101 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,176 @@ +#include +#include "stm32f1xx_hal_dma.h" + +#define OUTPIN PA8 +#define OUTPIN_N PB13 +#define HVGENPIN PB9 + +HardwareTimer *shockTimer; +uint32_t shockPwmChannel; +uint32_t shockPwmChannel_N; + +HardwareTimer *hvTimer; +uint32_t hvPwmChannel; + +uint32_t frequency = 50; +uint8_t dutyCycle = 50; +uint8_t deadTime = 50; + +uint32_t hvFrequency = 250000; +uint8_t hvDutyCycle = 61; +/** + * output = red, green jumper wire + * input = yellow + * + */ + +void setup(void) { + pinMode(PA0, INPUT_ANALOG); + pinMode(LED_BUILTIN, OUTPUT); + + TIM_BreakDeadTimeConfigTypeDef sBreakDeadTimeConfig = {0}; + + // https://github.com/ahmetonat/STM32F103-ADC-DMA-example/blob/master/Src/stm32f1xx_hal_msp.c + // https://www.stm32duino.com/viewtopic.php?f=41&t=110 + // https://controllerstech.com/dma-with-adc-using-registers-in-stm32/ + // https://stm32duinoforum.com/forum/viewtopic_f_48_t_4399.html + + TIM_TypeDef *shockInstance = (TIM_TypeDef *)pinmap_peripheral(digitalPinToPinName(OUTPIN), PinMap_PWM); + shockPwmChannel = STM_PIN_CHANNEL(pinmap_function(digitalPinToPinName(OUTPIN), PinMap_PWM)); + shockPwmChannel_N = STM_PIN_CHANNEL(pinmap_function(digitalPinToPinName(OUTPIN_N), PinMap_PWM)); + shockTimer = new HardwareTimer(shockInstance); + + TIM_TypeDef *hvInstance = (TIM_TypeDef *)pinmap_peripheral(digitalPinToPinName(HVGENPIN), PinMap_PWM); + hvPwmChannel = STM_PIN_CHANNEL(pinmap_function(digitalPinToPinName(HVGENPIN), PinMap_PWM)); + hvTimer = new HardwareTimer(hvInstance); + // hvTimer->getHandle()->Init.CounterMode = TIM_COUNTERMODE_CENTERALIGNED3; + hvTimer->setPWM(hvPwmChannel, HVGENPIN, hvFrequency, hvDutyCycle); + + Serial.begin(0); + + /*shockTimer->pause(); + shockTimer->setMode(shockPwmChannel, TIMER_OUTPUT_COMPARE_PWM1, OUTPIN); + shockTimer->setOverflow(5, HERTZ_FORMAT); // Hertz + shockTimer->setCaptureCompare(shockPwmChannel, 50, + PERCENT_COMPARE_FORMAT); // 50% Duty Cycle + shockTimer->resume(); */ + + shockTimer->setMode(shockPwmChannel_N, TIMER_OUTPUT_COMPARE_PWM1, OUTPIN_N); + shockTimer->setPWM(shockPwmChannel, OUTPIN, frequency, dutyCycle); + TIM1->CCER |= TIM_CCER_CC1NE; // Enable time r1 complementare output (channel 1) + + sBreakDeadTimeConfig.BreakState = TIM_BREAK_ENABLE; + sBreakDeadTimeConfig.DeadTime = deadTime; + sBreakDeadTimeConfig.BreakPolarity = TIM_BREAKPOLARITY_LOW; + sBreakDeadTimeConfig.LockLevel = TIM_LOCKLEVEL_1; + sBreakDeadTimeConfig.AutomaticOutput = TIM_AUTOMATICOUTPUT_ENABLE; + sBreakDeadTimeConfig.OffStateRunMode = TIM_OSSR_ENABLE; + sBreakDeadTimeConfig.OffStateIDLEMode = TIM_OSSI_ENABLE; + HAL_TIMEx_ConfigBreakDeadTime(shockTimer->getHandle(), &sBreakDeadTimeConfig); + // LL_TIM_OC_SetDeadTime(TIM1, 50); + // shockInstance->BDTR |= 0xFF; + + // with register manipulation, approx 14ns per increment 0 to 128 + // (DTG Bits 7:0) -> DGT Bit 2|5 = 00100100 = 560ns + // bitSet(TIMER1_BASE->BDTR, 2); + // bitSet(TIMER1_BASE->BDTR, 5); + + // shockTimer->setPWM(shockPwmChannel, OUTPIN_N, frequency, dutyCycle); + // shockTimer->setPWM(shockPwmChannel, OUTPIN_N, frequency, dutyCycle); + // setMode(shockPwmChannel, TIMER_, pin); + + /* shockTimer->pause(); + // TIM1->CCER |= 0x555; //3ch compl enable. 0x55 = 2ch compl enable. 0x5 = 1ch compl enable. As seen on RM0008 pages 353 & 354, CCxNE and CCxE bits + TIM1->CCER &= ~TIM_CCER_CC1NP; -- polarity low (or |= to high) + TIM1->CCER |= TIM_OCNPOLARITY_HIGH; + TIM1->CCER |= 0; + TIM1->CCER |= 2; + TIM1->CCER |= TIM_CCER_CC1NE; + TIM1->CR1 |= TIM_CR1_CMS_1; // center waveform + shockTimer->resume(); */ + +} + +void setRegister(uint8_t reg, uint32_t val) { + if (reg == 1) { + frequency = val; + } else if (reg == 2) { + dutyCycle = val; + } else if (reg == 3) { + hvFrequency = val; + } else if (reg == 4) { + hvDutyCycle = val; + } else if (reg == 5) { + deadTime = val; + } + + if (reg == 1 || reg == 2) { + shockTimer->setOverflow(frequency, HERTZ_FORMAT); + shockTimer->setCaptureCompare(shockPwmChannel, dutyCycle, PERCENT_COMPARE_FORMAT); + } else if (reg == 3 || reg == 4) { + hvTimer->setOverflow(hvFrequency, HERTZ_FORMAT); + hvTimer->setCaptureCompare(hvPwmChannel, hvDutyCycle, PERCENT_COMPARE_FORMAT); + } else if (reg == 5) { + LL_TIM_OC_SetDeadTime(TIM1, deadTime); + } + + /* char buffer[256]; + sprintf(buffer, "Setting register: %d to %d\n", reg, val); + Serial.print(buffer); */ +} + +uint32_t getRegister(uint8_t reg) { + if (reg == 1) { + return frequency; + } else if (reg == 2) { + return dutyCycle; + } else if (reg == 3) { + return hvFrequency; + } else if (reg == 4) { + return hvDutyCycle; + } else if (reg == 5) { + return deadTime; + } + + return -1; +} + +void processSerial() { + uint8_t msgType = Serial.read(); + if (msgType == 1) { + // ping + uint8_t ack = 0x69; + Serial.write((uint8_t*) &ack, sizeof(ack)); + } else if (msgType == 2) { + // set register + uint8_t reg = Serial.read(); + uint32_t buf; + Serial.readBytes((char*) &buf, sizeof(buf)); + setRegister(reg, buf); + uint8_t ack = 0x69; + Serial.write((uint8_t*) &ack, sizeof(ack)); + } else if (msgType == 3) { + // get register + uint8_t reg = Serial.read(); + uint32_t val = getRegister(reg); + // Serial.write((uint8_t*) &val, sizeof(val)); + Serial.write((uint8_t*) &val, sizeof(val)); + // Serial.write((uint8_t*) &val, sizeof(val)); + // uint32_t val = getRegister(reg); + } else { + for (int i = 0; i < 5; i++) { + digitalWrite(LED_BUILTIN, HIGH); + delay(100); + digitalWrite(LED_BUILTIN, LOW); + delay(100); + } + } +} + +void loop(void) { + if (Serial.available()) { + processSerial(); + } + + delay(1); +} diff --git a/test/README b/test/README new file mode 100644 index 0000000..9b1e87b --- /dev/null +++ b/test/README @@ -0,0 +1,11 @@ + +This directory is intended for PlatformIO Test Runner and project tests. + +Unit Testing is a software testing method by which individual units of +source code, sets of one or more MCU program modules together with associated +control data, usage procedures, and operating procedures, are tested to +determine whether they are fit for use. Unit testing finds problems early +in the development cycle. + +More information about PlatformIO Unit Testing: +- https://docs.platformio.org/en/latest/advanced/unit-testing/index.html diff --git a/todo/main_dma.cpp b/todo/main_dma.cpp new file mode 100644 index 0000000..8561e0d --- /dev/null +++ b/todo/main_dma.cpp @@ -0,0 +1,280 @@ +#include +#include "stm32f1xx_ll_dma.h" +#include "stm32f1xx_hal_dma.h" +#include "math.h" + +#define OUTPIN PA8 +#define OUTPIN_N PB13 +#define HVGENPIN PB9 + +#define M_PI 3.141526535 + +HardwareTimer *shockTimer; +uint32_t shockPwmChannel; +uint32_t shockPwmChannel_N; + +HardwareTimer *hvTimer; +uint32_t hvPwmChannel; + +uint32_t frequency = 50; +uint8_t dutyCycle = 50; +uint8_t deadTime = 50; +bool readyToSend = false; + + +uint32_t hvFrequency = 250000; +uint8_t hvDutyCycle = 61; +/** + * output = red, green jumper wire + * input = yellow + * + * + * + */ + + +__attribute__((aligned(4))) volatile uint16_t dmaBuf[] = { /*100, 150, 127, 100*/ 1, 1, 200, 200, 1, 1, 100, 100}; +size_t dmaBuf_len = sizeof(dmaBuf) / sizeof(dmaBuf[0]); + +void setup(void) { + pinMode(PA0, INPUT_ANALOG); + pinMode(LED_BUILTIN, OUTPUT); + + TIM_BreakDeadTimeConfigTypeDef sBreakDeadTimeConfig = {0}; + + // https://github.com/ahmetonat/STM32F103-ADC-DMA-example/blob/master/Src/stm32f1xx_hal_msp.c + // https://www.stm32duino.com/viewtopic.php?f=41&t=110 + // https://controllerstech.com/dma-with-adc-using-registers-in-stm32/ + // https://stm32duinoforum.com/forum/viewtopic_f_48_t_4399.html + + // DMA: + // https://controllerstech.com/pwm-with-dma-in-stm32/ -- good + // https://deepbluembedded.com/stm32-change-pwm-duty-cycle-with-dma-for-sine-wave-generation/ + // https://electronics.stackexchange.com/questions/652738/stm32-pwm-and-output-compare + // https://community.st.com/s/question/0D53W000007zeyoSAA/pwm-output-synchronization-problem + + // LL_TIM_OC_SetDeadTime + + + TIM_TypeDef *shockInstance = (TIM_TypeDef *)pinmap_peripheral(digitalPinToPinName(OUTPIN), PinMap_PWM); + shockPwmChannel = STM_PIN_CHANNEL(pinmap_function(digitalPinToPinName(OUTPIN), PinMap_PWM)); + shockPwmChannel_N = STM_PIN_CHANNEL(pinmap_function(digitalPinToPinName(OUTPIN_N), PinMap_PWM)); + shockTimer = new HardwareTimer(shockInstance); + + /* TIM_TypeDef *hvInstance = (TIM_TypeDef *)pinmap_peripheral(digitalPinToPinName(HVGENPIN), PinMap_PWM); + hvPwmChannel = STM_PIN_CHANNEL(pinmap_function(digitalPinToPinName(HVGENPIN), PinMap_PWM)); + hvTimer = new HardwareTimer(hvInstance); */ + + // hvTimer->handle + // _timerObj.handle.Init + // hvTimer->getHandle()->Init.CounterMode = TIM_COUNTERMODE_CENTERALIGNED3; + + Serial.begin(0); + + for (size_t i = 0; i < dmaBuf_len; i++) { + // dmaBuf[i] = sin(2 * M_PI * i / dmaBuf_len); + } + + /*shockTimer->pause(); + shockTimer->setMode(shockPwmChannel, TIMER_OUTPUT_COMPARE_PWM1, OUTPIN); + shockTimer->setOverflow(5, HERTZ_FORMAT); // Hertz + shockTimer->setCaptureCompare(shockPwmChannel, 50, + PERCENT_COMPARE_FORMAT); // 50% Duty Cycle + shockTimer->resume(); */ + + shockTimer->setMode(shockPwmChannel_N, TIMER_OUTPUT_COMPARE_PWM1, OUTPIN_N); + shockTimer->setPWM(shockPwmChannel, OUTPIN, frequency, dutyCycle); + TIM1->CCER |= TIM_CCER_CC1NE; + + + sBreakDeadTimeConfig.BreakState = TIM_BREAK_ENABLE; + sBreakDeadTimeConfig.DeadTime = deadTime; + sBreakDeadTimeConfig.BreakPolarity = TIM_BREAKPOLARITY_LOW; + sBreakDeadTimeConfig.LockLevel = TIM_LOCKLEVEL_1; + sBreakDeadTimeConfig.AutomaticOutput = TIM_AUTOMATICOUTPUT_ENABLE; + sBreakDeadTimeConfig.OffStateRunMode = TIM_OSSR_ENABLE; + sBreakDeadTimeConfig.OffStateIDLEMode = TIM_OSSI_ENABLE; + HAL_TIMEx_ConfigBreakDeadTime(shockTimer->getHandle(), &sBreakDeadTimeConfig); + // LL_TIM_OC_SetDeadTime(TIM1, 50); + // shockInstance->BDTR |= 0xFF; + + + //Inserts Dead Time approx 14ns per increment 0 to 128 + //(DTG Bits 7:0) + //DGT Bit 2&5 = 00100100 = 560ns + // bitSet(TIMER1_BASE->BDTR,2); + // bitSet(TIMER1_BASE->BDTR,5); + + //shockTimer->setPWM(shockPwmChannel, OUTPIN_N, frequency, dutyCycle); + // shockTimer->setPWM(shockPwmChannel, OUTPIN_N, frequency, dutyCycle); + // setMode(shockPwmChannel, TIMER_, pin); + + // hvTimer->setPWM(hvPwmChannel, HVGENPIN, hvFrequency, hvDutyCycle); + + + // delay(3000); + + /* shockTimer->pause(); + // TIM1->CCER |= 0x555; //3ch compl enable. 0x55 = 2ch compl enable. 0x5 = 1ch compl enable. As seen on RM0008 pages 353 & 354, CCxNE and CCxE bits + TIM1->CCER &= ~TIM_CCER_CC1NP; -- polarity low (or |= to high) + TIM1->CCER |= TIM_OCNPOLARITY_HIGH; + TIM1->CCER |= 0; + TIM1->CCER |= 2; + TIM1->CCER |= TIM_CCER_CC1NE; + TIM1->CR1 |= TIM_CR1_CMS_1; // center waveform + // shockTimer->setPWM(shockPwmChannel, OUTPIN, frequency, dutyCycle); + shockTimer->resume(); */ + + // auto timerMax = (__HAL_TIM_GET_AUTORELOAD(&(shockTimer->getHandle())) + 1); + + /* for (uint8_t i = 0; i < 200; i++) { + angle = ASR*(float)i; + IV[i] = (uint16_t) rint(100 + 99*sinf(angle*(PI/180))); + } */ + + DMA_HandleTypeDef hdma_tim2_ch1; + hdma_tim2_ch1.Instance = DMA1_Channel5; // Table 78 + hdma_tim2_ch1.Init.Direction = DMA_MEMORY_TO_PERIPH; + hdma_tim2_ch1.Init.PeriphInc = DMA_PINC_DISABLE; + hdma_tim2_ch1.Init.MemInc = DMA_MINC_ENABLE; + // on an STM32F407 Tim1 is 16bit so DMA transfer needs to be half word, eg Tim2 is 32bit so use word + hdma_tim2_ch1.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD; + hdma_tim2_ch1.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD; + hdma_tim2_ch1.Init.Mode = DMA_CIRCULAR; + hdma_tim2_ch1.Init.Priority = DMA_PRIORITY_HIGH; + + // HAL_TIM_PWM_DeInit(shockTimer->getHandle()); + + if (HAL_DMA_Init(&hdma_tim2_ch1) != HAL_OK) { + // Error_Handler(); + for (int i = 0; i < 5; i++) { + digitalWrite(LED_BUILTIN, HIGH); + delay(100); + digitalWrite(LED_BUILTIN, LOW); + delay(100); + } + } + + // __HAL_RCC_DMA1_CLK_ENABLE(); + + // __HAL_LINKDMA(shockTimer->getHandle(), hdma[TIM_DMA_ID_CC1], hdma_tim2_ch1); // TIM_DMA_ID_CC1 -> shockPwmChannel ??? + + __HAL_LINKDMA(shockTimer->getHandle(), hdma[TIM_DMA_ID_CC4], hdma_tim2_ch1); // TIM_DMA_ID_CC1 -> shockPwmChannel ??? + __HAL_LINKDMA(shockTimer->getHandle(), hdma[TIM_DMA_ID_TRIGGER], hdma_tim2_ch1); + + // page 358 Mastering STM32 + + // If anyone else is forced by their pinout to use one of the complementary PWM outputs (e.g. CH3N) + // then be sure to change the call to HAL_TIM_PWM_Start_DMA() for HAL_TIMEx_PWMN_Start_DMA() + // HAL_TIM_PWM_Start_DMA(shockTimer->getHandle(), TIM_CHANNEL_1, (uint32_t*) &dmaBuf, dmaBuf_len); + HAL_TIM_PWM_Start_DMA(shockTimer->getHandle(), TIM_CHANNEL_1, (uint32_t*) &dmaBuf[0], dmaBuf_len); + + + /* LL_DMA_InitTypeDef DMA_InitStruct; + DMA_InitStruct.Channel = LL_DMA_CHANNEL_4; + DMA_InitStruct.Direction = LL_DMA_DIRECTION_PERIPH_TO_MEMORY; + DMA_InitStruct.MemoryOrM2MDstAddress = (uint32_t)DMA_RX_Buffer; + DMA_InitStruct.NbData = DMA_RX_BUFFER_SIZE; + DMA_InitStruct.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; + DMA_InitStruct.PeriphOrM2MSrcAddress = (uint32_t)&TIM1->DR; + LL_DMA_Init(DMA1, LL_DMA_STREAM_5, &DMA_InitStruct); */ + + + analogReadResolution(12); + + +} + +void setShockFrequency(uint32_t freq) { + +} + +void setRegister(uint8_t reg, uint32_t val) { + if (reg == 1) { + frequency = val; + } else if (reg == 2) { + dutyCycle = val; + } else if (reg == 3) { + hvFrequency = val; + } else if (reg == 4) { + hvDutyCycle = val; + } else if (reg == 5) { + deadTime = val; + } + + if (reg == 1 || reg == 2) { + shockTimer->setOverflow(frequency, HERTZ_FORMAT); + shockTimer->setCaptureCompare(shockPwmChannel, dutyCycle, PERCENT_COMPARE_FORMAT); + } else if (reg == 3 || reg == 4) { + hvTimer->setOverflow(hvFrequency, HERTZ_FORMAT); + hvTimer->setCaptureCompare(hvPwmChannel, hvDutyCycle, PERCENT_COMPARE_FORMAT); + } else if (reg == 5) { + LL_TIM_OC_SetDeadTime(TIM1, deadTime); + } + + /* char buffer[256]; + sprintf(buffer, "Setting register: %d to %d\n", reg, val); + Serial.print(buffer); */ +} + +uint32_t getRegister(uint8_t reg) { + if (reg == 1) { + return frequency; + } else if (reg == 2) { + return dutyCycle; + } else if (reg == 3) { + return hvFrequency; + } else if (reg == 4) { + return hvDutyCycle; + } else if (reg == 5) { + return deadTime; + } +} + +void processSerial() { + uint8_t msgType = Serial.read(); + if (msgType == 1) { + // ping + uint8_t ack = 0x69; + Serial.write((uint8_t*) &ack, sizeof(ack)); + } else if (msgType == 2) { + // set register + uint8_t reg = Serial.read(); + uint32_t buf; + Serial.readBytes((char*) &buf, sizeof(buf)); + setRegister(reg, buf); + uint8_t ack = 0x69; + Serial.write((uint8_t*) &ack, sizeof(ack)); + } else if (msgType == 3) { + // get register + uint8_t reg = Serial.read(); + uint32_t val = getRegister(reg); + // Serial.write((uint8_t*) &val, sizeof(val)); + Serial.write((uint8_t*) &val, sizeof(val)); + // Serial.write((uint8_t*) &val, sizeof(val)); + // uint32_t val = getRegister(reg); + } else { + for (int i = 0; i < 5; i++) { + digitalWrite(LED_BUILTIN, HIGH); + delay(100); + digitalWrite(LED_BUILTIN, LOW); + delay(100); + } + } +} + +void sendMsg(uint8_t type) { + +} + +void loop(void) { + if (Serial.available()) { + processSerial(); + } + + uint16_t v = (uint16_t) analogRead(PA0); + // Serial.write((uint8_t*) &v, sizeof(v)); + /* Serial.print(v); + Serial.println(); */ + delay(1); +}