PWM output not as expected

Bodger62

New member
Hello,

I am trying to control a PWM signal that runs at 50Hz, with the aim of using it for automatic testing. A file will be provided on an SD card that shows the duration of the PWM signal in milliseconds, and the duty cycle as a number between 1000 and 2000, representing 1mS and 2mS, for each test.

It seems to work more or less okay apart from when it first starts.

If I have the following instructions in test.txt
40, 1000,
4000, 2000
4000, 1000

I would expect to get two PWM cycles with 1mS signal, followed by four seconds worth of PWM with a duty cycle at 2mS etc. However, whenever the code starts to run I only get one PWM cycle at 1mS as shown below.

NewFile1.png


I would be extremely grateful if someone could show me the error of my ways.

regards
Bodger

Code:
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <time.h>

#include <SD.h>
#include <SPI.h>

#include <PWMServo.h>

PWMServo myservo;  // create servo object to control a servo


#define LED LED_BUILTIN      /* Set LED pin number */

#define PIN_LOW   0
#define PIN_HIGH  1

#define RESET_COUNT_MS    500U
#define ONE_SECOND        1000

// Create an IntervalTimer object
IntervalTimer myTimer;

// Create an IntervalTimer object
IntervalTimer myTimer_2;

bool f_once;

uint8_t  ledState;

uint8_t  PWM_time_on;
uint8_t  PWM_state;
uint8_t  pin_state;

uint32_t waiting;
uint32_t waiting_old;
uint32_t loop_count;
uint32_t debounce_count;

uint64_t SDcard_mem_used;
uint64_t SDcard_total_mem;
uint64_t SDcard_mem_avail;

File myFile;

//---------------------------------------------------------
void Initialise_Variables(void)
{
  waiting     = 0U;
  waiting_old = 0U;
  loop_count  = 0U;
 
  debounce_count = 0U;

  ledState = LOW;

  f_once = true;
}

//---------------------------------------------------------
void Write_PWM_Data(int step_time, int PWM_Value)
{
  float f_value;

  elapsedMillis waiting;

    if (PWM_Value <= 1000)
    {
      f_value = 1;  //force the output to be 1mS
    }
    else
    {
      PWM_Value = PWM_Value - 1000;
      f_value = (float)PWM_Value * 0.18;  //Scale the output
    }

//#ifdef DEBUG_PRINT
    Serial.printf("\r\nT = %d D = %d ", (int)step_time, (int)f_value);
//#endif

    myservo.write((int)f_value);              // tell servo to go to position in variable 'pos'

    while (waiting < (unsigned long)step_time)
    {}
}

//----------------------------------------------------------
bool readLine(File &f, char* line, size_t maxLen) {

  for (size_t n = 0; n < maxLen; n++) {
    int c = f.read();
    if ( c < 0 && n == 0) return false;  // EOF
    if (c < 0 || c == '\n') {
      line[n] = 0;
      return true;
    }
    line[n] = c;
  }
  return false; // line too long
}

//---------------------------------------------------------
void Read_File(void)
{
    char line[100];
    char *sp;
    int step_time;
    int PWM_duty_time;

    int line_count = 0;
  while (myFile.available())
  {
    if (!readLine(myFile, line, sizeof(line)))
    {
      Serial.println("EOF or too long");  // EOF or too long   
    }
    else
    {
      sp = strtok(line, ",");
      
      if (sp != NULL)
      {
          step_time = atoi(sp);

#ifdef DEBUG_PRINT
          Serial.printf("\Time = %d", step_time);
#endif
          sp = strtok(NULL, ",");
          if (sp != NULL)
          {
            PWM_duty_time = atoi(sp);

#ifdef DEBUG_PRINT
            Serial.printf("  PWM_duty_time = %d", PWM_duty_time);
#endif
            Write_PWM_Data(step_time, PWM_duty_time);                   
          }
          else
          {
#ifdef DEBUG_PRINT
            Serial.printf("\r\nfo");
#endif
          }
          
          line_count++;
      }
      else
      {
          Serial.printf("something wrong! \n");
      }
#ifdef DEBUG_PRINT
      Serial.printf("\nline count %d", line_count);
#endif
    }
  }
  myFile.close();

}

//----------------------------------------------------------
void blinkLED()
{
  if (ledState == LOW)
  {
    ledState = HIGH;
  } else
  {
    ledState = LOW;
  }
  digitalWrite(LED, ledState);
}

