J.Bryan
Posts: 42
Joined: Tue Aug 01, 2017 2:44 pm

Hardware PWM Pin 32 and Pin 33 Question

Fri Jan 12, 2018 1:40 pm

Dear All,

I am trying to use the pigpio library to generate PWM signal on the Hardware PWM Pin 32 and 33 of the Raspberry Pi GPIO header.

I was referring to this thread:
viewtopic.php?f=33&t=179438

I saw that it can be done using:

gpioHardwarePWM(12, 5000, 500000);
gpioHardwarePWM(13, 10000, 100000);

I would like to check do I need to set the pin to use "Alternate function" or is it already done by the function "gpioHardwarePWM"?

I referred to the documentation but is not really understanding how "gpioHardwarePWM" function work.

The function header is listed as:
int gpioHardwarePWM(unsigned gpio, unsigned PWMfreq, unsigned PWMduty)

Does "PWMfreq" refers to the maximum frequency of the connected device? I am looking at the L298N motor board and would like to control the Speed of the connected DC Motor. For the "PWMduty", is the range always at 0 to 1000000 regardless of the "PWMfreq"?

Am I correct to assume that 1000000 refers to 100% duty cycle. So if I want a 70% duty cycle I can set e.g pin 32 to 700000 example:
gpioHardwarePWM(12, 10000, 700000);
gpioHardwarePWM(13, 10000, 700000);

If I am wrong, may I get some reference to a working example piece of c code that controls Motor Speed with the Hardware PWM pin?

Thanks for your help.

User avatar
joan
Posts: 13559
Joined: Thu Jul 05, 2012 5:09 pm
Location: UK

Re: Hardware PWM Pin 32 and Pin 33 Question

Fri Jan 12, 2018 1:48 pm

Your assumptions are correct. The function will set the GPIO to the correct mode. The duty cycle is out of one million, so 500 thousand will be 50%. The frequency is the PWM frequency you want. Mostly frequency is not critical for DC motor control. Anything around 1000 should be fine.

mattmiller
Posts: 1953
Joined: Thu Feb 05, 2015 11:25 pm

Re: Hardware PWM Pin 32 and Pin 33 Question

Fri Jan 12, 2018 1:54 pm

Anything around 1000 should be fine.
My personal experience with using motors on robots is that you seem to loose a lot of torque at high frequencies

My default value is 50Hz

I go as low as 10Hz when trying to move robot motors very slowly in order to get them to turn at all

J.Bryan
Posts: 42
Joined: Tue Aug 01, 2017 2:44 pm

Re: Hardware PWM Pin 32 and Pin 33 Question

Fri Jan 12, 2018 2:05 pm

Hi Joan,

Thanks for your reply! I'm not really getting on how to select the value for the frequency. From the datasheet, I saw that the maximum frequency of the l298n is 40KHz. Does it mean that it is okay to set the frequency to be any frequency below 40KHz? I understand theoretically there is difference in the time between setting 1KHz or 25KHz but not really sure how the selected frequency value will affect my application?

For most Arduino delicated motor shield board, there is a way to set the speed of the motor e.g -400 to 400. Am I achieving similar effect by varying the duty cycle?

I am sorry for the novice question.

Thanks for your help!

1dot0
Posts: 430
Joined: Mon Nov 28, 2016 12:31 pm

Re: Hardware PWM Pin 32 and Pin 33 Question

Fri Jan 12, 2018 2:19 pm

I am using DC motors by pwm frequencies from about 1kHz up to 25 kHz - all are fine.
Admittedly, for lower frequencies there might be increasingly audible beeps occuring.
On the Pi I used soft pwm generated by wiringPi, additionally I am using add-on shields which have built-in AVR or ARM cpus, no issues about either one.

User avatar
joan
Posts: 13559
Joined: Thu Jul 05, 2012 5:09 pm
Location: UK

Re: Hardware PWM Pin 32 and Pin 33 Question

Fri Jan 12, 2018 2:37 pm

@J.Bryan Use the duty cycle to vary the speed. Think of the duty cycle as setting the voltage between 0 and maximum. The Arduino interface seems to be setting the direction as well as speed.

