PWM and OneShot on TIM2 and TIM3"SOLVED"

Post here first, or if you can't find a relevant section!
altEnergy
Posts: 10
Joined: Sat Sep 25, 2021 1:02 pm

PWM and OneShot on TIM2 and TIM3"SOLVED"

Post by altEnergy »

Hi, I have had to re-register. I did have the same user name in the old archived forum, but this is my first time in the new forum everything looks the same and it is great to see the forum is back! So I have copied some code and written some code to output a user defined frequency and pulse width.
This will be driving some ir2110 mosfet drivers that will be switching coils via irfz44 mosfets, but I need some extra code to give one shot pulses to short some output coils at specific adjustable delays and on times, during the pwm pulse. I used the PWM example found in github, to setup my PWM. I am having to use registers for my one shot!(because I can not find api code to do it), and I am soo.. soooo.. unsure what I am doing there. I have looked everywhere including a post by roger clark and Mat03's code etc steve strong etc nick gammon etc but can not find a lot of support really for arduino ide I will attach my code so far, which works for pwm and allows me to set freq and duty on screen, but if you see the new function above void setup() in my code, that is where I am struggling setting up my one shot. There is a call to this function at the bottom of setup. The things I am not sure of is how to make my prescaller the same on both timers and make the overflow no more than timer 2's overflow, .... well sorry, thinking about it, I should beable to sort that, but will timer 2 and 1 be running both at 16 bits? and do I still need to make an instance like in timer 2? or is that just for the api on timer 2? and do I need to setup a hardware Timer like "HardwareTimer *oneshot_Tim1" for example? as I say all the other code works but the function before setup is my struggle. Here is my code I have tried to tidy it up a bit and remove most of un-needed bits

edit -- Just remembered I am using very recent STM32 official core and a week old arduino nightly build sorry
edit 2 -- am using bluepill stm32f103 I had to rush as my wife called half way through the post asking me to pick her up from shops

I think my main question is will the one shot just run when all the references in the "void Tim1Setup()" are complete as in "1.a) to 1.d) and
2.a) to 2.d) also 3." when void Tim1Setup() is called?, if so I can probably sort it myself but have already wasted much time trying to figure out
what I am doing! Thanks.
another Edit .. -- I guess if my brain is functioning , which it thinks it is .. It would say to me why do you need anything else if you are writing directly to the registers .. its all there Doh! ,, , but I don't always believe it ... its been known to fail before now!

Anyway hello to David Prentice who helped me extensivly with two specials Back in the old stm32duino forum and all the other good folk out there!

and ya'll restore my faith More by getting this back up and running.

Code: Select all

  
  #define BLACK 0x000                         // Define the display colours we'll be using
  #define BLUE 0x001F                         // so they're constants regardless of which
  #define GREEN 0x07E0                        // display library we use.
  #define YELLOW 0xFFE0
  #define GREY 0x632C
  #define RED  0xFF
  #define L_BROWN 0xC54F
  #define PEACH 0xF54F
  #define TURQUOISE 0x2D53
  #define TEAL 4C11
  #define TFT_CS        PA15
  #define TFT_RST       -1 // Or set to -1 and connect to Arduino RESET pin
  #define TFT_DC        PA8  


#define PIN_A   PB9          
#define PIN_B   PB8   

#include <Button.h>
      
#define adjSel PB12
#define M_Button  PA10 
#define L_Button PB14
#define R_Button PB13
  
  
  #define OscMenu PB15

  
  
  #include <Adafruit_ST7735.h>            // Hardware-specific library
   //Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_MOSI, TFT_SCLK, TFT_RST);
   Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_RST);


   
                //Timer2 Pulse Defines includes and variables
                

#define PulseApin  PA0

//other output is on PA1 called from two timmer isr's 


#include <Button.h>

uint16_t freq = 5;

//uint16_t freqB = 5;

uint8_t dutyA = 4;

//uint8_t dutyB = 5;

//const char* adjustArray[3] = {" Nothing", " Freq   ", " DutyA  ", };
const char* multiplier[3] = {" ONE ", " TEN  ", " 100 ", };
   //int active = 0;
   int Inc = 0;
   int enc = 0;
   //int activeold;
   //int mode = 0;
   
   HardwareTimer *pwm_Tim2;
   //HardwareTimer *MyTimB;
   
  //String adjusting = adjustArray[active];
  String mult = multiplier[Inc];

  byte dir = 0;
 
 volatile uint32_t channelA = STM_PIN_CHANNEL(pinmap_function(digitalPinToPinName(PulseApin), PinMap_PWM));
 volatile uint32_t channelB = STM_PIN_CHANNEL(pinmap_function(digitalPinToPinName(PulseBpin), PinMap_PWM));
 volatile uint32_t channel2 = STM_PIN_CHANNEL(pinmap_function(digitalPinToPinName(PulseApin), PinMap_PWM));
 volatile uint32_t channel3 = STM_PIN_CHANNEL(pinmap_function(digitalPinToPinName(PulseBpin), PinMap_PWM));
   //uint8_t pwmPin = PA1;

   // not currently used
   //uint32_t  PresScaler = pwm_Tim2-> getPrescaleFactor();
volatile   uint32_t  Overflow = pwm_Tim2-> getOverflow();
volatile   uint32_t  CCR = pwm_Tim2-> getCaptureCompare(channelA);
   //uint32_t  Count = pwm_Tim2-> getCount();   

 
volatile byte aFlag = 0; // let's us know when we're expecting a rising edge on pinA to signal that the encoder has arrived at a detent
volatile byte bFlag = 0; // let's us know when we're expecting a rising edge on pinB to signal that the encoder has arrived at a detent (opposite direction to when aFlag is set)
volatile int encoderPos = 0; //this variable stores our current value of encoder position. Change to int or uin16_t instead of byte if you want to record a larger range than 0-255
volatile int oldEncPos = 0; //stores the last encoder position value so we can compare to the current reading and see if it has changed (so we know when to print to the serial monitor)
volatile byte reading = 0; //somewhere to store the direct values we read from our interrupt pins before checking to see if we have moved a whole detent




//Button settings


Button M_But(M_Button);
Button L_But(L_Button);
Button R_But(R_Button);
Button AdjBut(adjSel);


                         //Timer  ISR FUNCTIONS
                   
 
void ch2_comp_high_interrupt(void)
{ 
  GPIOA->BSRR = 0b0000000000000010 <<16;//This puts pin PA1 HIGH
}


void ch3_comp_low_interrupt(void)
{ 
  GPIOA->BSRR = 0b0000000000000010;//This puts pin PA1 LOW
}


//                                                                   all other Functions
void setIncr()
{
  if(Inc<2)Inc++;
  else Inc=0;
  mult = multiplier[Inc];
}

void adjustFreq()
{

  pwm_Tim2->pause();
  freq=enc;
  if(freq<0)freq=0; 
 
 
  uint32_t channelA = STM_PIN_CHANNEL(pinmap_function(digitalPinToPinName(PulseApin), PinMap_PWM));
  
  pwm_Tim2->setPWM(channelA, PulseApin, freq, dutyA);
  pwm_Tim2->refresh();
  pwm_Tim2->resume();
  pwm_Tim2->pause();
  uint32_t  Overflow = pwm_Tim2-> getOverflow();
  uint32_t  CCR = pwm_Tim2-> getCaptureCompare(channelA);
  uint32_t channel2 = STM_PIN_CHANNEL(pinmap_function(digitalPinToPinName(PulseApin), PinMap_PWM));

  pwm_Tim2->setCaptureCompare(2, (Overflow/2) + CCR, TICK_COMPARE_FORMAT);
  uint32_t channel3 = STM_PIN_CHANNEL(pinmap_function(digitalPinToPinName(PulseBpin), PinMap_PWM));
  pwm_Tim2->setCaptureCompare(3, 50, PERCENT_COMPARE_FORMAT);
  
  pwm_Tim2->refresh();
  pwm_Tim2->resume();
   
  displayFreqPulse();
}
 
void adjustDutyA()
{
  
  

  
  pwm_Tim2->pause();
  dutyA=enc;
  if(dutyA<0){dutyA=0;}
 
  uint32_t channelA = STM_PIN_CHANNEL(pinmap_function(digitalPinToPinName(PulseApin), PinMap_PWM));

  
  pwm_Tim2->setPWM(channelA, PulseApin, freq, dutyA);
  pwm_Tim2->refresh();
  pwm_Tim2->resume();
  pwm_Tim2->pause();
  uint32_t  Overflow = pwm_Tim2-> getOverflow();
  uint32_t  CCR = pwm_Tim2-> getCaptureCompare(channelA);

  pwm_Tim2->setCaptureCompare(2, (Overflow/2) + CCR, TICK_COMPARE_FORMAT);
  pwm_Tim2->setCaptureCompare(3, 50, PERCENT_COMPARE_FORMAT);
  

 
  pwm_Tim2->refresh();
  pwm_Tim2->resume();
  
  displayFreqPulse();
}



                             
//ENCODER FUNCTIONS
void PIN_A_ISR(){
  uint32_t prim;
    
      prim = __get_PRIMASK();

    
      __disable_irq();//stop interrupts happening before we read pin values
      
    //reading = PIND & 0xC; // read all eight pin values then strip away all but pinA and pinB's values
    //if(reading == B00001100 && aFlag) { //check that we have both pins at detent (HIGH) and that we are expecting detent on this pin's rising edge
    
    //we need to replace above two lines with direct port manipulation for stm32f1 official stm32 core
    reading = (GPIOB->IDR & 0x300) >> 6;// in previous version I forgot to put the first part in brackets meaning
                                        // it was shifting 0x300 >> 6 then anding it with GPIO->IDR instead of shifting the result of
                                        // GPIOB->IDR then anding it with 0x300
    if (reading == B00001100 && aFlag)//check that we have both pins at detent (HIGH) and that we are expecting detent on this pin's rising edge
    {

    dir=0;
    encoderPos --; //decrement the encoder's position count
    //any other code or function that needs to be done on read of encoder
     
    
    bFlag = 0; //reset flags for the next turn
    aFlag = 0; //reset flags for the next turn
    }
  else if (reading == B00001000) bFlag = 1; //signal that we're expecting pinB to signal the transition to detent from free rotation
  
  if (!prim) 
      {
        __enable_irq();//restart interrupts
      }
      attachInterrupt(PIN_A,PIN_A_ISR,RISING);

}

