Notifications
Clear all

Specifying the frequency of AnalogWrite() for motor control on RP Pico

21 Posts
4 Users
2 Likes
2,383 Views
redi45
(@redi45)
Active Member
Joined: 4 years ago
Posts: 7
Topic starter  

Hi Everybody!

I'm building a small two-wheel driven autonomous car using Raspberry Pi's Pico. What a little jewel!

I'm using Arduino programming as I've got encoders on the wheel motors as Micropython/Circuitpython aren't great with interrups. Also, I'm planning to use PID to control the wheels. My motor driver is a Pololu's breakout of the TB6612FNG.

Unfortunately, I'm not as familiar with C++ as I am with python.

What I would like to do is set the frequency of analogWrite() that I send to my motor controller. My motors tend to growl if the frequency isn't 1.6K Hz.

I've searched around and cannot find the info. I think it has to do with scalers on a timer, but I fail to understand how to apply them to my issue.

Any help would be greatly appreciated!

Bo


   
Quote
Ron
 Ron
(@zander)
Famed Member
Joined: 2 years ago
Posts: 4016
 

Not my area of expertise, but I would first see if Bill has any YT videos about that, if he doesn't then a search in the library manager would be my next step followed by a simple google search. Good luck.

Arduino says and I agree, in general, the const keyword is preferred for defining constants and should be used instead of #define
"Never wrestle with a pig....the pig loves it and you end up covered in mud..." anon
My experience hours are >75,000 and I stopped counting in 2004.
Major Languages - 360 Macro Assembler, Intel Assembler, PLI/1, Pascal, C plus numerous job control and scripting


   
ReplyQuote
Ron
 Ron
(@zander)
Famed Member
Joined: 2 years ago
Posts: 4016
 

Here is Bill's video https://dronebotworkshop.com/tb6612fng-h-bridge/

Arduino says and I agree, in general, the const keyword is preferred for defining constants and should be used instead of #define
"Never wrestle with a pig....the pig loves it and you end up covered in mud..." anon
My experience hours are >75,000 and I stopped counting in 2004.
Major Languages - 360 Macro Assembler, Intel Assembler, PLI/1, Pascal, C plus numerous job control and scripting


   
ReplyQuote
b
 b
(@b)
Noble Member
Joined: 4 years ago
Posts: 912
 

@redi45

Have you considered using the pio of the pico for counting encoder pulses.  The assembler code can be run with mp.  Not that I've done this so I wont be much help in this, but it should be doable and I would love to see some good examples of using pio (and I confess this suggestion would be to use you as a guinea pig 😎).  I have also read about counting pulses using PMW, but again I've no idea, and I just made a mental note of this possibility.   

And another aside is the doc I link to below regarding reading encoders with micropython in case you've not perused it. 

https://github.com/peterhinch/micropython-samples/blob/master/encoders/ENCODERS.md

Of particular note in this doc maybe:

  1. encoder_rp2.py Version specific to Raspberry Pico RP2 chip. This uses the PIO and Viper code to achieve fast response - upto ~10K transitions/s.

I will have to leave it for others to comment on the setting of the frequency in C++

Have fun.

This post was modified 8 months ago by b

   
ReplyQuote
Ron
 Ron
(@zander)
Famed Member
Joined: 2 years ago
Posts: 4016
 

@byron @redi45 I think using the Sparkfun library like Bill did eliminates the need for any of that. Check out the article at https://dronebotworkshop.com/tb6612fng-h-bridge/ Look at the 2nd sketch.

Arduino says and I agree, in general, the const keyword is preferred for defining constants and should be used instead of #define
"Never wrestle with a pig....the pig loves it and you end up covered in mud..." anon
My experience hours are >75,000 and I stopped counting in 2004.
Major Languages - 360 Macro Assembler, Intel Assembler, PLI/1, Pascal, C plus numerous job control and scripting


   
ReplyQuote
b
 b
(@b)
Noble Member
Joined: 4 years ago
Posts: 912
 
Posted by: @zander

I think using the Sparkfun library like Bill did eliminates the need for any of that.

It probably does, but it depends on whether @redi45 may prefer to use micropython as its more familiar  or if the wish is to delve more deeply in C++ .  It appears that the latency of micropython interrupts was a possible stumbling block, hence my suggestions for fast read of encoder pulses within micropython 


   
ReplyQuote
Ron
 Ron
(@zander)
Famed Member
Joined: 2 years ago
Posts: 4016
 

@byron Not knowing anything python I wanted to point out that even C++ knowledge isn't really needed. Hopefully he will look at Bill's example to determine if it does what he needs.

Arduino says and I agree, in general, the const keyword is preferred for defining constants and should be used instead of #define
"Never wrestle with a pig....the pig loves it and you end up covered in mud..." anon
My experience hours are >75,000 and I stopped counting in 2004.
Major Languages - 360 Macro Assembler, Intel Assembler, PLI/1, Pascal, C plus numerous job control and scripting


   
ReplyQuote
redi45
(@redi45)
Active Member
Joined: 4 years ago
Posts: 7
Topic starter  

@zander 

Great Video. However, it doesn't speak to the issue of frequency.