User avatar
joan
Posts: 13559
Joined: Thu Jul 05, 2012 5:09 pm
Location: UK

Re: Hardware PWM Pin 32 and Pin 33 Question

Fri Jan 12, 2018 2:44 pm

mattmiller wrote:
Fri Jan 12, 2018 1:54 pm
Anything around 1000 should be fine.
My personal experience with using motors on robots is that you seem to loose a lot of torque at high frequencies

My default value is 50Hz

I go as low as 10Hz when trying to move robot motors very slowly in order to get them to turn at all
I only have limited experience of PWM with a few motor driver boards / motors. I think Arduino defaults to something like 500Hz which is partly why pigpio defaults to 800Hz.

1dot0
Posts: 430
Joined: Mon Nov 28, 2016 12:31 pm

Re: Hardware PWM Pin 32 and Pin 33 Question

Fri Jan 12, 2018 3:39 pm

Most Arduino pwm outputs are set to about 490Hz or 980Hz by default. The frequencies are generated as a result of the clock frequency being divided by a pre-scaler. One may also set the pre-scaler arbitrarily. Note that "Arduino" may relate to AVR cpus at 8-16MHz and ARM cpus from 48-84 MHz (and perhaps even Intel cpus).

User avatar
joan
Posts: 13559
Joined: Thu Jul 05, 2012 5:09 pm
Location: UK

Re: Hardware PWM Pin 32 and Pin 33 Question

Fri Jan 12, 2018 3:51 pm

1dot0 wrote:
Fri Jan 12, 2018 3:39 pm
Most Arduino pwm outputs are set to about 490Hz or 980Hz by default. The frequencies are generated as a result of the clock frequency being divided by a pre-scaler. One may also set the pre-scaler arbitrarily. Note that "Arduino" may relate to AVR cpus at 8-16MHz and ARM cpus from 48-84 MHz.
Thanks. The Arduino experience suggests that those values are reasonable for a lot of uses.

J.Bryan
Posts: 42
Joined: Tue Aug 01, 2017 2:44 pm

Re: Hardware PWM Pin 32 and Pin 33 Question

Thu Jan 18, 2018 10:23 am

Hi Joan,

I followed the suggestion for the frequency value, (mainly 10hz, 50hz,1000hz, 25000 hz) and all of it work fine with my dc motors.

I set it to 10Hz in the end for both the hardware PWM Pin.

I am trying to count the pulse of the encoder and I followed the other thread which you had answered.
viewtopic.php?f=33&t=71295

I was looking at the gpioSetAlertFunc from the documentation:

Code: Select all

int gpioSetAlertFunc(unsigned user_gpio, gpioAlertFunc_t f)
In your documentation, you mentioned that

Code: Select all

The alert may be cancelled by passing NULL as the function. 

The GPIO are sampled at a rate set when the library is started. 

If a value isn't specifically set the default of 5 us is used. 

The number of samples per second is given in the following table. 
I would like to ask, will the "gpioSetAlertFunc " function affect or change the 10Hz frequency which I had set for the hardware PWM pin?

Thank You!

User avatar
joan
Posts: 13559
Joined: Thu Jul 05, 2012 5:09 pm
Location: UK

Re: Hardware PWM Pin 32 and Pin 33 Question

Thu Jan 18, 2018 11:23 am

No, PWM is completely separate from sampling.

For usage of gpioSetAlertFunc perhaps look at the following example.

http://abyz.me.uk/rpi/pigpio/ex_rotary_encoder.html

J.Bryan
Posts: 42
Joined: Tue Aug 01, 2017 2:44 pm

Re: Hardware PWM Pin 32 and Pin 33 Question

Thu Jan 18, 2018 2:30 pm

Hi Joan,

I am not really understanding if i need to setup the pull up resistor.

One of the motor I intend to use is https://www.pololu.com/product/1446.

I would like to get some advice whether I need to setup the pull up resistor.

I am mainly interested in the following 2 output.

Code: Select all

Yellow	encoder A output
White	encoder B outpu
Thanks for your help!

User avatar
joan
Posts: 13559
Joined: Thu Jul 05, 2012 5:09 pm
Location: UK