void PIN_B_ISR(){

      uint32_t prim;
    
      prim = __get_PRIMASK();

    
      __disable_irq();//stop interrupts happening before we read pin values
  //reading = PIND & 0xC; //read all eight pin values then strip away all but pinA and pinB's values
  //if (reading == B00001100 && bFlag) { //check that we have both pins at detent (HIGH) and that we are expecting detent on this pin's rising edge
    
    //we need to replace above two lines with direct port manipulation for stm32f1 official stm32 core
    reading = (GPIOB->IDR & 0x300) >> 6;// in previous version I forgot to put the first part in brackets meaning
                                        // it was shifting 0x300 >> 6 then anding it with GPIO->IDR instead of shifting the result of
                                        // GPIOB->IDR then anding it with 0x300
    if (reading == B00001100 && bFlag)//check that we have both pins at detent (HIGH) and that we are expecting detent on this pin's rising edge
    {
    dir=1;
    encoderPos ++;//increment the encoder's position count
    //any other code or function that needs to be done on read of encoder
    
     
    bFlag = 0; //reset flags for the next turn
    aFlag = 0; //reset flags for the next turn
   }
   else if (reading == B00000100) aFlag = 1; //signal that we're expecting pinA to signal the transition to detent from free rotation


   if (!prim) 
      {
        __enable_irq();//restart interrupts
      }
      
    attachInterrupt(PIN_B,PIN_B_ISR,RISING);
}


                                            // End of rotary functions

void displayFreqPulse()
{
   //tft.fillScreen(ST7735_BLACK);

   tft.setCursor(11, 12);
   tft.setTextSize(1);
   tft.drawFastHLine(5, 10, tft.width()-10, ST7735_ORANGE);
   tft.drawFastHLine(5, tft.height() - 30, tft.width()-10, ST7735_ORANGE);
   tft.drawFastVLine(5, 10, 88, ST7735_ORANGE);
   tft.drawFastVLine(155, 10, 88, ST7735_ORANGE);
   //if(active==1){tft.setTextColor(YELLOW,ST7735_BLUE);}
   //else{
   tft.setTextColor(YELLOW,ST7735_BLACK);
  
   tft.print("Frequency =   ");
   tft.setCursor(78, 12);
   tft.print("     ");
   tft.setCursor(78, 12);
   tft.print(freq);
   
   //if(active==2){tft.setTextColor(YELLOW,ST7735_BLUE);}
   //else{
   tft.setTextColor(TURQUOISE,ST7735_BLACK);
   tft.setCursor(102, 12);
   tft.print(Inc);
   
   tft.setCursor(11, 24);
   tft.setTextColor(YELLOW,ST7735_BLACK);
   tft.print("DutyA = ");
   tft.setCursor(51, 24);
   tft.println("     ");
   tft.setCursor(51, 24);
   tft.println(dutyA);
   
   tft.setTextColor(ST7735_WHITE,ST7735_BLACK);
   tft.setCursor(97, 24);
   //here we will print the multiplier       
   
   tft.print("X = ");
   tft.setCursor(102,24);
   tft.print("   ");
   tft.setCursor(102,24);
   tft.println(mult);
  
   tft.setTextColor(YELLOW,ST7735_BLACK);

                            //hopefully done "The code below does not erase the higher digits when made smaller" 
                            //by using +" " instead of the commented out line below that

   //                       Two things when the frequency is increased 10 gets added to the duty output on the
   //                       interrupt pin a button to set increments to 10 or 100 also should be good
   //                       check for screen space!
   //                                                   The Above has all been sorted
   
   tft.setTextSize(1);
   tft.setCursor(11, 36);
   //tft.setTextColor(YELLOW,ST7735_BLACK);
   tft.print("value to set ");
   tft.setTextColor(ST7735_MAGENTA,ST7735_BLACK); 
   tft.setCursor(87, 36); tft.print("       "); 
   tft.setCursor(87, 36); tft.println(enc);
   //;tft.println("        ");
   
     
 
   
   //uint32_t  PresScaler = pwm_Tim2-> getPrescaleFactor();
   
   //uint32_t  Count = pwm_Tim2-> getCount();
   tft.setTextColor(ST7735_ORANGE,ST7735_BLACK);
   tft.setCursor(11, 47);
   tft.print("press left button");
   tft.setCursor(11, 56);
   tft.print("to set frequency");
   //tft.setCursor(11, 68);
   //tft.print("press right button");
   tft.setCursor(10, 65);
   tft.print("right for +ve Duty Cycle");
   tft.setCursor(11, 75);
   //tft.print("PreScaler = "); tft.println(PresScaler);
   tft.setTextColor(ST7735_GREEN,ST7735_BLACK);
   
   uint32_t  Overflow = pwm_Tim2-> getOverflow();
   uint32_t  CCR = pwm_Tim2-> getCaptureCompare(channelA);
   tft.print("Overflow = ");
   tft.setCursor(72, 75);
   tft.println("       ");
   tft.setCursor(72, 75);
   tft.println(Overflow);
   
   
   tft.setCursor(11, 85);
   tft.print("CCR = ");
   tft.setCursor(50, 85);
   tft.println("     ");
   tft.setCursor(50, 85);
   tft.println(CCR);
   //tft.println("  ");
   //tft.print("Count = ");  tft.println(Count);
   
  
}

                       // so this is where my problems are I refferenced the manual for my own settup referances
void Tim1Setup()
{
        //                              Setting Up Timer 1 for "one shot mode"(OPM)

              

               //---------------Input will be channel 1 Output will be channel 2
        //1.a)       Select the TIxFPx trigger to be used by writing CCxS bits (B000000000000011) in CCMRx register
        //2.b)       Select the output compare mode by writing OCyM bits (110000000000000)PWM1 mode  in CCMRy register (PWM1 
        //           or PWM2 mode)
        TIM1->CCMR1 &= 0b11000000000011; //TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1PE | TIM_CCMR1_CC2S_0;

        //1.b)       Select the polarity of the input pin by writing CCxP and CCxNP bits(10 & 1000) in CCER
        //           register
        TIM1->CCER  &= 0xA;
        
        //1.c)        Configure the TIxFPx trigger for the slave mode trigger by writing TS bits (1110000) in SMCR 
        //            register
        //1.d)        Select the trigger mode for the slave mode by writing SMS = (110) in SMCR 
        //            register.
        TIM1->SMCR &= 0x76;  //TIM_SMCR_TS_1 | TIM_SMCR_TS_2 | TIM_SMCR_SMS_3;
                                                                                                                                  // I think I have everything as I want down to here
        
        // CCER: CC1P=0 (OC1 active high), CC1E=1 (OC1 output activated),
        //       CC2NP=0 and CC2P=1 (channel 2 input is falling-edge sensitive)
        
        //2.a)       Select the output polarity by writing CCyP bit in CCER register

        //     check this is correct       
        TIM1->CCER &= 0x20; //TIM_CCER_CC1E | TIM_CCER_CC2P;

        //                           Find out the meanning of CCMRy vs CCCMRx..DONE  y is output channel 2 and x is input channel 1.
        //2.b)       Select the output compare mode by writing OCyM bits in CCMRy register (PWM1 
        //           or PWM2 mode)
        
                                    //   BELOW AND ABOVE ARE DONE USING CCMR1 above OC2M for output channel 2
        //TIM1->CCMR2 &= 110000000000000;//OC2M (pwm1)
      
        
        //2.c)       Set the delay value by writing in CCRy register
        //                                     need to figure what CCR to use ah ok
        //                                     It will be CCR2 which is Channel 2 there are
        //                                     4 ccr registers! one for each channel

        // channel 2 output this will set the delay which needs to be a variable 
        // programmed on screen but preset in variables as volatile int
        
        //TIM1->CCR2 &= 

        //2.d)       Set the auto reload value to have the desired pulse: pulse = TIMy_ARR - 
        //           TIMy_CCRy


        //3.         Select the one pulse mode by setting the OPM bit in CR1 register, if only one pulse is to 
        //           be generated. Otherwise this bit should be reset:
        //           Delay = CCRy/(TIMx_CLK/(PSC + 1))
        //           Pulse-Length= (ARR+1-CCRy)/(TIMx_CLK/(PSC+1)).
        //                For more details on using the timer in this mode refer to the examples provided in the 
        //                STM32Cube package in the Examples\TIM\TIM_OnePulse sub folder



                     //End of Timer1 setup for one shot
}


