element14 Community
element14 Community
    Register Log In
  • Site
  • Search
  • Log In Register
  • About Us
  • Community Hub
    Community Hub
    • What's New on element14
    • Feedback and Support
    • Benefits of Membership
    • Personal Blogs
    • Members Area
    • Achievement Levels
  • Learn
    Learn
    • Ask an Expert
    • eBooks
    • element14 presents
    • Learning Center
    • Tech Spotlight
    • STEM Academy
    • Webinars, Training and Events
    • Learning Groups
  • Technologies
    Technologies
    • 3D Printing
    • FPGA
    • Industrial Automation
    • Internet of Things
    • Power & Energy
    • Sensors
    • Technology Groups
  • Challenges & Projects
    Challenges & Projects
    • Design Challenges
    • element14 presents Projects
    • Project14
    • Arduino Projects
    • Raspberry Pi Projects
    • Project Groups
  • Products
    Products
    • Arduino
    • Avnet Boards Community
    • Dev Tools
    • Manufacturers
    • Multicomp Pro
    • Product Groups
    • Raspberry Pi
    • RoadTests & Reviews
  • Store
    Store
    • Visit Your Store
    • Choose another store...
      • Europe
      •  Austria (German)
      •  Belgium (Dutch, French)
      •  Bulgaria (Bulgarian)
      •  Czech Republic (Czech)
      •  Denmark (Danish)
      •  Estonia (Estonian)
      •  Finland (Finnish)
      •  France (French)
      •  Germany (German)
      •  Hungary (Hungarian)
      •  Ireland
      •  Israel
      •  Italy (Italian)
      •  Latvia (Latvian)
      •  
      •  Lithuania (Lithuanian)
      •  Netherlands (Dutch)
      •  Norway (Norwegian)
      •  Poland (Polish)
      •  Portugal (Portuguese)
      •  Romania (Romanian)
      •  Russia (Russian)
      •  Slovakia (Slovak)
      •  Slovenia (Slovenian)
      •  Spain (Spanish)
      •  Sweden (Swedish)
      •  Switzerland(German, French)
      •  Turkey (Turkish)
      •  United Kingdom
      • Asia Pacific
      •  Australia
      •  China
      •  Hong Kong
      •  India
      •  Korea (Korean)
      •  Malaysia
      •  New Zealand
      •  Philippines
      •  Singapore
      •  Taiwan
      •  Thailand (Thai)
      • Americas
      •  Brazil (Portuguese)
      •  Canada
      •  Mexico (Spanish)
      •  United States
      Can't find the country/region you're looking for? Visit our export site or find a local distributor.
  • Translate
  • Profile
  • Settings
BeagleBoard
  • Products
  • Dev Tools
  • Single-Board Computers
  • BeagleBoard
  • More
  • Cancel
BeagleBoard
Blog BeagleBone Control Stepper Motors - Part 6a: Use PRU Timer - Preparation
  • Blog
  • Forum
  • Documents
  • Quiz
  • Polls
  • Files
  • Members
  • Mentions
  • Sub-Groups
  • Tags
  • More
  • Cancel
  • New
Join BeagleBoard to participate - click to join for free!
  • Share
  • More
  • Cancel
Group Actions
  • Group RSS
  • More
  • Cancel
Engagement
  • Author Author: Jan Cumps
  • Date Created: 17 Aug 2019 12:58 PM Date Created
  • Views 1302 views
  • Likes 3 likes
  • Comments 2 comments
  • stepper_motor
  • pru
  • drv8711
  • BeagleBone
Related
Recommended

BeagleBone Control Stepper Motors - Part 6a: Use PRU Timer - Preparation

Jan Cumps
Jan Cumps
17 Aug 2019

I'm trying here is to let the real-time units of the BeagleBone generate the signals for a stepper motor.

In chapters 1 to 5, I developed PRU firmware that works for 1 stepper motor

 

In part 6, I switch to a timer controlled version, that should allow for more motors and smoother start and end. Here in post 6a: refactoring the code a first time to extract motor dependent logic from the main loop:

You don't have permission to edit metadata of this video.
Edit media
x
image
Upload Preview
image

 

Prepare for More Motors and Motor State

 

I created the shell for a structure to hold the state per motor.

 

