#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); }