void setup() {

   pinMode(PIN_A, INPUT_PULLUP);
   pinMode(PIN_B, INPUT_PULLUP);
   pinMode(adjSel, INPUT_PULLUP);
   pinMode(M_Button, INPUT_PULLUP);
   pinMode(L_Button, INPUT_PULLUP);
   pinMode(R_Button, INPUT_PULLUP);
   pinMode(OscMenu, INPUT_PULLUP);
   pinMode(TFT_CS,OUTPUT);
   pinMode(TFT_DC,OUTPUT);
   pinMode(PA1,OUTPUT);

     
   SPI.setMOSI(PB5);
   //SPI.setMISO(PB14);
   SPI.setSCLK(PB3);
   SPI.begin();
   SPI.setDataMode(SPI_MODE0);
   SPI.setClockDivider(SPI_CLOCK_DIV16);
   delay(100);
   //_colstart = 2;
   //_rowstart = 1;
   tft.initR(INITR_BLACKTAB);// I get a line down the right side but I get worse problems if using theGREENTAB so will settle with this
   tft.setRotation(1);
     
   tft.fillScreen(ST7735_BLACK);

   tft.setCursor(15, tft.height() -20);
   tft.setTextSize(1);
   tft.drawFastHLine(0, tft.height() - 23, tft.width()-10, ST7735_BLUE);
   tft.setTextColor(BLUE);
   tft.println(" STM32F103  0-1kHZ+");
   tft.print(" PWM Generator");

   delay(1000);

              
    
                                                         // changed from RISING to FALLING  as pull up resistors keep pins high this may require 
                                                         // 1100 to be changed to 0000 and a few other changes on that part of the code
                                                         // ignore the above statements I screwed up somewhere but it is sorted now using RISING
                                                         
      attachInterrupt(PIN_A,PIN_A_ISR,RISING); // set an interrupt on PinA, looking for a rising edge signal and executing the "PinA" Interrupt Service Routine (below)
      attachInterrupt(PIN_B,PIN_B_ISR,RISING); // set an interrupt on PinB, looking for a rising edge signal and executing the "PinB" Interrupt Service Routine (below)
      //attachInterrupt(adjSel,adjSel_ISR,FALLING);
      //attachInterrupt(L_Button,L_Button_ISR,FALLING);                              
                                           
                                           //setup for timer pulses

      TIM_TypeDef *Instance0 = (TIM_TypeDef *)pinmap_peripheral(digitalPinToPinName(PulseApin), PinMap_PWM);
      //TIM_TypeDef *Instance1 = (TIM_TypeDef *)pinmap_peripheral(digitalPinToPinName(PulseBpin), PinMap_PWM); not used
      TIM_TypeDef *Instance2 = (TIM_TypeDef *)pinmap_peripheral(digitalPinToPinName(PulseBpin), PinMap_PWM);
      TIM_TypeDef *Instance3 = (TIM_TypeDef *)pinmap_peripheral(digitalPinToPinName(PulseBpin), PinMap_PWM);


      
      // Instantiate HardwareTimer object. Thanks to 'new' instantiation, HardwareTimer is not destructed when setup() function is finished.

     
     
     
      //HardwareTimer *pwm_Tim2 = new HardwareTimer(InstanceA);
      pwm_Tim2 = new HardwareTimer(Instance0);
      //pwm_Tim2 = new HardwareTimer(Instance1); not used
      pwm_Tim2 = new HardwareTimer(Instance2);
      pwm_Tim2 = new HardwareTimer(Instance3);


      pwm_Tim2->setPWM(channelA, PulseApin, freq, dutyA); // 
   
      
      uint32_t  CCR = pwm_Tim2-> getCaptureCompare(channelA);
      uint32_t  Overflow = pwm_Tim2-> getOverflow();
      
      
      //Added below code for interupts
      pwm_Tim2->setMode(2, TIMER_OUTPUT_COMPARE);
      pwm_Tim2->setCaptureCompare(2, (Overflow/2) -(Overflow - CCR), TICK_COMPARE_FORMAT);
      pwm_Tim2->attachInterrupt(2, ch2_comp_high_interrupt);

      
      // prepare timer2 channel3 at 50% of oveflow
      pwm_Tim2->setMode(3, TIMER_OUTPUT_COMPARE);
      pwm_Tim2->setCaptureCompare(3, 50, PERCENT_COMPARE_FORMAT);
      pwm_Tim2->attachInterrupt(3, ch3_comp_low_interrupt);
      //void setCaptureCompare(uint32_t channel, uint32_t compare, TimerCompareFormat_t format = TICK_COMPARE_FORMAT);  // set Compare register value of specified channel depending on format provided
      
      
                                                          
     
    
      pwm_Tim2->refresh();
      
      //                 call to setup 1 pulse timer needs to be done after pwm_Tim2 as some variables must be read from pwm_Tim2
                                           
      
      Tim1Setup();//This funtion is right above void setup()

       // I need to figure out how or if we need instance for setting up through direct reg manipulation
      
      //oneshot_Tim1
      
      
       // Not Used
      //LL_TIM_SetCounterMode(pwm_Tim2->getHandle()->Instance, LL_TIM_COUNTERMODE_CENTER_UP_DOWN);
      //LL_TIM_SetCounterMode(pwm_Tim2->getHandle()->Instance2, LL_TIM_COUNTERMODE_CENTER_UP_DOWN);
      //LL_TIM_SetCounterMode(pwm_Tim2->getHandle()->Instance3, LL_TIM_COUNTERMODE_CENTER_UP_DOWN);
      //pwm_Tim2->setMode(channelB,TIMER_OUTPUT_COMPARE_PWM1,PulseBpin);
      
      //LL_TIM_SetCounterMode(pwm_Tim2->getHandle()->Instance, LL_TIM_COUNTERMODE_CENTER_UP_DOWN);
      //LL_TIM_SetRepetitionCounter(pwm_Tim2->getHandle()->Instance,1);
       
       

                               //End of Timer setup
      
      M_But.begin();
      L_But.begin();
      R_But.begin();
      AdjBut.begin();


      
      Serial.begin(115200);

      displayFreqPulse();

}
   

void loop() {
if(oldEncPos != encoderPos)
    {
       
       switch(Inc)
    {
        case 0: enc = enc + (encoderPos-oldEncPos);break;
        case 1: enc = enc + ((encoderPos-oldEncPos) * 10);break;
        case 2: enc = enc + ((encoderPos-oldEncPos) * 100);break;
    }
       oldEncPos = encoderPos;
       displayFreqPulse();
    }

if(L_But.pressed())
{
  adjustFreq();
  
  displayFreqPulse();
}
if(R_But.pressed())
{
  
  adjustDutyA();
  displayFreqPulse();
}
if (AdjBut.pressed())
{
  setIncr();
  displayFreqPulse();
}
if (M_But.pressed())
{
  enc = 0;
  freq = 10;
  adjustFreq();
  dutyA = 1;
  adjustDutyA();
  displayFreqPulse();
}
 
  
}
Any Help or pointers greatly appriciated! I will make the second display menu for oneshot when i get it working also do i need to connect my pwmoutputs(timer2) which I think are PA1 and PA0 to the timer1 channel 1 which I think is PA6? or can I map to any pin?

I noticed that a number of people where asking about complimentary outputs without
advanced timer which is not exactly what I have here but the outputs are like below with adjustable pulse and freq simutaneously on both pins

______| |______| |_______| |___ on one pin PA0 whilst

__| |______| |______| |________ on the other pin PA1 is 180 out of phase, handy in some h bridge cases if anyone wants this just remove the function for timer one and the call near the end of setup, but it uses a st7735 screen and a rotary encoder and some buttons. I used an old mouse for three buttons and encoder, and a touch button for the 4th button. Most of the pins can be changed but remember if your button pullup is 5v use a 5v tollerant pin. Not me , I am only 0.004 volt tolerant the rest is just ranting.
Last edited by altEnergy on Tue Sep 28, 2021 7:31 am, edited 6 times in total.
altEnergy
Posts: 10
Joined: Sat Sep 25, 2021 1:02 pm

Re: PWM and OneShot on TIM2 and TIM3

Post by altEnergy »

I think I have worked out the required code, I have not tested it yet but here is what I have got and I also realized it was Timer3 pins I was looking at not timer one, I have changed the subject accordingly. I think I just made myself too tired and stressed with it all. I will test it when I have wrote the display code for oneshot pulse which will be a completely new screen so that when frequency and pulse of pwm are set correctly the oneshot screen is toggled up for oneshot adjustment by another button.

Code: Select all

  
  #define BLACK 0x000                         // Define the display colours we'll be using
  #define BLUE 0x001F                         // so they're constants regardless of which
  #define GREEN 0x07E0                        // display library we use.
  #define YELLOW 0xFFE0
  #define GREY 0x632C
  #define RED  0xFF
  #define L_BROWN 0xC54F
  #define PEACH 0xF54F
  #define TURQUOISE 0x2D53
  #define TEAL 4C11
  #define TFT_CS        PA15
  #define TFT_RST       -1 // Or set to -1 and connect to Arduino RESET pin
  #define TFT_DC        PA8  
 






#define PIN_A   PB9          
#define PIN_B   PB8   

#include <Button.h>
      
#define adjSel PB12
#define M_Button  PA10 
#define L_Button PB14
#define R_Button PB13
  
  
#define TogPwmOneShot PB15

  
  
  #include <Adafruit_ST7735.h>            // Hardware-specific library
   //Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_MOSI, TFT_SCLK, TFT_RST);
   Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_RST);


   
                //Timer Pulse Defines includes and variables
                

#define PulseApin  PA0
#define PulseBpin  PA2 //this pin is not used its only because I dont know how else to deal with the two instances of interupt channels on Tim2

//timer3 input channel 1 is pin PA6, and output channel 2 is PA7

#include <Button.h>

uint16_t freq = 5;

uint8_t dutyA = 4;


const char* multiplier[3] = {" ONE ", " TEN  ", " 100 ", };
   
   int Inc = 0;
   int enc = 0;
   
   
   HardwareTimer *pwm_Tim2;
  
  String mult = multiplier[Inc];

  byte dir = 0;
 
 volatile uint32_t channelA = STM_PIN_CHANNEL(pinmap_function(digitalPinToPinName(PulseApin), PinMap_PWM));
 volatile uint32_t channelB = STM_PIN_CHANNEL(pinmap_function(digitalPinToPinName(PulseBpin), PinMap_PWM));
 volatile uint32_t channel2 = STM_PIN_CHANNEL(pinmap_function(digitalPinToPinName(PulseApin), PinMap_PWM));
 volatile uint32_t channel3 = STM_PIN_CHANNEL(pinmap_function(digitalPinToPinName(PulseBpin), PinMap_PWM));



   //uint32_t  T2PSC = pwm_Tim2-> getPrescaleFactor();
volatile   uint32_t  Overflow = pwm_Tim2-> getOverflow();
volatile   uint32_t  CCR = pwm_Tim2-> getCaptureCompare(channelA);
   //uint32_t  Count = pwm_Tim2-> getCount();   
//                                                       Timer1 Variables
uint32_t  oneShotDelay = 0; 
uint32_t  pulseWidth = 0;  
int Delay = 40;
int Pulse = 20;


 
volatile byte aFlag = 0; // let's us know when we're expecting a rising edge on pinA to signal that the encoder has arrived at a detent
volatile byte bFlag = 0; // let's us know when we're expecting a rising edge on pinB to signal that the encoder has arrived at a detent (opposite direction to when aFlag is set)
volatile int encoderPos = 0; //this variable stores our current value of encoder position. Change to int or uin16_t instead of byte if you want to record a larger range than 0-255
volatile int oldEncPos = 0; //stores the last encoder position value so we can compare to the current reading and see if it has changed (so we know when to print to the serial monitor)
volatile byte reading = 0; //somewhere to store the direct values we read from our interrupt pins before checking to see if we have moved a whole detent




//Button settings


Button M_But(M_Button);
Button L_But(L_Button);
Button R_But(R_Button);
Button AdjBut(adjSel);

//Button


                         ///Button FUNCTIONS
                   
                         
                             // Timmer based pulses functions

                             

void ch2_comp_high_interrupt(void)
{ 
  GPIOA->BSRR = 0b0000000000000010 <<16;//This puts pin PA1 HIGH
}


void ch3_comp_low_interrupt(void)
{ 
  GPIOA->BSRR = 0b0000000000000010;//This puts pin PA1 LOW
}


void setIncr()
{
  if(Inc<2)Inc++;
  else Inc=0;
  mult = multiplier[Inc];
}

