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();
}
}
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.