Re: Hardware PWM Pin 32 and Pin 33 Question

Thu Jan 18, 2018 2:50 pm

I can't be sure.

All the encoder A/B lines I've used float to the external voltage except when pulled to ground during the rotation. So yes, normally you need to enable the GPIO pull-ups to pull the A/B lines high.

J.Bryan
Posts: 42
Joined: Tue Aug 01, 2017 2:44 pm

Re: Hardware PWM Pin 32 and Pin 33 Question

Thu Jan 18, 2018 3:26 pm

joan wrote:
Thu Jan 18, 2018 2:50 pm
I can't be sure.

All the encoder A/B lines I've used float to the external voltage except when pulled to ground during the rotation. So yes, normally you need to enable the GPIO pull-ups to pull the A/B lines high.
Noted with thanks. I would also like to check with you, will I get the correct voltage reading when I read a PWM signal using a DMM? Or is it better to use a oscilloscope.

User avatar
joan
Posts: 13559
Joined: Thu Jul 05, 2012 5:09 pm
Location: UK

Re: Hardware PWM Pin 32 and Pin 33 Question

Thu Jan 18, 2018 4:40 pm

If you turn the encoder slowly you should be able to see the lines going high and low with a meter.

J.Bryan
Posts: 42
Joined: Tue Aug 01, 2017 2:44 pm

Re: Hardware PWM Pin 32 and Pin 33 Question

Sat Jan 20, 2018 6:20 pm

joan wrote:
Thu Jan 18, 2018 11:23 am
No, PWM is completely separate from sampling.

For usage of gpioSetAlertFunc perhaps look at the following example.

http://abyz.me.uk/rpi/pigpio/ex_rotary_encoder.html
Hi Joan for the rotary encoder example.

Is it okay if i do not include the debounce code, I am not really sure what it does? I want very accurate count for every pulse so I would like to remove the debounce code. Is the default value for "level" equal to 1? I have 4 pins, and I would like to set up 4 gpioSetAlertFunc.

Example:

Code: Select all

   gpioSetAlertFunc(L_ENCODER_A, L_encoderPulse);
   gpioSetAlertFunc(L_ENCODER_B, L_encoderPulse);
   gpioSetAlertFunc(R_ENCODER_A, R_encoderPulse);
   gpioSetAlertFunc(R_ENCODER_B, R_encoderPulse);

Code: Select all

void  L_encoderPulse(int gpio, int level, uint32_t tick)
{
   static int levA=0, levB=0;

   if (gpio == L_ENCODER_A) levA = level; else levB = level;

      if ((gpio ==  L_ENCODER_A) && (level == 0))
      {
         if (!levB) ++L_encoderPos;
      }
      else if ((gpio ==  L_ENCODER_B) && (level == 1))
      {
         if (levA) --L_encoderPos;
      }

}

Code: Select all

void  R_encoderPulse(int gpio, int level, uint32_t tick)
{
   static int levA=0, levB=0;

   if (gpio == R_ENCODER_A) levA = level; else levB = level;

      if ((gpio ==  R_ENCODER_A) && (level == 0))
      {
         if (!levB) ++R_encoderPos;
      }
      else if ((gpio ==  R_ENCODER_B) && (level == 1))
      {
         if (levA) --R_encoderPos;
      }

}
Thanks. :)

User avatar
joan
Posts: 13559
Joined: Thu Jul 05, 2012 5:09 pm
Location: UK

Re: Hardware PWM Pin 32 and Pin 33 Question

Sat Jan 20, 2018 8:51 pm

You can alter it anyway you want. I wouldn't be surprised if debounce was a bad choice of words on my part. There should be standard implementations. Perhaps try with and without the "debounce" to see which works better.

J.Bryan
Posts: 42
Joined: Tue Aug 01, 2017 2:44 pm

Re: Hardware PWM Pin 32 and Pin 33 Question

Sun Jan 28, 2018 1:09 pm

Hi Joan,

I am trying to read a quadrature encoder on the dc motor.

I read through the page at Arduino http://playground.arduino.cc/Main/RotaryEncoders. And I would like to modified your rotary encoder to something like the code below.