void adjustFreq()
{

  //uint32_t  T2PSC = pwm_Tim2-> getPrescaleFactor();
  //uint32_t  Overflow = pwm_Tim2-> getOverflow();
  //uint32_t  CCR = pwm_Tim2-> getCaptureCompare(channelA);
  //uint32_t  Count = pwm_Tim2-> getCount(); 
  pwm_Tim2->pause();
  freq=enc;
  if(freq<0)freq=0; 
  //TIM_TypeDef *InstanceA = (TIM_TypeDef *)pinmap_peripheral(digitalPinToPinName(PulseApin), PinMap_PWM);
  uint32_t channelA = STM_PIN_CHANNEL(pinmap_function(digitalPinToPinName(PulseApin), PinMap_PWM));

  
  pwm_Tim2->setPWM(channelA, PulseApin, freq, dutyA);
  pwm_Tim2->refresh();
  pwm_Tim2->resume();
  pwm_Tim2->pause();
  uint32_t  Overflow = pwm_Tim2-> getOverflow();
  uint32_t  CCR = pwm_Tim2-> getCaptureCompare(channelA);
  uint32_t channel2 = STM_PIN_CHANNEL(pinmap_function(digitalPinToPinName(PulseApin), PinMap_PWM));
  //pwm_Tim2->setCaptureCompare(2, (Overflow/2) -(Overflow - CCR), TICK_COMPARE_FORMAT);
  pwm_Tim2->setCaptureCompare(2, (Overflow/2) + CCR, TICK_COMPARE_FORMAT);
  uint32_t channel3 = STM_PIN_CHANNEL(pinmap_function(digitalPinToPinName(PulseBpin), PinMap_PWM));
  pwm_Tim2->setCaptureCompare(3, 50, PERCENT_COMPARE_FORMAT);
  
  pwm_Tim2->refresh();
  pwm_Tim2->resume();
   
  displayFreqPulse();
}
 
void adjustDutyA()
{
  
  

  
  pwm_Tim2->pause();
  dutyA=enc;
  if(dutyA<0){dutyA=0;}
  //uint32_t  CCR = pwm_Tim2-> getCaptureCompare(channelA);
  //uint32_t  Overflow = pwm_Tim2->getOverflow();
  
  uint32_t channelA = STM_PIN_CHANNEL(pinmap_function(digitalPinToPinName(PulseApin), PinMap_PWM));
 
  
  pwm_Tim2->setPWM(channelA, PulseApin, freq, dutyA);
  pwm_Tim2->refresh();
  pwm_Tim2->resume();
  pwm_Tim2->pause();
  uint32_t  Overflow = pwm_Tim2-> getOverflow();
  uint32_t  CCR = pwm_Tim2-> getCaptureCompare(channelA);
  
                              
  //pwm_Tim2->setCaptureCompare(2, (Overflow/2) -(Overflow - CCR), TICK_COMPARE_FORMAT);
  pwm_Tim2->setCaptureCompare(2, (Overflow/2) + CCR, TICK_COMPARE_FORMAT);
  pwm_Tim2->setCaptureCompare(3, 50, PERCENT_COMPARE_FORMAT);
  
 
  
 
  pwm_Tim2->refresh();
  pwm_Tim2->resume();
  
  displayFreqPulse();
}



                             
//ENCODER FUNCTIONS
void PIN_A_ISR(){
  uint32_t prim;
    
      prim = __get_PRIMASK();

    
      __disable_irq();//stop interrupts happening before we read pin values
      
   
    reading = (GPIOB->IDR & 0x300) >> 6;
    
    if (reading == B00001100 && aFlag)//check that we have both pins at detent (HIGH) and that we are expecting detent on this pin's rising edge
    {

    dir=0;
    encoderPos --; //decrement the encoder's position count
    //any other code or function that needs to be done on read of encoder
     
    
    bFlag = 0; //reset flags for the next turn
    aFlag = 0; //reset flags for the next turn
    }
  else if (reading == B00001000) bFlag = 1; //signal that we're expecting pinB to signal the transition to detent from free rotation
  
  if (!prim) 
      {
        __enable_irq();//restart interrupts
      }
      attachInterrupt(PIN_A,PIN_A_ISR,RISING);

}

void PIN_B_ISR(){

      uint32_t prim;
    
      prim = __get_PRIMASK();

    
      __disable_irq();//stop interrupts happening before we read pin values
  
    
    reading = (GPIOB->IDR & 0x300) >> 6;
    
    if (reading == B00001100 && bFlag)//check that we have both pins at detent (HIGH) and that we are expecting detent on this pin's rising edge
    {
    dir=1;
    encoderPos ++;//increment the encoder's position count
        
     
    bFlag = 0; //reset flags for the next turn
    aFlag = 0; //reset flags for the next turn
   }
   else if (reading == B00000100) aFlag = 1; //signal that we're expecting pinA to signal the transition to detent from free rotation


   if (!prim) 
      {
        __enable_irq();//restart interrupts
      }
      
    attachInterrupt(PIN_B,PIN_B_ISR,RISING);
}


                                            // End of rotary functions

void displayFreqPulse()
{
   //tft.fillScreen(ST7735_BLACK);

   tft.setCursor(11, 12);
   tft.setTextSize(1);
   tft.drawFastHLine(5, 10, tft.width()-10, ST7735_ORANGE);
   tft.drawFastHLine(5, tft.height() - 30, tft.width()-10, ST7735_ORANGE);
   tft.drawFastVLine(5, 10, 88, ST7735_ORANGE);
   tft.drawFastVLine(155, 10, 88, ST7735_ORANGE);
   //if(active==1){tft.setTextColor(YELLOW,ST7735_BLUE);}
   //else{
   tft.setTextColor(YELLOW,ST7735_BLACK);
  
   tft.print("Frequency =   ");
   tft.setCursor(78, 12);
   tft.print("     ");
   tft.setCursor(78, 12);
   tft.print(freq);
   
   //if(active==2){tft.setTextColor(YELLOW,ST7735_BLUE);}
   //else{
   tft.setTextColor(TURQUOISE,ST7735_BLACK);
   tft.setCursor(102, 12);
   tft.print(Inc);
   
   tft.setCursor(11, 24);
   tft.setTextColor(YELLOW,ST7735_BLACK);
   tft.print("DutyA = ");
   tft.setCursor(51, 24);
   tft.println("     ");
   tft.setCursor(51, 24);
   tft.println(dutyA);
   
   tft.setTextColor(ST7735_WHITE,ST7735_BLACK);
   tft.setCursor(97, 24);
   //here we will print the multiplier           // just check the button through to here
   tft.print("X = ");
   tft.setCursor(102,24);
   tft.print("   ");
   tft.setCursor(102,24);
   tft.println(mult);
  

   tft.setTextColor(YELLOW,ST7735_BLACK);

                      

   tft.setTextSize(1);
   tft.setCursor(11, 36);
   //tft.setTextColor(YELLOW,ST7735_BLACK);
   tft.print("value to set ");
   tft.setTextColor(ST7735_MAGENTA,ST7735_BLACK); 
   tft.setCursor(87, 36); tft.print("       "); 
   tft.setCursor(87, 36); tft.println(enc);
   //;tft.println("        ");
   
     
 
   
   //uint32_t  T2PSC = pwm_Tim2-> getPrescaleFactor();
   
   //uint32_t  Count = pwm_Tim2-> getCount();
   tft.setTextColor(ST7735_ORANGE,ST7735_BLACK);
   tft.setCursor(11, 47);
   tft.print("press left button");
   tft.setCursor(11, 56);
   tft.print("to set frequency");
   //tft.setCursor(11, 68);
   //tft.print("press right button");
   tft.setCursor(10, 65);
   tft.print("right for +ve Duty Cycle");
   tft.setCursor(11, 75);
   
   tft.setTextColor(ST7735_GREEN,ST7735_BLACK);
   
   uint32_t  Overflow = pwm_Tim2-> getOverflow();
   uint32_t  CCR = pwm_Tim2-> getCaptureCompare(channelA);
   tft.print("Overflow = ");
   tft.setCursor(72, 75);
   tft.println("       ");
   tft.setCursor(72, 75);
   tft.println(Overflow);
   
   
   tft.setCursor(11, 85);
   tft.print("CCR = ");
   tft.setCursor(50, 85);
   tft.println("     ");
   tft.setCursor(50, 85);
   tft.println(CCR);
   
   
  
}






void Tim1Setup()
{

        //         Get Timer2 Prescaler for near the end of this function 
        //          where we set Timer1 PreScaler to the same as Timer2 PreScaler
        uint32_t  T2PSC = pwm_Tim2-> getPrescaleFactor();
        uint32_t  Overflow = pwm_Tim2-> getOverflow();
        
        uint32_t  oneShotDelay = (Overflow/100) * Delay; 
        uint32_t  pulseWidth = (Overflow/100) * Pulse;  
                            
                             

        
        //                              Setting Up Timer 3 for "one shot mode"(OPM)


               //---------------Input will be channel 1 Output will be channel 2
        //1.a)       Select the TIxFPx trigger to be used by writing CCxS bits (B000000000000011) in CCMRx register
        //2.b)       Select the output compare mode by writing OCyM bits (110000000000000)PWM1 mode  in CCMRy register (PWM1 
        //           or PWM2 mode)
        TIM3->CCMR1 &= 0b11000000000011; //TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1PE | TIM_CCMR1_CC2S_0;

        //1.b)       Select the polarity of the input pin by writing CCxP and CCxNP bits(10 & 1000) in CCER
        //           register
        TIM3->CCER  &= 0xA;
        
        // CR2: defaults
        // SMCR: TS=110 (TI2FP2 trig source), SMS=1000 (combined reset + trigger mode)
               //but changed below code as 111 is for TS bits  and 110 for SMS bits besides compiler complained
        //1.c)        Configure the TIxFPx trigger for the slave mode trigger by writing TS bits (1110000) in SMCR 
        //            register
        //1.d)        Select the trigger mode for the slave mode by writing SMS = (110) in SMCR 
        //            register.
        TIM3->SMCR &= 0x76;  //TIM_SMCR_TS_1 | TIM_SMCR_TS_2 | TIM_SMCR_SMS_3;
        
        // CCER: CC1P=0 (OC1 active high), CC1E=1 (OC1 output activated),
        //       CC2NP=0 and CC2P=1 (channel 2 input is falling-edge sensitive)
        
        //2.a)       Select the output polarity by writing CCyP bit in CCER register

        //     check this is correct       
        TIM3->CCER &= 0x20; // looks ok
        

        //                           Find out the meanning of CCMRy vs CCCMRx..DONE  y is output channel 2 and x is input channel 1.
        //2.b)       Select the output compare mode by writing OCyM bits in CCMRy register (PWM1 
        //           or PWM2 mode) 
                                    // The Above is Done in 1.a)
        
                                    //   BELOW AND ABOVE ARE DONE USING CCMR1 above OC2M for output channel 2
                                    
        //TIM3->CCMR2 &= 110000000000000;//OC2M (pwm1)

        
                
        //2.c)       Set the delay value by writing in CCRy register
        //                                     need to figure what CCR to use 1 or 2
        //                                     It will be CCR2 which is Channel 2 there are
        //                                     4 ccr registers! one for each channel
        TIM3->CCR2 = oneShotDelay; // In ticks, to be worked out in separate function from - Delay = CCRy/(TIMx_CLK/(PSC + 1))
        //      So if we get timer2 overflow we know what % delay we'll get, by divide by 100 and multiply by % of needed delay
                                        //     Simple Right?
        // channel 2 output this will set the delay which needs to be a variable 
        
        // programmed on screen but preset in variables as volatile int
        
       

                             //might be time to make a seperate display screen for delay and pulse
                                                              //  Ya said that already

        //2.d)       Set the auto reload value to have the desired pulse: pulse = TIMy_ARR - 
        //           TIMy_CCRy

        //            Nothing
                                                                              //                                         Delayed pulse
        TIM3->ARR = pulseWidth;  // pulseWidth = ARR - oneShotDelay; sort of thing,  so trigger pulse  ____||____________|????????????|
                                                                                                        //   oneShotDelay             ^ARR 
                                          // so pulseWidth(ARR reg above) should be equal to oneShotDelay + desired pulse width but as
                                          // everything is taken from timer 2 we need the number of overflow to get the possitions in percent                                                              
        
        
                                    // Carry on here .............. But come back to above two register settings and create the function
                                    // or functions required to set oneShotDelay and pulseWidth

                                    
        //3.         Select the one pulse mode by setting the OPM bit in CR1 register, if only one pulse is to 
        //           be generated. Otherwise this bit should be reset:
        TIM3->CR1 &= 0b1000; // OnePulseMode bit , I do believe that could be it for now 
        // The above could probably be written as follows from someone elses code 
                                              // CR1: OPM=1 (one pulse mode, up-counter, counter disabled)
                                                  //TIM3->CR1 = TIM_CR1_OPM;
        

        
        //           Delay = CCRy/(TIMx_CLK/(PSC + 1))
        //           Pulse-Length= (ARR+1-CCRy)/(TIMx_CLK/(PSC+1)).

           // the prescaler must be set to same value as channel 2
           
        TIM3->PSC = T2PSC;

         
         //uint32_t  Overflow = pwm_Tim2-> getOverflow();
        //                For more details on using the timer in this mode refer to the examples provided in the 
        //                STM32Cube package in the Examples\TIM\TIM_OnePulse sub folder

        // Generate an update event to force these values into the active registers
        TIM3->EGR = TIM_EGR_UG;

        //                I think that is all the code for the one shot timer complete
        
        //               we need to create a new display function to be called from a button press of some sort
        //               that will display delay and pulse as a percentage of the original wave where the percent of 
        //               both delay and pulse will be displayed and set in same way as freq and pulse width of timer 2.
        //                     And that should be it!


       

                     //End of Timer1 setup for one shot

}