Thank you @zander!


   
ReplyQuote
Ron
 Ron
(@zander)
Famed Member
Joined: 2 years ago
Posts: 4016
 

@redi45 Because it's built into the library I bet.

Arduino says and I agree, in general, the const keyword is preferred for defining constants and should be used instead of #define
"Never wrestle with a pig....the pig loves it and you end up covered in mud..." anon
My experience hours are >75,000 and I stopped counting in 2004.
Major Languages - 360 Macro Assembler, Intel Assembler, PLI/1, Pascal, C plus numerous job control and scripting


   
ReplyQuote
Ron
 Ron
(@zander)
Famed Member
Joined: 2 years ago
Posts: 4016
 

@redi45 Instead of speculating, run the sketch and see if it works for you.

Arduino says and I agree, in general, the const keyword is preferred for defining constants and should be used instead of #define
"Never wrestle with a pig....the pig loves it and you end up covered in mud..." anon
My experience hours are >75,000 and I stopped counting in 2004.
Major Languages - 360 Macro Assembler, Intel Assembler, PLI/1, Pascal, C plus numerous job control and scripting


   
ReplyQuote
redi45
(@redi45)
Active Member
Joined: 4 years ago
Posts: 7
Topic starter  

@byron 

O!

I'll have ta check it out! squeek squeek(guinea pig sound)


   
ReplyQuote
Ron
 Ron
(@zander)
Famed Member
Joined: 2 years ago
Posts: 4016
 

@redi45 @byron I just looked at the library code and do NOT see any manipulation of the PWM pin other than level. That doesn't sound right but as I said I am not an expert. Maybe it's time I bought that robot kit I have been looking at. Perhaps directing the OP question towards Bill will yield a gem.

Arduino says and I agree, in general, the const keyword is preferred for defining constants and should be used instead of #define
"Never wrestle with a pig....the pig loves it and you end up covered in mud..." anon
My experience hours are >75,000 and I stopped counting in 2004.
Major Languages - 360 Macro Assembler, Intel Assembler, PLI/1, Pascal, C plus numerous job control and scripting


   
ReplyQuote
b
 b
(@b)
Noble Member
Joined: 4 years ago
Posts: 912
 
Posted by: @redi45

I'll have ta check it out! squeek squeek(guinea pig sound)

I had a look at the doc I linked to.  I had it bookmarked but now just had a good read of it.  A very impressive and knowledgeable write up of the subject matter I must say.   Plus I now see the example program for the rpi pico does all the pio stuff so you may not have to squeek too loud to get it all working. 😀 

Depending on the diameter of your wheels and the encoder pulse frequency my quick and dirty calc (and probably wrong) indicated your small bot would have to be travelling at twice the speed of sound before the counter maxed out.   Is it a bird, is it a plane, no its a redi45 superbot breaking the sound barrier (with a very loud squeek 😎)


   
Ron reacted
ReplyQuote
Ron
 Ron
(@zander)
Famed Member
Joined: 2 years ago
Posts: 4016
 

@redi45 Here is a link that covers it all. Unless you have a specific need to go deeper, just hooking up a pot to a PWM pin does all the work. The code involving PRE-SCALARS etc is all inside the arduino.

I suggest using Bill's sketch as a start and then maybe move on to learning the underlying hardware and assembler language (the hardest) to roll your own if you have spare time, meanwhile you have a working car while you either do or don't learn the hardware and low level programming techniques just for chuckles.

https://docs.arduino.cc/tutorials/generic/secrets-of-arduino-pwm

Arduino says and I agree, in general, the const keyword is preferred for defining constants and should be used instead of #define
"Never wrestle with a pig....the pig loves it and you end up covered in mud..." anon
My experience hours are >75,000 and I stopped counting in 2004.
Major Languages - 360 Macro Assembler, Intel Assembler, PLI/1, Pascal, C plus numerous job control and scripting


   
ReplyQuote
THRandell
(@thrandell)
Estimable Member
Joined: 2 years ago
Posts: 136
 
Posted by: @redi45

I'm building a small two-wheel driven autonomous car using Raspberry Pi's Pico. What a little jewel!

I couldn’t agree more!  I’m lovin’ that little MCU.

 

I didn’t read what the other arm chair engineers wrote, so I apologize if this is a re-tread.

 

I read up on the Pololu board and my take is that each motor uses two digital pins to control direction.  They are all pulled down so I’m assuming that if you pull one up and the other down you’ll move the motor forward or backward, dah.  The third pin is for a PWM input to control speed.

I loaded up the RP2040 Arduino libraries myself and tried out the IDE but eventually I removed them and started writing C code by scratch.  Here is what I came up with to drive a simple Pololu dual H-bridge board.  If you adapt it for your board you'll need to add two more GPIO_OUT pins for direction control.   I apologize in advance for the wrapping comments in the code, but it does work and it’s not C++ code.

 

Good Luck   Tom

 

/*  an attempt to get a DRV8835 dual H-bridge running on a small DC motor
 *
 *  I'll need four pins: 2 SIO for direction, and 2 PWM for speed.
 *   GP14 7A and GP15 7B with GP12 and GP13 for direction
 *    my montra: level = duty-cycle and wrap = top which determines the frequency
 *
 */

