Problem with cycling though PWM outputs on timer
Posted: Mon Jan 20, 2020 11:33 pm
I'm getting some strange behaviour with this, that maybe somebody could help with please?
The purpose of the code is to cycle sequentially though 11 different configurations, with each configuration (I call it a crank position) calling for 3 out of 11 PWM channels to be activated. A different 3 channels for each crank position. I don't have much experience of using timers, or coding in general.
The behavior I am trying for is that the crank position should cycle through positions every 50ms then when it gets to position 11 should go back to position 1, so a cycle time of 550ms. The problem is that I am not getting PWM on the last 3 channels (8,9,10), so not reaching Crank Pos 7. Even though if I put a print statement in the relevant case I can confirm that the case is being executed.
I checked things in general before introducing the timers and Crank feature by just pulsing each channel simultaneously and attaching to LEDs and that worked fine, so the hardware is all ok.
I don't have a scope unfortunately for any debug.
The code:
The purpose of the code is to cycle sequentially though 11 different configurations, with each configuration (I call it a crank position) calling for 3 out of 11 PWM channels to be activated. A different 3 channels for each crank position. I don't have much experience of using timers, or coding in general.
The behavior I am trying for is that the crank position should cycle through positions every 50ms then when it gets to position 11 should go back to position 1, so a cycle time of 550ms. The problem is that I am not getting PWM on the last 3 channels (8,9,10), so not reaching Crank Pos 7. Even though if I put a print statement in the relevant case I can confirm that the case is being executed.
I checked things in general before introducing the timers and Crank feature by just pulsing each channel simultaneously and attaching to LEDs and that worked fine, so the hardware is all ok.
I don't have a scope unfortunately for any debug.
The code:
- // initiallise the timer conditions
- hw_timer_t * timer = NULL;
- hw_timer_t * timer2 = NULL;
- volatile bool gGetADCflag;
- volatile bool gCrankPosChangeflag;
- portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED;
- // PWMs are for 11 channel individual Mosfets
- // setting things up for PWM out - use first channel of 16 channels (started from zero)
- #define LEDC_CHANNEL_0 0
- #define LEDC_CHANNEL_1 1
- #define LEDC_CHANNEL_2 2
- #define LEDC_CHANNEL_3 3
- #define LEDC_CHANNEL_4 4
- #define LEDC_CHANNEL_5 5
- #define LEDC_CHANNEL_6 6
- #define LEDC_CHANNEL_7 7
- #define LEDC_CHANNEL_8 8
- #define LEDC_CHANNEL_9 9
- #define LEDC_CHANNEL_10 10
- // use 13 bit precission for LEDC timer
- #define LEDC_TIMER_13_BIT 13
- // use 5000 Hz as a LEDC base frequency
- #define LEDC_BASE_FREQ 25000 // max frequency for IBT-2 is 25kHz
- // fade LED PIN (replace with LED_BUILTIN constant for built-in LED)
- #define PWM1 21
- #define PWM2 32 // was 1
- #define PWM3 4 // was 3
- #define PWM4 22
- #define PWM5 19
- #define PWM6 23
- #define PWM7 18
- #define PWM8 5
- #define PWM9 15
- #define PWM10 25
- #define PWM11 33
- unsigned long durationNow = 0;
- unsigned long previous = 0;
- int PWMVal = 120; // 0-255
- int analog_value = 0; // 0-4095
- int outputValue = 0; // value output to the PWM (analog out)
- int rateOfChange = 0; // Capture the rate of change of the input at each time step (in theory could be a deimal but if <1 then 0 is fine)
- int rateLimit = 50; // ADC increments
- int oldDemand = 0;
- int newDemand = 0;
- int mAmp = 0;
- String gcommand;
- int gDuty=0;
- int gCrankPos = 1; // initialisation of the crank position (1-11 variable)
- // timer related functions
- void ADCTimer(){
- gGetADCflag = true;
- }
- void startADCTimer() {
- timer = timerBegin(0, 80, true); // timer_id = 0; divider=80; countUp = true;
- timerAttachInterrupt(timer, &ADCTimer, true); // edge = true
- timerAlarmWrite(timer, 1000, true); //1 ms
- timerAlarmEnable(timer);
- }
- void endADCTimer() {
- timerEnd(timer);
- timer = NULL;
- }
- void crankTimer(){
- gCrankPosChangeflag = true;
- }
- void startCrankTimer() {
- timer2 = timerBegin(1, 80, true); // timer_id = 1; divider=80; countUp = true;
- timerAttachInterrupt(timer2, &crankTimer, true); // edge = true
- timerAlarmWrite(timer2, 50000, true); //50 ms (0.5s)
- timerAlarmEnable(timer2);
- }
- void endCrankTimer() {
- timerEnd(timer2);
- timer2 = NULL;
- }
- void incCrankPos(){
- // anything in here that involves shifting around the 11 channel postions
- Serial.print("Crank Pos: ");
- Serial.println(gCrankPos);
- if (gCrankPos >= 11) gCrankPos = 0;
- gCrankPos++; //
- gCrankPosChangeflag = false;
- }
- /*
- // the function that get's Called once the Timer Interrupt flag is True
- void getADC() {
- // do suff in here that runs outside of the ISR but is triggered by the ISR
- // the timer should be set so that it triggers 10x faster than the data tare you want to see, as it will be averaged then thrown away every 10 cycles
- // Note that this simplified approach will introduce a phase shift or delay to the figures, as the average figure is being reported against the final sample timestamp. a rolling average should be used if needs to be avoided
- // any variables that need to not be reset each time the function is called need to be declared as static
- static unsigned int counter = 1;
- static int rawInput[NOOFAVERAGES] = {0}; // array for storing the 10 values
- static int aveADC = 0; // variable for storing the potentiometer value
- static int sumInput = 0; // variable to hold the sum of inputs prior to averaging
- static int current1 = 0; // convert ADC counts to current mA, with offset and multiplier
- static int offset1 = -210; // ADC offset for 1st ACS725 sensor
- rawInput[counter-1] = analogRead(ANALOG_PIN_1);
- sumInput = sumInput + rawInput[counter-1];
- // take the NOOFAVERAGES readings from the Array and average them
- if (counter == NOOFAVERAGES){
- aveADC = sumInput/NOOFAVERAGES; // add up and divide by 10
- current1 = (aveADC+offset1)*1000/300; // Convert ADC to mA, 264mV/A - chaged to 300mV/A
- Serial.print("ADCTimer ");
- Serial.print(counter);
- Serial.print(" at ");
- Serial.print(millis());
- Serial.print(" ms ");
- Serial.print(" ADC value ");
- Serial.print(aveADC);
- Serial.print(" Current(mA) ");
- Serial.println(current1);
- sumInput = 0;
- counter = 0;
- }
- gGetADCflag = false;
- counter++;
- }
- */
- // Arduino like analogWrite
- // value has to be between 0 and valueMax
- void ledcAnalogWrite(uint8_t channel, uint32_t value, uint32_t valueMax = 255) {
- // calculate duty, 8191 from 2 ^ 13 - 1
- uint32_t duty = (8191 / valueMax) * min(value, valueMax);
- // write duty to LEDC
- ledcWrite(channel, duty);
- }
- void setup() {
- delay(1000);
- Serial.begin(115200);
- delay(1000);
- // SerialBT.begin("ESP32hula"); //Bluetooth device name
- Serial.println("The device started, now you can pair it with bluetooth!");
- // Setup timer and attach timer to a pwm pin
- ledcSetup(LEDC_CHANNEL_0, LEDC_BASE_FREQ, LEDC_TIMER_13_BIT);
- ledcSetup(LEDC_CHANNEL_1, LEDC_BASE_FREQ, LEDC_TIMER_13_BIT);
- ledcSetup(LEDC_CHANNEL_2, LEDC_BASE_FREQ, LEDC_TIMER_13_BIT);
- ledcSetup(LEDC_CHANNEL_3, LEDC_BASE_FREQ, LEDC_TIMER_13_BIT);
- ledcSetup(LEDC_CHANNEL_4, LEDC_BASE_FREQ, LEDC_TIMER_13_BIT);
- ledcSetup(LEDC_CHANNEL_5, LEDC_BASE_FREQ, LEDC_TIMER_13_BIT);
- ledcSetup(LEDC_CHANNEL_6, LEDC_BASE_FREQ, LEDC_TIMER_13_BIT);
- ledcSetup(LEDC_CHANNEL_7, LEDC_BASE_FREQ, LEDC_TIMER_13_BIT);
- ledcSetup(LEDC_CHANNEL_8, LEDC_BASE_FREQ, LEDC_TIMER_13_BIT);
- ledcSetup(LEDC_CHANNEL_9, LEDC_BASE_FREQ, LEDC_TIMER_13_BIT);
- ledcSetup(LEDC_CHANNEL_10, LEDC_BASE_FREQ, LEDC_TIMER_13_BIT);
- ledcAttachPin(PWM1, LEDC_CHANNEL_0);
- ledcAttachPin(PWM2, LEDC_CHANNEL_1);
- ledcAttachPin(PWM3, LEDC_CHANNEL_2);
- ledcAttachPin(PWM4, LEDC_CHANNEL_3);
- ledcAttachPin(PWM5, LEDC_CHANNEL_4);
- ledcAttachPin(PWM6, LEDC_CHANNEL_5);
- ledcAttachPin(PWM7, LEDC_CHANNEL_6);
- ledcAttachPin(PWM8, LEDC_CHANNEL_7);
- ledcAttachPin(PWM9, LEDC_CHANNEL_8);
- ledcAttachPin(PWM10, LEDC_CHANNEL_9);
- ledcAttachPin(PWM11, LEDC_CHANNEL_10);
- // pinMode(ANALOG_PIN_0, INPUT);
- // pinMode(ANALOG_PIN_1, INPUT);
- // analogSetPinAttenuation(ANALOG_PIN_1, ADC_11db); // for ESP32 max vooltages are 0db = 1.1v, 2.5db = 1.4v, 6db = 1.9v, 11db = 3.2v, representing 4095 ADC (12bit)
- Serial.println("pin modes set");
- startADCTimer();
- startCrankTimer();
- delay(1000);
- Serial.println("exiting setup");
- }
- /*
- void parseCommand (String com) {
- String part1;
- String part2;
- // split into 2 parts. 1st part will be a letter, either an override status or a motor speed change. 2nd part will be either override on/off or motor speed 0-100.
- part1 = com.substring(0,1);
- part2 = com.substring(1,com.indexOf("."));
- // Serial.print("part1= ");
- // Serial.println(part1);
- // Serial.print("part2= ");
- // Serial.println(part2);
- if (part1.equalsIgnoreCase("o")){
- gOverrideStatus = part2.toInt(); // change the global override status to either 1 or 0;
- // Serial.print("gOverride status changed to ");
- // Serial.println(gOverrideStatus);
- }
- else if(part1.equalsIgnoreCase("v")){
- if (gOverrideStatus == 1){
- gDuty = part2.toInt();
- // Serial.print("duty (%) changed to ");
- // Serial.println(gDuty);
- }
- }
- else{
- part1="";
- part2="";
- gcommand="";
- }
- }
- */
- /*
- int rateLimiter(int latestValue){
- durationNow = millis();
- // check the input value:
- int sensorValue = latestValue;
- newDemand = sensorValue;
- rateOfChange = (newDemand-oldDemand)/(durationNow-previous);
- if (rateOfChange > rateLimit)
- {
- if (newDemand>oldDemand)
- {
- newDemand = oldDemand+rateLimit;
- }
- if (newDemand<=oldDemand)
- {
- newDemand = oldDemand-rateLimit;
- }
- }
- else
- {
- newDemand = newDemand;
- }
- //reset for next loop
- oldDemand = newDemand;
- previous = durationNow;
- return newDemand;
- }
- */
- void loop() {
- // Reading ADC value (current sense input)
- if (gGetADCflag == true) {
- // Serial.println("ADCFlag = TRUE");
- // getADC();
- }
- if (gCrankPosChangeflag == true) {
- incCrankPos();
- }
- // check motor speed is within acceptable boundaries and limit
- if (PWMVal <= 0){
- PWMVal = 0;
- }
- if (PWMVal >120){
- PWMVal = 120; // set to be approx 2A max (true maximum 255, but would result in peak current draw)
- }
- // call PWM funtions for each coil
- // use switch cases depeding on crank position
- switch (gCrankPos) {
- case 1:
- ledcAnalogWrite(LEDC_CHANNEL_0, PWMVal);
- ledcAnalogWrite(LEDC_CHANNEL_1, PWMVal);
- ledcAnalogWrite(LEDC_CHANNEL_2, PWMVal);
- ledcAnalogWrite(LEDC_CHANNEL_3, 0);
- ledcAnalogWrite(LEDC_CHANNEL_4, 0);
- ledcAnalogWrite(LEDC_CHANNEL_5, 0);
- ledcAnalogWrite(LEDC_CHANNEL_6, 0);
- ledcAnalogWrite(LEDC_CHANNEL_7, 0);
- ledcAnalogWrite(LEDC_CHANNEL_8, 0);
- ledcAnalogWrite(LEDC_CHANNEL_9, 0);
- ledcAnalogWrite(LEDC_CHANNEL_10, 0);
- break;
- case 2:
- ledcAnalogWrite(LEDC_CHANNEL_0, 0);
- ledcAnalogWrite(LEDC_CHANNEL_1, PWMVal);
- ledcAnalogWrite(LEDC_CHANNEL_2, PWMVal);
- ledcAnalogWrite(LEDC_CHANNEL_3, PWMVal);
- ledcAnalogWrite(LEDC_CHANNEL_4, 0);
- ledcAnalogWrite(LEDC_CHANNEL_5, 0);
- ledcAnalogWrite(LEDC_CHANNEL_6, 0);
- ledcAnalogWrite(LEDC_CHANNEL_7, 0);
- ledcAnalogWrite(LEDC_CHANNEL_8, 0);
- ledcAnalogWrite(LEDC_CHANNEL_9, 0);
- ledcAnalogWrite(LEDC_CHANNEL_10, 0);
- break;
- case 3:
- ledcAnalogWrite(LEDC_CHANNEL_0, 0);
- ledcAnalogWrite(LEDC_CHANNEL_1, 0);
- ledcAnalogWrite(LEDC_CHANNEL_2, PWMVal);
- ledcAnalogWrite(LEDC_CHANNEL_3, PWMVal);
- ledcAnalogWrite(LEDC_CHANNEL_4, PWMVal);
- ledcAnalogWrite(LEDC_CHANNEL_5, 0);
- ledcAnalogWrite(LEDC_CHANNEL_6, 0);
- ledcAnalogWrite(LEDC_CHANNEL_7, 0);
- ledcAnalogWrite(LEDC_CHANNEL_8, 0);
- ledcAnalogWrite(LEDC_CHANNEL_9, 0);
- ledcAnalogWrite(LEDC_CHANNEL_10, 0);
- break;
- case 4:
- ledcAnalogWrite(LEDC_CHANNEL_0, 0);
- ledcAnalogWrite(LEDC_CHANNEL_1, 0);
- ledcAnalogWrite(LEDC_CHANNEL_2, 0);
- ledcAnalogWrite(LEDC_CHANNEL_3, PWMVal);
- ledcAnalogWrite(LEDC_CHANNEL_4, PWMVal);
- ledcAnalogWrite(LEDC_CHANNEL_5, PWMVal);
- ledcAnalogWrite(LEDC_CHANNEL_6, 0);
- ledcAnalogWrite(LEDC_CHANNEL_7, 0);
- ledcAnalogWrite(LEDC_CHANNEL_8, 0);
- ledcAnalogWrite(LEDC_CHANNEL_9, 0);
- ledcAnalogWrite(LEDC_CHANNEL_10, 0);
- break;
- case 5:
- ledcAnalogWrite(LEDC_CHANNEL_0, 0);
- ledcAnalogWrite(LEDC_CHANNEL_1, 0);
- ledcAnalogWrite(LEDC_CHANNEL_2, 0);
- ledcAnalogWrite(LEDC_CHANNEL_3, 0);
- ledcAnalogWrite(LEDC_CHANNEL_4, PWMVal);
- ledcAnalogWrite(LEDC_CHANNEL_5, PWMVal);
- ledcAnalogWrite(LEDC_CHANNEL_6, PWMVal);
- ledcAnalogWrite(LEDC_CHANNEL_7, 0);
- ledcAnalogWrite(LEDC_CHANNEL_8, 0);
- ledcAnalogWrite(LEDC_CHANNEL_9, 0);
- ledcAnalogWrite(LEDC_CHANNEL_10, 0);
- break;
- case 6:
- ledcAnalogWrite(LEDC_CHANNEL_0, 0);
- ledcAnalogWrite(LEDC_CHANNEL_1, 0);
- ledcAnalogWrite(LEDC_CHANNEL_2, 0);
- ledcAnalogWrite(LEDC_CHANNEL_3, 0);
- ledcAnalogWrite(LEDC_CHANNEL_4, 0);
- ledcAnalogWrite(LEDC_CHANNEL_5, PWMVal);
- ledcAnalogWrite(LEDC_CHANNEL_6, PWMVal);
- ledcAnalogWrite(LEDC_CHANNEL_7, PWMVal);
- ledcAnalogWrite(LEDC_CHANNEL_8, 0);
- ledcAnalogWrite(LEDC_CHANNEL_9, 0);
- ledcAnalogWrite(LEDC_CHANNEL_10, 0);
- break;
- case 7:
- ledcAnalogWrite(LEDC_CHANNEL_0, 0);
- ledcAnalogWrite(LEDC_CHANNEL_1, 0);
- ledcAnalogWrite(LEDC_CHANNEL_2, 0);
- ledcAnalogWrite(LEDC_CHANNEL_3, 0);
- ledcAnalogWrite(LEDC_CHANNEL_4, 0);
- ledcAnalogWrite(LEDC_CHANNEL_5, 0);
- ledcAnalogWrite(LEDC_CHANNEL_6, PWMVal);
- ledcAnalogWrite(LEDC_CHANNEL_7, PWMVal);
- ledcAnalogWrite(LEDC_CHANNEL_8, PWMVal);
- ledcAnalogWrite(LEDC_CHANNEL_9, 0);
- ledcAnalogWrite(LEDC_CHANNEL_10, 0);
- break;
- case 8:
- ledcAnalogWrite(LEDC_CHANNEL_0, 0);
- ledcAnalogWrite(LEDC_CHANNEL_1, 0);
- ledcAnalogWrite(LEDC_CHANNEL_2, 0);
- ledcAnalogWrite(LEDC_CHANNEL_3, 0);
- ledcAnalogWrite(LEDC_CHANNEL_4, 0);
- ledcAnalogWrite(LEDC_CHANNEL_5, 0);
- ledcAnalogWrite(LEDC_CHANNEL_6, 0);
- ledcAnalogWrite(LEDC_CHANNEL_7, PWMVal);
- ledcAnalogWrite(LEDC_CHANNEL_8, PWMVal);
- ledcAnalogWrite(LEDC_CHANNEL_9, PWMVal);
- ledcAnalogWrite(LEDC_CHANNEL_10, 0);
- break;
- case 9:
- ledcAnalogWrite(LEDC_CHANNEL_0, 0);
- ledcAnalogWrite(LEDC_CHANNEL_1, 0);
- ledcAnalogWrite(LEDC_CHANNEL_2, 0);
- ledcAnalogWrite(LEDC_CHANNEL_3, 0);
- ledcAnalogWrite(LEDC_CHANNEL_4, 0);
- ledcAnalogWrite(LEDC_CHANNEL_5, 0);
- ledcAnalogWrite(LEDC_CHANNEL_6, 0);
- ledcAnalogWrite(LEDC_CHANNEL_7, 0);
- ledcAnalogWrite(LEDC_CHANNEL_8, PWMVal);
- ledcAnalogWrite(LEDC_CHANNEL_9, PWMVal);
- ledcAnalogWrite(LEDC_CHANNEL_10, PWMVal);
- break;
- case 10:
- ledcAnalogWrite(LEDC_CHANNEL_0, PWMVal);
- ledcAnalogWrite(LEDC_CHANNEL_1, 0);
- ledcAnalogWrite(LEDC_CHANNEL_2, 0);
- ledcAnalogWrite(LEDC_CHANNEL_3, 0);
- ledcAnalogWrite(LEDC_CHANNEL_4, 0);
- ledcAnalogWrite(LEDC_CHANNEL_5, 0);
- ledcAnalogWrite(LEDC_CHANNEL_6, 0);
- ledcAnalogWrite(LEDC_CHANNEL_7, 0);
- ledcAnalogWrite(LEDC_CHANNEL_8, 0);
- ledcAnalogWrite(LEDC_CHANNEL_9, PWMVal);
- ledcAnalogWrite(LEDC_CHANNEL_10, PWMVal);
- break;
- case 11:
- ledcAnalogWrite(LEDC_CHANNEL_0, PWMVal);
- ledcAnalogWrite(LEDC_CHANNEL_1, PWMVal);
- ledcAnalogWrite(LEDC_CHANNEL_2, 0);
- ledcAnalogWrite(LEDC_CHANNEL_3, 0);
- ledcAnalogWrite(LEDC_CHANNEL_4, 0);
- ledcAnalogWrite(LEDC_CHANNEL_5, 0);
- ledcAnalogWrite(LEDC_CHANNEL_6, 0);
- ledcAnalogWrite(LEDC_CHANNEL_7, 0);
- ledcAnalogWrite(LEDC_CHANNEL_8, 0);
- ledcAnalogWrite(LEDC_CHANNEL_9, 0);
- ledcAnalogWrite(LEDC_CHANNEL_10, PWMVal);
- break;
- default:
- // if nothing else matches, do the default
- ledcAnalogWrite(LEDC_CHANNEL_0, 0);
- ledcAnalogWrite(LEDC_CHANNEL_1, 0);
- ledcAnalogWrite(LEDC_CHANNEL_2, 0);
- ledcAnalogWrite(LEDC_CHANNEL_3, 0);
- ledcAnalogWrite(LEDC_CHANNEL_4, 0);
- ledcAnalogWrite(LEDC_CHANNEL_5, 0);
- ledcAnalogWrite(LEDC_CHANNEL_6, 0);
- ledcAnalogWrite(LEDC_CHANNEL_7, 0);
- ledcAnalogWrite(LEDC_CHANNEL_8, 0);
- ledcAnalogWrite(LEDC_CHANNEL_9, 0);
- ledcAnalogWrite(LEDC_CHANNEL_10, 0);
- break;
- }
- }