void setup() {

     
   pinMode(PIN_A, INPUT_PULLUP);
   pinMode(PIN_B, INPUT_PULLUP);
   pinMode(adjSel, INPUT_PULLUP);
   pinMode(M_Button, INPUT_PULLUP);
   pinMode(L_Button, INPUT_PULLUP);
   pinMode(R_Button, INPUT_PULLUP);
   pinMode(TogPwmOneShot, INPUT_PULLUP);
   pinMode(TFT_CS,OUTPUT);
   pinMode(TFT_DC,OUTPUT);
   pinMode(PA1,OUTPUT);

     
   SPI.setMOSI(PB5);
   //SPI.setMISO(PB14);
   SPI.setSCLK(PB3);
   SPI.begin();
   SPI.setDataMode(SPI_MODE0);
   SPI.setClockDivider(SPI_CLOCK_DIV16);
   delay(100);
   //_colstart = 2;
   //_rowstart = 1;
   tft.initR(INITR_BLACKTAB);
   tft.setRotation(1);
     
   tft.fillScreen(ST7735_BLACK);

   tft.setCursor(15, tft.height() -20);
   tft.setTextSize(1);
   tft.drawFastHLine(0, tft.height() - 23, tft.width()-10, ST7735_BLUE);
   tft.setTextColor(BLUE);
   tft.println(" STM32F103  0-1kHZ+");
   tft.print(" PWM Generator");

   delay(1000);

              
    
                                                        
      attachInterrupt(PIN_A,PIN_A_ISR,RISING); // set an interrupt on PinA, looking for a rising edge signal and executing the "PinA" Interrupt Service Routine (below)
      attachInterrupt(PIN_B,PIN_B_ISR,RISING); // set an interrupt on PinB, looking for a rising edge signal and executing the "PinB" Interrupt Service Routine (below)
                                 
                                           
                                           //setup for timer pulses

      TIM_TypeDef *Instance0 = (TIM_TypeDef *)pinmap_peripheral(digitalPinToPinName(PulseApin), PinMap_PWM);
      //TIM_TypeDef *Instance1 = (TIM_TypeDef *)pinmap_peripheral(digitalPinToPinName(PulseBpin), PinMap_PWM);
      TIM_TypeDef *Instance2 = (TIM_TypeDef *)pinmap_peripheral(digitalPinToPinName(PulseBpin), PinMap_PWM);
      TIM_TypeDef *Instance3 = (TIM_TypeDef *)pinmap_peripheral(digitalPinToPinName(PulseBpin), PinMap_PWM);
     
     
     
      pwm_Tim2 = new HardwareTimer(Instance0);
      //pwm_Tim2 = new HardwareTimer(Instance1);
      pwm_Tim2 = new HardwareTimer(Instance2);
      pwm_Tim2 = new HardwareTimer(Instance3);
     

      // Configure and start PWM
      // MyTim->setPWM(channel, pin, 5, 10, NULL, NULL); // No callback required, we can simplify the function call
      pwm_Tim2->setPWM(channelA, PulseApin, freq, dutyA); // 
   
     
      uint32_t  CCR = pwm_Tim2-> getCaptureCompare(channelA);
      uint32_t  Overflow = pwm_Tim2-> getOverflow();
      
      
      //for rising edge on pin PA1
      pwm_Tim2->setMode(2, TIMER_OUTPUT_COMPARE);
      pwm_Tim2->setCaptureCompare(2, (Overflow/2) -(Overflow - CCR), TICK_COMPARE_FORMAT);
      pwm_Tim2->attachInterrupt(2, ch2_comp_high_interrupt);

      
      //for falling edge on pin PA1
      pwm_Tim2->setMode(3, TIMER_OUTPUT_COMPARE);
      pwm_Tim2->setCaptureCompare(3, 50, PERCENT_COMPARE_FORMAT);
      pwm_Tim2->attachInterrupt(3, ch3_comp_low_interrupt);
     
     
      pwm_Tim2->refresh();
      
      //                 call to setup 1 pulse timer needs to be done after pwm_Tim2 as some variables must be read from pwm_Tim2
                                           
      
      Tim3Setup();//This funtion is right above void setup()

      
       
       
       
       


                               //End of Timer setup
      
      M_But.begin();
      L_But.begin();
      R_But.begin();
      AdjBut.begin();


      
      Serial.begin(115200);

      displayFreqPulse();

}
   

void loop() {
if(oldEncPos != encoderPos)
    {
       
       switch(Inc)
    {
        case 0: enc = enc + (encoderPos-oldEncPos);break;
        case 1: enc = enc + ((encoderPos-oldEncPos) * 10);break;
        case 2: enc = enc + ((encoderPos-oldEncPos) * 100);break;
    }
       oldEncPos = encoderPos;
       displayFreqPulse();
    }


if(L_But.pressed())
   {
     adjustFreq();
     displayFreqPulse();
   }
if(R_But.pressed())
   {
    adjustDutyA();
    displayFreqPulse();
   }
if (AdjBut.pressed())
   {
    setIncr();
    displayFreqPulse();
   }
if (M_But.pressed())
   {
    enc = 0;
    freq = 10;
    adjustFreq();
    dutyA = 1;
    adjustDutyA();
    displayFreqPulse();
   }

  
}
I think that is what I was trying to do but still not sure if it will work correctly.
Thanks
altEnergy
Posts: 10
Joined: Sat Sep 25, 2021 1:02 pm

Re: PWM and OneShot on TIM2 and TIM3

Post by altEnergy »

Well I have completed the code to display everything which is working but I am only getting my 2 180 out of phase pwm signals nothing is coming out of PA7 and I have wired PA6 to PA1 so the input trigger is getting to the correct pin. I am almost certain the problem is in the "void Tim3Setup()"
Function or some initialization of timer 3 but Not sure how or what can anyone please Help?

Code: Select all

  #define BLACK 0x000                         // Define the display colours we'll be using
  #define BLUE 0x001F                         // so they're constants regardless of which
  #define GREEN 0x07E0                        // display library we use.
  #define YELLOW 0xFFE0
  #define GREY 0x632C
  #define RED  0xFF
  #define ORANGE 0xFC00
  #define MAGENTA 0xF81F
  #define CYAN 0x07FF
  #define L_LEMON 0xE7B1
  #define L_BROWN 0xC54F
  #define PEACH 0xF54F
  #define TURQUOISE 0x2D53
  #define TEAL 0x4C11
  #define PINK 0xF555
  #define L_BLUE 0x767F
  #define L_GREEN 0x67F2
  #define L_GREY 0x8C51
  #define M_GREY 0x630C
  #define D_GREY 0x3186
  #define NAVY 0x018A
  #define D_ARMY_GREEN 0x0181
  #define OFF_WHITE 0xFFFA
  #define WHITE 0xFFFF
  #define TFT_CS        PA15
  #define TFT_RST       -1 // Or set to -1 and connect to Arduino RESET pin
  #define TFT_DC        PA8  
 






#define PIN_A   PB9          
#define PIN_B   PB8   
#define oneShotIn PA6
#define oneShotOut PA7

#include <Button.h>
      
#define adjSel PB12
#define M_Button  PA10 
#define L_Button PB14
#define R_Button PB13
#define P_Button PB15

  
  
  #include <Adafruit_ST7735.h>            // Hardware-specific library
   //Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_MOSI, TFT_SCLK, TFT_RST);
   Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_RST);


   
                //Timer Pulse Defines includes and variables
                

#define PulseApin  PA0
#define PulseBpin  PA2 //this pin is not used its only because I dont know how else to deal with the two instances of interupt channels


#include <Button.h>

uint16_t freq = 5;

uint8_t dutyA = 4;


const char* multiplier[3] = {" ONE ", " TEN  ", " 100 ", };
   
   int Inc = 0;
   int enc = 0;
   
   
   HardwareTimer *pwm_Tim2;
   //HardwareTimer *MyTimB;
   

  String mult = multiplier[Inc];

  byte dir = 0;
 
 volatile uint32_t channelA = STM_PIN_CHANNEL(pinmap_function(digitalPinToPinName(PulseApin), PinMap_PWM));
 volatile uint32_t channelB = STM_PIN_CHANNEL(pinmap_function(digitalPinToPinName(PulseBpin), PinMap_PWM));
 volatile uint32_t channel2 = STM_PIN_CHANNEL(pinmap_function(digitalPinToPinName(PulseApin), PinMap_PWM));
 volatile uint32_t channel3 = STM_PIN_CHANNEL(pinmap_function(digitalPinToPinName(PulseBpin), PinMap_PWM));



   //uint32_t  T2PSC = pwm_Tim2-> getPrescaleFactor();