typedef struct {
  // pin assignments
  uint32_t pin_step;
  uint32_t pin_dir;
  // status
  uint32_t dir;
  uint32_t steps;
  uint32_t clockDivider;
} motor_t;

 

And an array to keep all motors.

 

motor_t motors[MOTOR_COUNT];

 

This doesn't fundamentally change the program at this point. I defined the array size 1, so that's still one motor.

And the code doesn't loop over the array yet, it just uses that first motor.

But it's a starting point. One that can be used to validate if the program is still working after making the change.

 

Make Main Loop Simpler

 

The initialisation of the motor(s) and getting data from Linux/ARM are refactored into a separate function now.

 

void motor_init() {
   // todo this should become parameterised.
   // initialise motor array
   motors[0].pin_dir = PIN_DIR_MOTOR1;
   motors[0].pin_step = PIN_STEP_MOTOR1;
   motors[0].dir = 0U;
   // set STEP low
   __R30 &= ~motors[0].pin_step;
   // set dir to inital dir
   __R30 &= ~motors[0].dir << motors[0].pin_dir;
 }

 bool parseArmCmd (void *data, uint16_t len) {
   if (len < 2) { // at least directory and 1 bit of counts
     return false;
   }

   // fist position is direction. If '0', then clear DIR bit, else set dir bit
   if (payload[0] == '0') {
     // __R30 &= ~(1UL << DIR);
     motors[0].dir = 0U;
   } else {
     // __R30 |= 1UL << DIR;
     motors[0].dir = 1U;
   }

   if (motors[0].dir) {
     __R30 |= 1UL << motors[0].pin_dir;
   } else {
     __R30 &= ~(1UL <<  motors[0].pin_dir);
   }

   // get the number of pulses
   int i;
   uint32_t pulses = 0U;
   for (i = 1; i < len; i++) {
     if (isdigit(payload[i])) {
       pulses += ((payload[i] - '0') * opt_int_pow(len - 2 - i));
     }
   }
   motors[0].steps = pulses;
   return true;
 }

 

This makes the loop simpler to read, and simpler to work on in the next exercise.

 

  motor_init();

  /* Create the RPMsg channel between the PRU and ARM user space using the transport structure. */
  while (pru_rpmsg_channel(RPMSG_NS_CREATE, &transport, CHAN_NAME, CHAN_DESC, CHAN_PORT) != PRU_RPMSG_SUCCESS);
  while (1) {

    /* Check bit 30 of register R31 to see if the ARM has kicked us */
    if (__R31 & HOST_INT) {
      /* Clear the event status */
      CT_INTC.SICR_bit.STS_CLR_IDX = FROM_ARM_HOST;
      /* Receive all available messages, multiple messages can be sent per kick */
      while (pru_rpmsg_receive(&transport, &src, &dst, payload, &len) == PRU_RPMSG_SUCCESS) {
        if (parseArmCmd(payload, len)) {
          int delay = 60000;
          int i;
          // toggle STEP twice the pulse count (a pulse is two toggles)
          for (i = 0; i < motors[0].steps * 2; i++) {
            __R30 ^= 1UL << motors[0].pin_step;  // can be optimised if I constant the right operator
            //                    __delay_cycles(300000); // Intrinsic method delay
            int j;
            for (j = 0; j < delay; j++) {
            }
          }
        }
      }
    }
  }

 

The combination of these two activities help, while not making so many modifications in the program flow that debugging becomes difficult.

 

This version of the PRU firmware: https://gist.githubusercontent.com/jancumps/ee9482dd27856c823ddcf505830dece4/raw/9a251b9f81f5576499db23d7570b0564bdcd639…

 

Make Testing Simpler

 

I used to do a number of manual steps each time I changed the PRU source. I've reduced that to 3 steps by using a shell script.

 