Code: Select all

#define encoder0PinA  2
#define encoder0PinB  4

volatile unsigned int encoder0Pos = 0;

void setup() {
  pinMode(encoder0PinA, INPUT);
  digitalWrite(encoder0PinA, HIGH);       // turn on pull-up resistor
  pinMode(encoder0PinB, INPUT);
  digitalWrite(encoder0PinB, HIGH);       // turn on pull-up resistor

  attachInterrupt(0, doEncoder, CHANGE);  // encoder pin on interrupt 0 - pin 2
  Serial.begin (9600);
  Serial.println("start");                // a personal quirk
}

void loop() {
  // do some stuff here - the joy of interrupts is that they take care of themselves
}

void doEncoder() {
  /* If pinA and pinB are both high or both low, it is spinning
     forward. If they're different, it's going backward.

     For more information on speeding up this process, see
     [Reference/PortManipulation], specifically the PIND register.
  */
  if (digitalRead(encoder0PinA) == digitalRead(encoder0PinB)) {
    encoder0Pos++;
  } else {
    encoder0Pos--;
  }

  Serial.println (encoder0Pos, DEC);
}

/* See this expanded function to get a better understanding of the
   meanings of the four possible (pinA, pinB) value pairs:
*/
void doEncoder_Expanded() {
  if (digitalRead(encoder0PinA) == HIGH) {   // found a low-to-high on channel A
    if (digitalRead(encoder0PinB) == LOW) {  // check channel B to see which way
      // encoder is turning
      encoder0Pos = encoder0Pos - 1;         // CCW
    }
    else {
      encoder0Pos = encoder0Pos + 1;         // CW
    }
  }
  else                                        // found a high-to-low on channel A
  {
    if (digitalRead(encoder0PinB) == LOW) {   // check channel B to see which way
      // encoder is turning
      encoder0Pos = encoder0Pos + 1;          // CW
    }
    else {
      encoder0Pos = encoder0Pos - 1;          // CCW
    }

  }
  Serial.println (encoder0Pos, DEC);          // debug - remember to comment out
  // before final program run
  // you don't want serial slowing down your program if not needed
}

/*  to read the other two transitions - just use another attachInterrupt()
  in the setup and duplicate the doEncoder function into say,
  doEncoderA and doEncoderB.
  You also need to move the other encoder wire over to pin 3 (interrupt 1).
*/

Attached is my modified code for use in Raspberry PI:

Code: Select all

#define LEFT_ENCODER_A 5
#define LEFT_ENCODER_B 6

gpioSetMode(LEFT_ENCODER_A, PI_INPUT);
gpioSetMode(LEFT_ENCODER_B, PI_INPUT);

gpioSetPullUpDown(LEFT_ENCODER_A, PI_PUD_UP);
gpioSetPullUpDown(LEFT_ENCODER_B, PI_PUD_UP);


gpioSetAlertFunc(LEFT_ENCODER_A, COUNT-ENCODER)
gpioSetAlertFunc(LEFT_ENCODER_B, COUNT-ENCODER);

Code: Select all