volatile   uint32_t  Overflow = pwm_Tim2-> getOverflow();
volatile   uint32_t  CCR = pwm_Tim2-> getCaptureCompare(channelA);
   //uint32_t  Count = pwm_Tim2-> getCount();   
//                                                       Timer1 Variables
uint32_t  oneShotDelay = 0; // this and below need correctly set see 2.d) in timer1setup()
uint32_t  pulseWidth = 0;  //just to test but should actually be 50% give or take of timer two overflow
                             // as that is where the voltage will be higher and current will start to flow
                             // and we need alternate names to hold the percentage value that these will be calculated from
int Delay = 40;
int Pulse = 20;

byte pwmOneshot = 0;
 
volatile byte aFlag = 0; // let's us know when we're expecting a rising edge on pinA to signal that the encoder has arrived at a detent
volatile byte bFlag = 0; // let's us know when we're expecting a rising edge on pinB to signal that the encoder has arrived at a detent (opposite direction to when aFlag is set)
volatile int encoderPos = 0; //this variable stores our current value of encoder position. Change to int or uin16_t instead of byte if you want to record a larger range than 0-255
volatile int oldEncPos = 0; //stores the last encoder position value so we can compare to the current reading and see if it has changed (so we know when to print to the serial monitor)
volatile byte reading = 0; //somewhere to store the direct values we read from our interrupt pins before checking to see if we have moved a whole detent




//Button settings


Button M_But(M_Button);
Button L_But(L_Button);
Button R_But(R_Button);
Button AdjBut(adjSel);
Button P_But(P_Button);
//Button


                         ///Button FUNCTIONS
                   
                         
                             // Timmer based pulses functions

                             

void ch2_comp_high_interrupt(void)
{ 
  GPIOA->BSRR = 0b0000000000000010 <<16;//This puts pin PA1 HIGH
}


void ch3_comp_low_interrupt(void)
{ 
  GPIOA->BSRR = 0b0000000000000010;//This puts pin PA1 LOW
}


void setIncr()
{
  if(Inc<2)Inc++;
  else Inc=0;
  mult = multiplier[Inc];
}

void setIncr1S()
{
  if(Inc<1)Inc++;
  else Inc=0;
  mult = multiplier[Inc];
}

void adjustFreq()
{

  //uint32_t  T2PSC = pwm_Tim2-> getPrescaleFactor();
  //uint32_t  Overflow = pwm_Tim2-> getOverflow();
  //uint32_t  CCR = pwm_Tim2-> getCaptureCompare(channelA);
  //uint32_t  Count = pwm_Tim2-> getCount(); 
  pwm_Tim2->pause();
  freq=enc;
  if(freq<0)freq=0; 
  //TIM_TypeDef *InstanceA = (TIM_TypeDef *)pinmap_peripheral(digitalPinToPinName(PulseApin), PinMap_PWM);
  uint32_t channelA = STM_PIN_CHANNEL(pinmap_function(digitalPinToPinName(PulseApin), PinMap_PWM));

  
  pwm_Tim2->setPWM(channelA, PulseApin, freq, dutyA);
  pwm_Tim2->refresh();
  pwm_Tim2->resume();
  pwm_Tim2->pause();
  uint32_t  Overflow = pwm_Tim2-> getOverflow();
  uint32_t  CCR = pwm_Tim2-> getCaptureCompare(channelA);
  uint32_t channel2 = STM_PIN_CHANNEL(pinmap_function(digitalPinToPinName(PulseApin), PinMap_PWM));
  //pwm_Tim2->setCaptureCompare(2, (Overflow/2) -(Overflow - CCR), TICK_COMPARE_FORMAT);
  pwm_Tim2->setCaptureCompare(2, (Overflow/2) + CCR, TICK_COMPARE_FORMAT);
  uint32_t channel3 = STM_PIN_CHANNEL(pinmap_function(digitalPinToPinName(PulseBpin), PinMap_PWM));
  pwm_Tim2->setCaptureCompare(3, 50, PERCENT_COMPARE_FORMAT);
  
  pwm_Tim2->refresh();
  pwm_Tim2->resume();
   
  displayFreqPulse();
}
 
void adjustDutyA()
{
  
  

  
  pwm_Tim2->pause();
  dutyA=enc;
  if(dutyA<0){dutyA=0;}
  //uint32_t  CCR = pwm_Tim2-> getCaptureCompare(channelA);
  //uint32_t  Overflow = pwm_Tim2->getOverflow();
  
  uint32_t channelA = STM_PIN_CHANNEL(pinmap_function(digitalPinToPinName(PulseApin), PinMap_PWM));
 
  
  pwm_Tim2->setPWM(channelA, PulseApin, freq, dutyA);
  pwm_Tim2->refresh();
  pwm_Tim2->resume();
  pwm_Tim2->pause();
  uint32_t  Overflow = pwm_Tim2-> getOverflow();
  uint32_t  CCR = pwm_Tim2-> getCaptureCompare(channelA);
  
                              
  //pwm_Tim2->setCaptureCompare(2, (Overflow/2) -(Overflow - CCR), TICK_COMPARE_FORMAT);
  pwm_Tim2->setCaptureCompare(2, (Overflow/2) + CCR, TICK_COMPARE_FORMAT);
  pwm_Tim2->setCaptureCompare(3, 50, PERCENT_COMPARE_FORMAT);
  
 
  
 
  pwm_Tim2->refresh();
  pwm_Tim2->resume();
  
  displayFreqPulse();
}



                             
//ENCODER FUNCTIONS
void PIN_A_ISR(){
  uint32_t prim;
    
      prim = __get_PRIMASK();

    
      __disable_irq();//stop interrupts happening before we read pin values
      
   
    reading = (GPIOB->IDR & 0x300) >> 6;
    
    if (reading == B00001100 && aFlag)//check that we have both pins at detent (HIGH) and that we are expecting detent on this pin's rising edge
    {

    dir=0;
    encoderPos --; //decrement the encoder's position count
    //any other code or function that needs to be done on read of encoder
     
    
    bFlag = 0; //reset flags for the next turn
    aFlag = 0; //reset flags for the next turn
    }
  else if (reading == B00001000) bFlag = 1; //signal that we're expecting pinB to signal the transition to detent from free rotation
  
  if (!prim) 
      {
        __enable_irq();//restart interrupts
      }
      attachInterrupt(PIN_A,PIN_A_ISR,RISING);

}

void PIN_B_ISR(){

      uint32_t prim;
    
      prim = __get_PRIMASK();

    
      __disable_irq();//stop interrupts happening before we read pin values
  
    
    reading = (GPIOB->IDR & 0x300) >> 6;
    
    if (reading == B00001100 && bFlag)//check that we have both pins at detent (HIGH) and that we are expecting detent on this pin's rising edge
    {
    dir=1;
    encoderPos ++;//increment the encoder's position count
        
     
    bFlag = 0; //reset flags for the next turn
    aFlag = 0; //reset flags for the next turn
   }
   else if (reading == B00000100) aFlag = 1; //signal that we're expecting pinA to signal the transition to detent from free rotation


   if (!prim) 
      {
        __enable_irq();//restart interrupts
      }
      
    attachInterrupt(PIN_B,PIN_B_ISR,RISING);
}


                                            // End of rotary functions

void displayFreqPulse()
{
   tft.fillScreen(BLACK);

   tft.setCursor(15, tft.height() -20);
   tft.setTextSize(1);
   tft.drawFastHLine(0, tft.height() - 23, tft.width()-10, TEAL);
   tft.setTextColor(TURQUOISE);
   tft.println(" STM32F103  0-1kHZ+");
   tft.print(" PWM Generator");

   tft.setCursor(11, 12);
   tft.setTextSize(1);
   tft.drawFastHLine(5, 10, tft.width()-10, ST7735_ORANGE);
   tft.drawFastHLine(5, tft.height() - 30, tft.width()-10, ST7735_ORANGE);
   tft.drawFastVLine(5, 10, 88, ST7735_ORANGE);
   tft.drawFastVLine(155, 10, 88, ST7735_ORANGE);
   
   tft.setTextColor(YELLOW,BLACK);
  
   tft.print("Frequency =   ");
   tft.setCursor(78, 12);
   tft.print("     ");
   tft.setCursor(78, 12);
   tft.print(freq);
   
   //if(active==2){tft.setTextColor(YELLOW,ST7735_BLUE);}
   //else{
   tft.setTextColor(TURQUOISE,BLACK);
   tft.setCursor(102, 12);
   tft.print(Inc);
   
   tft.setCursor(11, 24);
   tft.setTextColor(YELLOW,BLACK);
   tft.print("DutyA = ");
   tft.setCursor(51, 24);
   tft.println("     ");
   tft.setCursor(51, 24);
   tft.println(dutyA);
   
   tft.setTextColor(WHITE,BLACK);
   tft.setCursor(97, 24);
   //here we will print the multiplier           // just check the button through to here
   tft.print("X = ");
   tft.setCursor(102,24);
   tft.print("   ");
   tft.setCursor(102,24);
   tft.println(mult);
  

   tft.setTextColor(YELLOW,BLACK);

                      

   tft.setTextSize(1);
   tft.setCursor(11, 36);
   //tft.setTextColor(YELLOW,ST7735_BLACK);
   tft.print("value to set ");
   tft.setTextColor(MAGENTA,BLACK); 
   tft.setCursor(87, 36); tft.print("       "); 
   tft.setCursor(87, 36); tft.println(enc);
   //;tft.println("        ");
   
     
 
   
   //uint32_t  T2PSC = pwm_Tim2-> getPrescaleFactor();
   
   //uint32_t  Count = pwm_Tim2-> getCount();
   tft.setTextColor(ORANGE,BLACK);
   tft.setCursor(11, 47);
   tft.print("press left button");
   tft.setCursor(11, 56);
   tft.print("to set frequency");
   //tft.setCursor(11, 68);
   //tft.print("press right button");
   tft.setCursor(10, 65);
   tft.print("right for +ve Duty Cycle");
   tft.setCursor(11, 75);
   
   tft.setTextColor(GREEN,BLACK);
   
   uint32_t  Overflow = pwm_Tim2-> getOverflow();
   uint32_t  CCR = pwm_Tim2-> getCaptureCompare(channelA);
   tft.print("Overflow = ");
   tft.setCursor(72, 75);
   tft.println("       ");
   tft.setCursor(72, 75);
   tft.println(Overflow);
   
   
   tft.setCursor(11, 85);
   tft.print("CCR = ");
   tft.setCursor(50, 85);
   tft.println("     ");
   tft.setCursor(50, 85);
   tft.println(CCR);
   
   
  
}