#include "pico/stdlib.h"
#include "hardware/pwm.h"

void setM1Speed(uint slice_num, uint chanM1, int speed) { // values can be -100 to 100.
  bool reverse = 0;

  if (speed < 0) {   // make speed a positive number
     speed = -speed;
     reverse = 1;
  }
  if (speed > 100)  // max PWM duty cycle
     speed = 100;
  // pwm_set_chan_level(slice_num, chanM1, pwm_get_wrap(slice_num) * speed / 100);
  pwm_set_chan_level(slice_num, chanM1, pwm_hw->slice[slice_num].top * speed / 100);

  if (reverse)
     gpio_put(12, 1);                // drive m1 direction gpio high 
  else
     gpio_put(12, 0);                // drive m1 direction gpio low 
}

void setM2Speed(uint slice_num, uint chanM2, int speed) {
  bool reverse = 0;

  if (speed < 0) {   // make speed a positive number
     speed = -speed;
     reverse = 1;
  }
  if (speed > 100)  // max PWM duty cycle
     speed = 100;
  // pwm_set_chan_level(slice_num, chanM2, pwm_get_wrap(slice_num) * speed / 100); 
  pwm_set_chan_level(slice_num, chanM2, pwm_hw->slice[slice_num].top * speed / 100);
  if (reverse)
    gpio_put(13, 1);                // drive m2 direction gpio high 
  else
    gpio_put(13, 0);                // drive m2 direction gpio low
}

void setSpeeds(uint slice_num, uint chanM1, uint chanM2, int m1Speed, int m2Speed) {
   setM1Speed(slice_num, chanM1, m1Speed);
   setM2Speed(slice_num, chanM2, m2Speed);
}

int main()
{
  gpio_init(12);                  // enable i/o and set function to GPIO_FUNC_SIO, clear output, set to input.
  gpio_set_dir(12, GPIO_OUT);     // set gpio direction
  gpio_put(12, 0);                // drive gpio low 
  gpio_init(13);                  // enable i/o and set function to GPIO_FUNC_SIO, clear output, set to input.
  gpio_set_dir(13, GPIO_OUT);     // set gpio direction
  gpio_put(13, 0);                // drive gpio low
  gpio_set_function(14, GPIO_FUNC_PWM); // select function (PWM here) to be allocated to gpio pin, slice 7A
  gpio_set_function(15, GPIO_FUNC_PWM); //                                                         slice 7B
  uint slice_num = pwm_gpio_to_slice_num(14); // return PWM slice that is attached to gpio pin

  uint chanM1 = pwm_gpio_to_channel(14); // return channel (A or B) that is attached to gpio pin
  uint chanM2 = pwm_gpio_to_channel(15); // the PWM channel that controls the specified gpio
  
  // Using this formula to determine wrap for 20kHz phase correct carrier frequency
  // wrap = f(clock)/2*f(desired) - 1   so 125,000,000/2*20,000 - 1; 125,000,000/40,000 - 1; 3,124
  // f(clock) as used here is after applying the divider.
  uint32_t clock = 125000000;
  uint32_t f = 20000;
  uint32_t wrap = clock / (2 * f) - 1;
  pwm_set_clkdiv_int_frac(slice_num, 1,0); // probably the default
  pwm_set_wrap(slice_num, wrap);
  pwm_set_phase_correct(slice_num, true);

  pwm_set_chan_level(slice_num, chanM1, 0); // set current PWM counter compare value for channel
  pwm_set_chan_level(slice_num, chanM2, 0); // set current PWM counter compare value for channel
  pwm_set_enabled(slice_num, true);         //  enable/disable PWM

  while (true)
  {
    for (int x = 0; x <=100; x++) {
       setM1Speed(slice_num, chanM1, x);
       sleep_ms(50);
    }
    for (int x = 100; x >=0; x--) {
       setM1Speed(slice_num, chanM1, x);
       sleep_ms(50);
    }
    for (int x = 0; x >= -100; x--) {
       setM1Speed(slice_num, chanM1, x);
       sleep_ms(50);
    }
    for (int x = -100; x <=0; x++) {
       setM1Speed(slice_num, chanM1, x); 
       sleep_ms(50);
    }
    for (int x = 0; x <=100; x++) {
       setM2Speed(slice_num, chanM2, x);
       sleep_ms(50);
    }
    for (int x = 100; x >=0; x--) {
       setM2Speed(slice_num, chanM2, x);
       sleep_ms(50);
    }
    for (int x = 0; x >= -100; x--) {
       setM2Speed(slice_num, chanM2, x);
       sleep_ms(50);
    }
    for (int x = -100; x <=0; x++) {
       setM2Speed(slice_num, chanM2, x); 
       sleep_ms(50);
    }
  }
}

 

 

See, the human mind is kind of like... a pinata. When it breaks open, there's a lot of surprises inside. Once you get the pinata perspective, you see that losing your mind can be a peak experience.
Jane Wagner - The Search for Signs of Intelligent Life in the Universe


   
ReplyQuote
Page 1 / 2