What I used to do

  • compile in CCS
  • move to the BB in CCS by dropping it on a remote systems window
  • 1 time set all pins, enable SPI, send SPI commands to stepper controller
  • stop running PRU firmware on BB
  • register new firmware
  • start running new firmware, get stepper out of sleep
  • run test
  • put stepper driver back to sleep (or it would heat up, 0.6A constant through the coils when not running.

 

With the shell script:

  • compile in CCS
  • move to the BB in CCS by dropping it on a remote systems window
  • run shell script on the BB. It does everything mentioned above

 

When I know that I have to do a series of repetitive steps to test code modifications, I get lazy and hold off.

An efficient round trip rocess motivates me to do small incremental improvements and test them.

I can optimise this later and add the upload to BB and launching the shell script to the CCS build process. I'll do that when the 3-step process starts to bother me.

 

Here is the script. I named it PRU_STEPPER.sh.

 

#!/bin/bash
PRU_STEPPER_link="$(readlink -f /home/debian/bin/bb_PRU_STEPPER.out)"
fw0_link=${PRU_STEPPER_link}
if [ ${fw0_link} ];
     then
     echo "stop the program if running"
     echo 'stop' > /sys/class/remoteproc/remoteproc1/state 2>/dev/null
     rm /lib/firmware/am335x-pru0-fw &> /dev/null
     ln -s ${fw0_link} /lib/firmware/am335x-pru0-fw

     echo "load the firmware to PRU"
     echo 'am335x-pru0-fw' > /sys/class/remoteproc/remoteproc1/firmware

     echo "start the program"
     echo 'start' > /sys/class/remoteproc/remoteproc1/state

     echo ""
     echo "Firmware is running"
     echo ""
else
     echo "Firmware .out files does not seem to exist. Did you place " ${PRU_STEPPER_link} "?"
     echo ""
fi

echo "sleep on"
echo out > /sys/class/gpio/gpio30/direction
echo 0 > /sys/class/gpio/gpio30/value
  
echo "reset off"
echo out > /sys/class/gpio/gpio31/direction
echo 0 > /sys/class/gpio/gpio31/value

echo "set pin 29 and 31 as PRU GPIO out"
config-pin P9_31 pruout    
config-pin P9_29 pruout    

echo "initialise SPI"
/home/debian/bin/bb_LINUX_STEPPER_SPI 
echo "stepper motor control ready from PRU0"

while test $# -gt 0
do
    case "$1" in
        --test) 
     echo "test requested"
     echo "sleep off"  
     echo 1 > /sys/class/gpio/gpio30/value
     echo "motor move start"
     echo '120' > /dev/rpmsg_pru30
     echo '020' > /dev/rpmsg_pru30
     echo "motor move stop"
     echo "short pause before activating motor sleep..."
     sleep 0.5s
     echo "sleep on"
     echo 0 > /sys/class/gpio/gpio30/value
        
     ;;
    esac
    shift
done

 

 

To do a full test cycle:

sudo /home/debian/bin/PRU_STEPPER.sh --test

  • Sign in to reply

Top Comments

  • Jan Cumps
    Jan Cumps over 6 years ago +2
    note to self: this should not be in the parse command because it changes a running motor. The parse should merely set the directory in the structure, without changing the pin. Setting the direction pin…
  • Jan Cumps
    Jan Cumps over 6 years ago

    note to self: this should not be in the parse command because it changes a running motor.

    The parse should merely set the directory in the structure, without changing the pin.

    Setting the direction pin should be done when the motor is ready to run the command, just before doing a first step.

     

       if (motors[0].dir) {  
         __R30 |= 1UL << motors[0].pin_dir;  
       } else {  
         __R30 &= ~(1UL <<  motors[0].pin_dir);  
       } 

    • Cancel
    • Vote Up +2 Vote Down
    • Sign in to reply
    • More
    • Cancel
  • clem57
    clem57 over 6 years ago

    Good video Jan Cumps. I use small steps, too. I often program incrementally proving each step to avoid complex debugging.

    Clem

    PS Also enjoyed reading your details.

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • More
    • Cancel
element14 Community

element14 is the first online community specifically for engineers. Connect with your peers and get expert answers to your questions.

  • Members
  • Learn
  • Technologies
  • Challenges & Projects
  • Products
  • Store
  • About Us
  • Feedback & Support
  • FAQs
  • Terms of Use
  • Privacy Policy
  • Legal and Copyright Notices
  • Sitemap
  • Cookies

An Avnet Company © 2025 Premier Farnell Limited. All Rights Reserved.

Premier Farnell Ltd, registered in England and Wales (no 00876412), registered office: Farnell House, Forge Lane, Leeds LS12 2NE.

ICP 备案号 10220084.

Follow element14

  • X
  • Facebook
  • linkedin
  • YouTube