void displayOneShot()
{
   tft.fillScreen(BLACK);
   
   tft.setCursor(15, tft.height() -20);
   tft.setTextSize(1);
   tft.drawFastHLine(0, tft.height() - 23, tft.width()-10, L_BLUE);
   tft.setTextColor(CYAN);
   tft.println(" STM32F103  OneShot");
   tft.setTextColor(L_GREEN);
   tft.print(" Phase and Pulse adjust");
   
   tft.setCursor(12, 12);
   tft.setTextSize(1);
   tft.drawFastHLine(5, 10, tft.width()-10, PEACH);
   tft.drawFastHLine(5, tft.height() - 30, tft.width()-10, PEACH);
   tft.drawFastVLine(5, 10, 88, PEACH);
   tft.drawFastVLine(155, 10, 88, PEACH);


   tft.setTextColor(L_LEMON,BLACK);
   tft.print("Delay =  ");
   tft.setCursor(58, 12);
   tft.print("     ");
   tft.setCursor(58, 12);
   tft.print(Delay);
   tft.setCursor(74, 12);
   tft.print("%");

   tft.setCursor(12, 24);
   tft.setTextColor(L_LEMON,BLACK);
   tft.print("Pulse = ");
   tft.setCursor(51, 24);
   tft.println("     ");
   tft.setCursor(51, 24);
   tft.println(Pulse);
   tft.setCursor(74, 24);
   tft.print("%");

   tft.setTextColor(YELLOW,BLACK);

                      

   tft.setTextSize(1);
   tft.setCursor(11, 36);
   //tft.setTextColor(YELLOW,ST7735_BLACK);
   tft.print("value to set ");
   tft.setTextColor(L_GREEN,BLACK); 
   tft.setCursor(78, 36); tft.print("       "); 
   tft.setCursor(78, 36); tft.println(enc);
   
   tft.setTextColor(OFF_WHITE,BLACK);
   tft.setCursor(12, 68);
   //here we will print the multiplier           // just check the button through to here
   tft.print("Multiplier = ");
   tft.setCursor(78,68);
   tft.print("     ");
   tft.setCursor(78,68);
   tft.println(mult);
   
}



void Tim3Setup()
{

        //         Get Timer2 Prescaler for near the end of this function 
        //          where we set Timer1 PreScaler to the same as Timer2 PreScaler
        uint32_t  T2PSC = pwm_Tim2-> getPrescaleFactor();
        uint32_t  Overflow = pwm_Tim2-> getOverflow();
        
        uint32_t  oneShotDelay = (Overflow/100) * Delay; 
        uint32_t  pulseWidth = (Overflow/100) * Pulse;  
                            
                             

        
        //                              Setting Up Timer 3 for "one shot mode"(OPM)


               //---------------Input will be channel 1 Output will be channel 2
        //1.a)       Select the TIxFPx trigger to be used by writing CCxS bits (B000000000000011) in CCMRx register
        //2.b)       Select the output compare mode by writing OCyM bits (110000000000000)PWM1 mode  in CCMRy register (PWM1 
        //           or PWM2 mode)
        TIM3->CCMR1 &= 0b11000000000011; //TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1PE | TIM_CCMR1_CC2S_0;

        //1.b)       Select the polarity of the input pin by writing CCxP and CCxNP bits(10 & 1000) in CCER
        //           register
        TIM3->CCER  &= 0xA;
        
        // CR2: defaults
        // SMCR: TS=110 (TI2FP2 trig source), SMS=1000 (combined reset + trigger mode)
               //but changed below code as 111 is for TS bits  and 110 for SMS bits besides compiler complained
        //1.c)        Configure the TIxFPx trigger for the slave mode trigger by writing TS bits (1110000) in SMCR 
        //            register
        //1.d)        Select the trigger mode for the slave mode by writing SMS = (110) in SMCR 
        //            register.
        TIM3->SMCR &= 0x76;  //TIM_SMCR_TS_1 | TIM_SMCR_TS_2 | TIM_SMCR_SMS_3;
        
        // CCER: CC1P=0 (OC1 active high), CC1E=1 (OC1 output activated),
        //       CC2NP=0 and CC2P=1 (channel 2 input is falling-edge sensitive)
        
        //2.a)       Select the output polarity by writing CCyP bit in CCER register

        //     check this is correct       
        TIM3->CCER &= 0x20; // looks ok
        

        //                           Find out the meanning of CCMRy vs CCCMRx..DONE  y is output channel 2 and x is input channel 1.
        //2.b)       Select the output compare mode by writing OCyM bits in CCMRy register (PWM1 
        //           or PWM2 mode) 
                                    // The Above is Done in 1.a)
        
                                    //   BELOW AND ABOVE ARE DONE USING CCMR1 above OC2M for output channel 2
                                    
        //TIM3->CCMR2 &= 110000000000000;//OC2M (pwm1)

        
                
        //2.c)       Set the delay value by writing in CCRy register
        //                                     need to figure what CCR to use 1 or 2
        //                                     It will be CCR2 which is Channel 2 there are
        //                                     4 ccr registers! one for each channel
        TIM3->CCR2 = oneShotDelay; // In ticks, to be worked out in separate function from - Delay = CCRy/(TIMx_CLK/(PSC + 1))
        //      So if we get timer2 overflow we know what % delay we'll get, by divide by 100 and multiply by % of needed delay
                                        //     Simple Right?
        // channel 2 output this will set the delay which needs to be a variable 
        
        // programmed on screen but preset in variables as volatile int
        
       

                             //might be time to make a seperate display screen for delay and pulse
                                                              //  Ya said that already

        //2.d)       Set the auto reload value to have the desired pulse: pulse = TIMy_ARR - 
        //           TIMy_CCRy

        //            Nothing
                                                                              //                                         Delayed pulse
        TIM3->ARR = pulseWidth;  // pulseWidth = ARR - oneShotDelay; sort of thing,  so trigger pulse  ____||____________|????????????|
                                                                                                        //   oneShotDelay             ^ARR 
                                          // so pulseWidth(ARR reg above) should be equal to oneShotDelay + desired pulse width but as
                                          // everything is taken from timer 2 we need the number of overflow to get the possitions in percent                                                              
        
        
                                    // Carry on here .............. But come back to above two register settings and create the function
                                    // or functions required to set oneShotDelay and pulseWidth

                                    
        //3.         Select the one pulse mode by setting the OPM bit in CR1 register, if only one pulse is to 
        //           be generated. Otherwise this bit should be reset:
        TIM3->CR1 &= 0b1000; // OnePulseMode bit , I do believe that could be it for now 
        // The above could probably be written as follows from someone elses code 
                                              // CR1: OPM=1 (one pulse mode, up-counter, counter disabled)
                                                  //TIM3->CR1 = TIM_CR1_OPM;
        

        
        //           Delay = CCRy/(TIMx_CLK/(PSC + 1))
        //           Pulse-Length= (ARR+1-CCRy)/(TIMx_CLK/(PSC+1)).

           // the prescaler must be set to same value as channel 2
           
        TIM3->PSC = T2PSC;

         
         //uint32_t  Overflow = pwm_Tim2-> getOverflow();
        //                For more details on using the timer in this mode refer to the examples provided in the 
        //                STM32Cube package in the Examples\TIM\TIM_OnePulse sub folder

        // Generate an update event to force these values into the active registers
        TIM3->EGR = TIM_EGR_UG;

        //                I think that is all the code for the one shot timer complete
        
        //               we need to create a new display function to be called from a button press of some sort
        //               that will display delay and pulse as a percentage of the original wave where the percent of 
        //               both delay and pulse will be displayed and set in same way as freq and pulse width of timer 2.
        //                     And that should be it!


       

                     //End of Timer1 setup for one shot

}


void setup() {

     
   pinMode(PIN_A, INPUT_PULLUP);
   pinMode(PIN_B, INPUT_PULLUP);
   pinMode(adjSel, INPUT_PULLUP);
   pinMode(M_Button, INPUT_PULLUP);
   pinMode(L_Button, INPUT_PULLUP);
   pinMode(R_Button, INPUT_PULLUP);
   pinMode(P_Button, INPUT_PULLUP);
   pinMode(TFT_CS,OUTPUT);
   pinMode(TFT_DC,OUTPUT);
   pinMode(PA1,OUTPUT);
   pinMode(oneShotIn,INPUT);
   pinMode(oneShotOut,OUTPUT);
   
     
   SPI.setMOSI(PB5);
   //SPI.setMISO(PB14);
   SPI.setSCLK(PB3);
   SPI.begin();
   SPI.setDataMode(SPI_MODE0);
   SPI.setClockDivider(SPI_CLOCK_DIV16);
   delay(100);
   //_colstart = 2;
   //_rowstart = 1;
   tft.initR(INITR_BLACKTAB);
   tft.setRotation(1);
     
   tft.fillScreen(BLACK);

   tft.setCursor(15, tft.height() -20);
   tft.setTextSize(1);
   tft.drawFastHLine(0, tft.height() - 23, tft.width()-10, BLUE);
   tft.setTextColor(BLUE);
   tft.println(" STM32F103  0-1kHZ+");
   tft.print(" PWM Generator");

   delay(1000);

              
    
                                                        
      attachInterrupt(PIN_A,PIN_A_ISR,RISING); // set an interrupt on PinA, looking for a rising edge signal and executing the "PinA" Interrupt Service Routine (below)
      attachInterrupt(PIN_B,PIN_B_ISR,RISING); // set an interrupt on PinB, looking for a rising edge signal and executing the "PinB" Interrupt Service Routine (below)
                                 
                                           
                                           //setup for timer pulses

      TIM_TypeDef *Instance0 = (TIM_TypeDef *)pinmap_peripheral(digitalPinToPinName(PulseApin), PinMap_PWM);
      //TIM_TypeDef *Instance1 = (TIM_TypeDef *)pinmap_peripheral(digitalPinToPinName(PulseBpin), PinMap_PWM);
      TIM_TypeDef *Instance2 = (TIM_TypeDef *)pinmap_peripheral(digitalPinToPinName(PulseBpin), PinMap_PWM);
      TIM_TypeDef *Instance3 = (TIM_TypeDef *)pinmap_peripheral(digitalPinToPinName(PulseBpin), PinMap_PWM);
     
     
     
      pwm_Tim2 = new HardwareTimer(Instance0);
      //pwm_Tim2 = new HardwareTimer(Instance1);
      pwm_Tim2 = new HardwareTimer(Instance2);
      pwm_Tim2 = new HardwareTimer(Instance3);
     

      // Configure and start PWM
      // MyTim->setPWM(channel, pin, 5, 10, NULL, NULL); // No callback required, we can simplify the function call
      pwm_Tim2->setPWM(channelA, PulseApin, freq, dutyA); // 
   
     
      uint32_t  CCR = pwm_Tim2-> getCaptureCompare(channelA);
      uint32_t  Overflow = pwm_Tim2-> getOverflow();
      
      
      //for rising edge on pin PA1
      pwm_Tim2->setMode(2, TIMER_OUTPUT_COMPARE);
      pwm_Tim2->setCaptureCompare(2, (Overflow/2) -(Overflow - CCR), TICK_COMPARE_FORMAT);
      pwm_Tim2->attachInterrupt(2, ch2_comp_high_interrupt);

      
      //for falling edge on pin PA1
      pwm_Tim2->setMode(3, TIMER_OUTPUT_COMPARE);
      pwm_Tim2->setCaptureCompare(3, 50, PERCENT_COMPARE_FORMAT);
      pwm_Tim2->attachInterrupt(3, ch3_comp_low_interrupt);
     
     
      pwm_Tim2->refresh();
      
      //                 call to setup 1 pulse timer needs to be done after pwm_Tim2 as some variables must be read from pwm_Tim2
                                           
      
      Tim3Setup();//This funtion is right above void setup()

      
       
       
       
       


                               //End of Timer setup
      
      M_But.begin();
      L_But.begin();
      R_But.begin();
      AdjBut.begin();
      P_But.begin();

      
      Serial.begin(115200);

      displayFreqPulse();

}
   