void COUNT_ENCODER(int gpio, int level, uint32_t tick)
{
		if ((gpio == LEFT_ENCODER_A) && (level == 1))
		{

			if ((gpio == LEFT_ENCODER_B) && (level == 0))
				--LEFT_ENCODER;
			else
				++LEFT_ENCODER;
		}
		else 
		{
			if ((gpio == LEFT_ENCODER_B) && (level == 0))
				++LEFT_ENCODER;
			else
				--LEFT_ENCODER;
		}

	
However, I am always getting a result of 0, 1, or -1.

Is there something that i miss out that should be configured?

Thanks for your help.

User avatar
Paeryn
Posts: 2146
Joined: Wed Nov 23, 2011 1:10 am
Location: Sheffield, England

Re: Hardware PWM Pin 32 and Pin 33 Question

Sun Jan 28, 2018 2:29 pm

J.Bryan wrote:
Sun Jan 28, 2018 1:09 pm
Attached is my modified code for use in Raspberry PI:

Code: Select all

#define LEFT_ENCODER_A 5
#define LEFT_ENCODER_B 6

gpioSetMode(LEFT_ENCODER_A, PI_INPUT);
gpioSetMode(LEFT_ENCODER_B, PI_INPUT);

gpioSetPullUpDown(LEFT_ENCODER_A, PI_PUD_UP);
gpioSetPullUpDown(LEFT_ENCODER_B, PI_PUD_UP);


gpioSetAlertFunc(LEFT_ENCODER_A, COUNT-ENCODER)
gpioSetAlertFunc(LEFT_ENCODER_B, COUNT-ENCODER);

void COUNT_ENCODER(int gpio, int level, uint32_t tick)
{
		if ((gpio == LEFT_ENCODER_A) && (level == 1))
		{

			if ((gpio == LEFT_ENCODER_B) && (level == 0))
				--LEFT_ENCODER;
			else
				++LEFT_ENCODER;
		}
		else 
		{
			if ((gpio == LEFT_ENCODER_B) && (level == 0))
				++LEFT_ENCODER;
			else
				--LEFT_ENCODER;
		}

Your logic in the function doesn't look right. The first if tests if gpio == LEFT_ENCODER_A and level == 1, if both of them are true you then test if gpio == LEFT_ENCODER_B and level == 0, but when you get to that inner if statement you already know that both of these must be false so that inner if block will always increment LEFT_ENCODER, it can never decrement it.

The original code looks to use the two gpios to decide how to alter the output

Code: Select all

ENCODER_A   ENCODER_B  adjust output by
  LOW          LOW        +1
  LOW          HIGH       -1
  HIGH         LOW        -1
  HIGH         HIGH       +1
Your function gets called twice, once for A changing and once for B changing. Not taking into account what could happen if the function is running from the interrupt for A changing when the interrupt for B changing happens, I don't know if the library will queue them up so wait for the first to finish before calling the second or if it will let both run at the same time. If the latter then without protection both instances could read the initial value of LEFT_ENCODER before either has written its updated value so when the first writes the new value it will get overwritten with the value from the second, but the second will have the value from before the first modified it (so you effectively loose that first modification).

Code: Select all

When A changes
ENCODER_A  adjust output by
  LOW          0
  HIGH         +1

When B changes
ENCODER_B  adjust output by
  LOW         +1
  HIGH        -1
   
Combined result after receiving a pulse on each
ENCODER_A   ENCODER_B  adjust output by
  LOW          LOW        +1
  LOW          HIGH       -1
  HIGH         LOW        +2
  HIGH         HIGH       0
She who travels light — forgot something.

J.Bryan
Posts: 42
Joined: Tue Aug 01, 2017 2:44 pm

Re: Hardware PWM Pin 32 and Pin 33 Question

Tue Mar 13, 2018 2:27 pm

Hi Joan,

Have the link http://abyz.me.uk/rpi/pigpio/ex_rotary_encoder.html change/move? I would like to look at the code again for the encoder code.

Thank You.

User avatar
joan
Posts: 13559
Joined: Thu Jul 05, 2012 5:09 pm
Location: UK

Re: Hardware PWM Pin 32 and Pin 33 Question

Tue Mar 13, 2018 2:34 pm

The link is correct. Unfortunately it is broken as the contents are being deleted by my hosting provider for God knows what reason. We have a little game, I periodically complain, and they periodically say it's fixed.

I doubt there is anything on that page to help you.

J.Bryan
Posts: 42
Joined: Tue Aug 01, 2017 2:44 pm

Re: Hardware PWM Pin 32 and Pin 33 Question

Tue Mar 13, 2018 3:39 pm

joan wrote:
Tue Mar 13, 2018 2:34 pm
The link is correct. Unfortunately it is broken as the contents are being deleted by my hosting provider for God knows what reason. We have a little game, I periodically complain, and they periodically say it's fixed.

I doubt there is anything on that page to help you.
Hi Joan,

Actually it is pretty helpful because it is the only link which I can find on how to decode a encoder on raspberry pi. I wanted to understand more about your code to see if it is the same as the implementation as the following arduino code which is used for quadrature encoder:

Code: Select all

#define encoder0PinA  2
#define encoder0PinB  4

volatile unsigned int encoder0Pos = 0;

void setup() {
  pinMode(encoder0PinA, INPUT);
  digitalWrite(encoder0PinA, HIGH);       // turn on pull-up resistor
  pinMode(encoder0PinB, INPUT);
  digitalWrite(encoder0PinB, HIGH);       // turn on pull-up resistor

  attachInterrupt(0, doEncoder, CHANGE);  // encoder pin on interrupt 0 - pin 2
  Serial.begin (9600);
  Serial.println("start");                // a personal quirk
}

void loop() {
  // do some stuff here - the joy of interrupts is that they take care of themselves
}

void doEncoder() {
  /* If pinA and pinB are both high or both low, it is spinning
     forward. If they're different, it's going backward.

     For more information on speeding up this process, see
     [Reference/PortManipulation], specifically the PIND register.
  */
  if (digitalRead(encoder0PinA) == digitalRead(encoder0PinB)) {
    encoder0Pos++;
  } else {
    encoder0Pos--;
  }

  Serial.println (encoder0Pos, DEC);
}

/* See this expanded function to get a better understanding of the
   meanings of the four possible (pinA, pinB) value pairs:
*/
void doEncoder_Expanded() {
  if (digitalRead(encoder0PinA) == HIGH) {   // found a low-to-high on channel A
    if (digitalRead(encoder0PinB) == LOW) {  // check channel B to see which way
      // encoder is turning
      encoder0Pos = encoder0Pos - 1;         // CCW
    }
    else {
      encoder0Pos = encoder0Pos + 1;         // CW
    }
  }
  else                                        // found a high-to-low on channel A
  {
    if (digitalRead(encoder0PinB) == LOW) {   // check channel B to see which way
      // encoder is turning
      encoder0Pos = encoder0Pos + 1;          // CW
    }
    else {
      encoder0Pos = encoder0Pos - 1;          // CCW
    }

  }
  Serial.println (encoder0Pos, DEC);          // debug - remember to comment out
  // before final program run
  // you don't want serial slowing down your program if not needed
}

/*  to read the other two transitions - just use another attachInterrupt()
  in the setup and duplicate the doEncoder function into say,
  doEncoderA and doEncoderB.
  You also need to move the other encoder wire over to pin 3 (interrupt 1).
*/
I really appreciate if you can reply and help me about this. Because I am trying to use the code for my motor program and it is really a crucial piece of code for my tuning of variable (similar to PID). I had read through Paeryn comment more than 10 times and understand the logic behind this arduino code. But when I try to implement it using your library and writing my own code, I can't get it to work (most likely is because i am not writing the right code). I would really need some assistance on this. Thank you for your time.
Last edited by J.Bryan on Tue Mar 13, 2018 3:47 pm, edited 1 time in total.

User avatar
joan
Posts: 13559
Joined: Thu Jul 05, 2012 5:09 pm
Location: UK

Re: Hardware PWM Pin 32 and Pin 33 Question

Tue Mar 13, 2018 3:46 pm


Vicha
Posts: 2
Joined: Tue Mar 27, 2018 8:01 am

Re: Hardware PWM Pin 32 and Pin 33 Question

Tue Mar 27, 2018 8:13 am

Hello,

I am trying to use the pigpio library to control a servo/stepper motor on a pi. The function hardwarePWM constantly generates a pwm signal from a desired pin. How can I use this for position control (specific number of pulses) at high speeds?

Example:
Motor is 2000 steps per revolution
I want to do one revolution (2000 pulses) at 50KHz

I would normally just write a high or low value with a delay in between to generate the pulse myself but its really jittery and i cannot achieve my speed requirements.

the-snowlight
Posts: 3
Joined: Sun Nov 15, 2015 4:55 am

Re: Hardware PWM Pin 32 and Pin 33 Question

Wed Mar 28, 2018 2:09 am

Here is a related question. I used PWM on pins 7,11, 13 and 15 and it seem to work well to conttol DC motor speed by varying the duty cycle. However everywhere it stays that there are only 4 PWM dedicated pins!
What am I missing?

Return to “C/C++”

Who is online

Users browsing this forum: No registered users and 4 guests