//---------------------------------------------------------
void Check_for_Reset(uint32_t time_ms)
{
  pin_state = digitalRead(PIN_A2);

  if (pin_state == PIN_LOW)
  {
    if (debounce_count == RESET_COUNT_MS)
    {
#ifdef DEBUG_PRINT
      Serial.println("Reset count reached \r\n");
#endif
    }
    else
    {
      debounce_count++;
    }
  }
  else
  {
    if (debounce_count == RESET_COUNT_MS)
    {
      if (pin_state == PIN_HIGH)       //Wait for pin release
      {
        Serial.println("Reset here");
        delay(ONE_SECOND);
        SRC_GPR5 = 0x0BAD00F1;
        SCB_AIRCR = 0x05FA0004;
      }
    }
    else
    {
      debounce_count = 0U;
    }
  }
}

//---------------------------------------------------------
void Wait_for_Start(void)
{
  myTimer.begin(blinkLED, 250000);  // blinkLED to run every 0.15 seconds

  while (true)
  {
     pin_state = digitalRead(PIN_A2);

    //Check the initial state
    if (pin_state == PIN_HIGH)
    {
      if (f_once)
      {
        f_once = false;
        Serial.printf("\r\npin state currently HIGH \r\n");
      }
    }
    else
    {
      Serial.printf("\r\npin state currently LOW \r\n");
      break;
    }
  }
  myTimer.end();
  digitalWrite(LED, LOW);
 
}

//---------------------------------------------------------
void setup()
{
  elapsedMillis waiting;

  Serial.begin(115200); 
 
  delay(400);

  // put your setup code here, to run once:
  Serial.println("\r\nVersion Number V_1.0.0.A \r\n");

  pinMode ( LED, OUTPUT );                /* Setup the LED */
  pinMode (3, OUTPUT);
  pinMode (PIN_A2, INPUT_PULLUP);

  if (!SD.begin(BUILTIN_SDCARD))
  {
    Serial.println(("SD initialization failed!"));

      myTimer_2.begin(blinkLED, 2000000);  // blinkLED to run every 0.15 seconds

      while (1)
      {loop_count++;}
  }
  else
  {
    Serial.println(("SD initialization complete. \r\n"));

    // open the file.
    myFile = SD.open("test.txt", FILE_READ);

    if (myFile)
    {
      Serial.println("File (test.csv) found");       

      SDcard_total_mem = SD.totalSize();
      SDcard_mem_used  = SD.usedSize();
      SDcard_mem_avail = SDcard_total_mem - SDcard_mem_used;

      Serial.printf("\r\n Total mem %llu   Used mem %llu \r\n", SDcard_total_mem, SDcard_mem_used);
      Serial.printf("\r\n Total mem avail %llu  0x%llx   \r\n", SDcard_mem_avail, SDcard_mem_avail);

    }
    else
    {
      Serial.println("File (test.csv) not found");       

      myTimer_2.begin(blinkLED, 3000000);  // blinkLED to run every 3 seconds

      while (true)
      {loop_count++;}   //give the processor something to do
    }
  }

  Initialise_Variables();

  Wait_for_Start();

  myservo.attach(SERVO_PIN_A, 1000, 2000); // some motors need min/max setting

  delay(500);   //let the servo settle

  Read_File();  //read the file and set servo accordingly

}

//---------------------------------------------------------
void loop()
{

  waiting = millis();

  if (waiting != waiting_old)
  {
    loop_count++;
    waiting_old = waiting;
    Check_for_Reset(waiting);
  }

  if (loop_count == ONE_SECOND)
  {
    loop_count = 0U;
    digitalToggle(LED);
  }
}
 

Attachments

  • test.txt
    12 KB · Views: 10
  • sweep_all.ino
    6.3 KB · Views: 9
  • NewFile1.png
    NewFile1.png
    34.8 KB · Views: 4
In your Write_PWM_Data function you have:
Code:
    while (waiting < (unsigned long)step_time)
    {}
"waiting" is type ElapsedMillis, so if step_time is 1000, it's going to wait 1000 ms (1 sec). Is that what you intended, or did you mean to use ElapsedMicros? Also, you should initialize waiting = 0. Local variables are not automatically initialized.
 
OK thanks.

The test duration times from the file are 40mS, 4000mS and 4000mS, while the duty cycles required at those test durations are 1mS, 2mS and 1mS.

From the scope trace you can see there is only one PWM at 1mS, and I think there should be two.
 
I think you only get one because you are stuck in the “waiting” loop. The units of step-time are MICROseconds, not milliseconds.
 

Timeout Waiting For Input​

void loop() { // each time loop() runs,
elapsedMillis waiting; // "waiting" starts at zero
while (waiting < 1000) {
if (Serial.available()) {
char c = Serial.read();
Serial.print("got char = "); // do something with c
Serial.println(c);
waiting = 0; // reset waiting back to zero
}
}
Serial.println("waited 1 second, no data arrived");
}

When elapsedMillis is created as a local variable (inside the function), it automatically starts at zero each time the function executes.

From this snippet of code you can see that the local variable is set to 0 at the start, and the time expected is in milliseconds, as the count is set to 1000.

 
Back
Top