void loop() {
if(P_But.pressed())
{
  if(pwmOneshot==1)
  {
    pwmOneshot = 0;
    displayFreqPulse();
  }
  if(pwmOneshot==0)
  {
    pwmOneshot = 1;
    displayOneShot();
  }
}

  if (pwmOneshot = 0)
  {
      if(oldEncPos != encoderPos)
       {
        switch(Inc)
         {
           case 0: enc = enc + (encoderPos-oldEncPos);break;
           case 1: enc = enc + ((encoderPos-oldEncPos) * 10);break;
           case 2: enc = enc + ((encoderPos-oldEncPos) * 100);break;
         }
         oldEncPos = encoderPos;
         displayFreqPulse();
       }


      if(L_But.pressed())
        {
          adjustFreq();
          displayFreqPulse();
        }
     if(R_But.pressed())
        {
          adjustDutyA();
          displayFreqPulse();
        }
     if (AdjBut.pressed())
        {
          setIncr();
          displayFreqPulse();
        }
     if (M_But.pressed())
        {
          enc = 0;
          freq = 10;
          adjustFreq();
          dutyA = 1;
          adjustDutyA();
          displayFreqPulse();
        }
   }
   else
   {
    if(oldEncPos != encoderPos)
       {if (Inc == 2)Inc--;
        switch(Inc)
         {
           case 0: enc = enc + (encoderPos-oldEncPos);break;// if these go above 100 we need to limit them
           case 1: enc = enc + ((encoderPos-oldEncPos) * 10);break;
           //case 2: enc = enc + ((encoderPos-oldEncPos) * 100);break;
         }
         oldEncPos = encoderPos;
         displayOneShot();
       }


      if(L_But.pressed())
        {
          Delay = enc;
          if (Delay + Pulse >100)Delay = 100 - Pulse;
          displayOneShot();
        }
     if(R_But.pressed())
        {
          Pulse = enc;
          if (Delay + Pulse >100)Pulse = 100 - Delay;
          displayOneShot();
        }
     if (AdjBut.pressed())
        {
          setIncr1S();
          displayOneShot();
        }
     if (M_But.pressed())
        {
          enc = 0;
          Delay = 50;
          Pulse = 0;
          Tim3Setup();
          displayOneShot();
        }
   }
}
Am going to get some sleep, I may figure it out after that if no one else has. Thank you.
altEnergy
Posts: 10
Joined: Sat Sep 25, 2021 1:02 pm

Re: PWM and OneShot on TIM2 and TIM3

Post by altEnergy »

Ok I get it, interrupts are good and bad but when there is nothing easier go with it!
Well I hope I left something of use to someone or two . . :?
ag123
Posts: 1655
Joined: Thu Dec 19, 2019 5:30 am
Answers: 24

Re: PWM and OneShot on TIM2 and TIM3

Post by ag123 »

I think generally the HardwareTimer API is modelled after generic 'square waves' signal train, flexibly with PWM and interrupts etc.
I've not dug into 'oneshot' mode as, more commonly, I used the timer interrupt call back to simply drive my codes. e.g. blink a led in a simple case.

I did notice that while i'm trying to write some 'oscilloscope' codes, the max i can get out of a h/w timer interrupt callback to read the ADC data register is 500 kHz on stm32f103. Any higher sample rates, and it stalls, give unpredictable results etc. I'd guess if you try to interface real external signals, this is just going to be harder/worse.
altEnergy
Posts: 10
Joined: Sat Sep 25, 2021 1:02 pm

Re: PWM and OneShot on TIM2 and TIM3

Post by altEnergy »

ag123 ... Thankyou for the reply and I found it all great feedback the 500khz is more than I need the way I am doing things with this application. That is why I switched from using a dds (ad9833 and ad9850) tried both good for speed but not so easy to get nice clear pulses at certain places in sync with each other ... when I was thinking analog using comparetors, It was hard to get the clean signal I wanted, and I started thinking capacitor resistor oneshot hence why I let the mcu one shot grab me so hard but realizing how precise the interrupts are at the frequency I am now using probably always under 400hz in this app, Although it does it all in oneshot code, I am sure the interrupts can get me better results faster which for now is what matters as I am deep in experimental stages. It is nice to know 500khz limit rather than 200 khz as I let myself be led to believe. I love the work that has been done on osciloscopes especially the stmOscope inspired by Ray Burnette I believe. I am trying to find a 2 channel scope to make from one of my bluepills as even my usb hanteck is big and clumbersome compared to a bluepill O scope even though there are times we need to read higher frequency. Thanks for sharing / keeping me sane .. might be a bit late for that but I hope you get my drift.
ag123
Posts: 1655
Joined: Thu Dec 19, 2019 5:30 am
Answers: 24

Re: PWM and OneShot on TIM2 and TIM3

Post by ag123 »

Among other things, one of the surprises that I seldom paid much attention to is radio waves ! (i.e. EMI)
they are particularly severe if rather long wires are involved (i used a 70cm wire to my temperature sensor probe).
viewtopic.php?p=8455#p8455
I'm quite 'shocked' initially with the huge variance observed and after much frustrating time, the conclusion is radio waves is literally measured on the ADC. This is probably made worse by the semiconductor temperature sensor (it is pretty much a diode with amplification). As it amplifies the diode forward voltages to provide temperature readings.

So if you are having external inputs, do lookout for those as well.

In addition, if you are driving *outputs*, and that your speeds are low enough to do timer interrupts.
Do review the ref manual e..g RM0008 about that capture/compare register. As it can be set on the fly, you can probably generate complicated waveforms with it. Say, driven by the timer interrupt, interrupt calls your code, and your code sets the capture/compare register(s). But I've not tried it, just that i postulated that it is possible.

Oh and get at least get 1 (or more) of those stm32f401/f411 or 'higher' f4 series. After messing with them and finding many advantages, I kind of 'upgraded' to it. that 'scope' picture and the temperature sensor experiment runs on the stm32f401. In particular, that 'scope' looks almost 'real', and pretty precise that comes straight from the stm32f401 ADC pa0, driven by HardwareTimer to read the ADC data register.
viewtopic.php?p=8455#p8455
altEnergy
Posts: 10
Joined: Sat Sep 25, 2021 1:02 pm

Re: PWM and OneShot on TIM2 and TIM3

Post by altEnergy »

I will take a look at the scope. I have a couple of adc chips that go up to 100Meg samples but I currently can not make a board with small enough traces to mount them. However my current project is all short wires at lower frequencies. The outputs are driving mosfet drivers that are all housed close by. The only reason I was going to wire the pin to another input is I could not figure out how to send timer 2's output to timer 3 so the wire would be about 2 - 3 cm but with your 70 cm wire would a ferite bead help near the mcu ? or may be an rc suppresor? I am just using my setup to experiment with bucking coils and resonance with coil shorting at various windows of the wave form, the main interference I could suffer is the mag feilds I am creating could interfere as they are close by but if that is the case I will just put some sheilding in place. I am still a bit frustrated that I can't get the oneshot working but I shouldn't be as I am almost certain I can achieve the same results with interrupts. I already have RM0008, AN4013 and AN4776 open in adobe reader but thanks for the good advice.
altEnergy
Posts: 10
Joined: Sat Sep 25, 2021 1:02 pm

Re: PWM and OneShot on TIM2 and TIM3

Post by altEnergy »

ag123, I have just noticed you linked twice to the temp sensor post, I guess when you copied the scope link it did not copy and you pasted the same link to the temp sensor lol
ag123
Posts: 1655
Joined: Thu Dec 19, 2019 5:30 am
Answers: 24

Re: PWM and OneShot on TIM2 and TIM3

Post by ag123 »

The 70 cm wire problem is that, this is exactly some radio frequencies at a full wavelength, half wavelength or quarter wavelength, so it is an antenna kind of. When it resonates and with the amplifier in the temperature sensor, the whole setup becomes a radio in addition to being just a temperature sensor. And I'd imagine that the temperature sensor being a diode, it'd demodulate that and present the signal to the ADC, an accidental SDR.

For now, that 'radio waves' problem is solved by placing a 1 nF capacitor between the ADC input & GND. This works as I'm pretty much using 'DC', except when it is rapidly heated or cooled. 1 nF is the max that the LMT86 temperature sensor can handle.
It reduced the variance significantly, but It did not completely remove the large outliers

But I'd guess in general the stm32 ADCs or in a way GPIO inputs are very sensitive and can pick up radio wave signals. I've seen those variances measuring resistor dividers (i.e. potentiometers) ! So it isn't just that 'temperature sensor' that can be affected.
---
back to the topic

It seemed in your case the problems are different, as you mentioned short wires are used.
I think in the ref manuals, it is mentioned it is kind of possible to 'daisy chain' timers. i.e. one timer feeding into another.
I've not tried those either, and I find that rather complicated to design for specific uses.

If frequencies are low as in your case, I'd use the timer interrupts and those 'capture/compare registers', those are the most fluid, and you can define nearly any waveforms you want using them I'd think. Those 'capture/compare registers' are provided to every timer channel, so if you have a common frequency, you can probably create elaborate different waveforms on each channel simply tweaking values in the 'capture/compare registers' for each channel, say driven by interrupts.
Post Reply

Return to “General discussion”