element14 Community
element14 Community
    Register Log In
  • Site
  • Search
  • Log In Register
  • Members
    Members
    • Benefits of Membership
    • Achievement Levels
    • Members Area
    • Personal Blogs
    • Feedback and Support
    • What's New on element14
  • Learn
    Learn
    • Learning Center
    • eBooks
    • STEM Academy
    • Webinars, Training and Events
    • Learning Groups
  • Technologies
    Technologies
    • 3D Printing
    • Experts & Guidance
    • FPGA
    • Industrial Automation
    • Internet of Things
    • Power & Energy
    • Sensors
    • Technology Groups
  • Challenges & Projects
    Challenges & Projects
    • Design Challenges
    • element14 presents
    • Project14
    • Arduino Projects
    • Raspberry Pi Projects
    • Project Groups
  • Products
    Products
    • Arduino
    • Dev Tools
    • Manufacturers
    • Raspberry Pi
    • RoadTests & Reviews
    • Avnet Boards Community
    • Product Groups
  • 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
BeagleBoard
  • Products
  • Dev Tools
  • Single-Board Computers
  • BeagleBoard
  • More
  • Cancel
BeagleBoard
Blog BeagleBone Control Stepper Motors with PRU - Part 4: SPI Setup
  • Blog
  • Forum
  • Documents
  • Polls
  • Files
  • Members
  • Mentions
  • Sub-Groups
  • Tags
  • More
  • Cancel
  • New
BeagleBoard requires membership for participation - click to join
Blog Post Actions
  • Subscribe by email
  • More
  • Cancel
  • Share
  • Subscribe by email
  • More
  • Cancel
Group Actions
  • Group RSS
  • More
  • Cancel
Engagement
  • Author Author: Jan Cumps
  • Date Created: 2 Aug 2019 3:44 PM Date Created
  • Views 1282 views
  • Likes 1 like
  • Comments 0 comments
  • stepper_motor
  • bb
  • spi
  • drv8711
  • bbb
  • BeagleBone
  • code_composer_studio
  • linux
Related
Recommended

BeagleBone Control Stepper Motors with PRU - Part 4: SPI Setup

Jan Cumps
Jan Cumps
2 Aug 2019

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

In this post: configure the stepper motor driver via the SPI interface.

image

 

This is virtually the same code as used in my Hercules real-time stepper driver project.

I'm using the DRV8711 SPI interface to set its registers. They determine the driver's behaviour.

The only difference is that I now use the BB's Linux SPI interface to talk to it instead of the Hercules SPI module.

I've made an executable in C. It sets the registers, then exits.

 

The program flow is extremely simple:

 

int main(int argc, char *argv[]) {
    int fd = initialize();
    WriteAllRegisters(fd);
    finalize(fd);
    return 0;
}

 

Declaration of constants and variables (the code with the struct structures is in the attached full CCS project).

 

// DRV8711 Registers
struct CTRL_Register     G_CTRL_REG;
struct TORQUE_Register   G_TORQUE_REG;
struct OFF_Register      G_OFF_REG;
struct BLANK_Register    G_BLANK_REG;
struct DECAY_Register    G_DECAY_REG;
struct STALL_Register    G_STALL_REG;
struct DRIVE_Register    G_DRIVE_REG;
struct STATUS_Register   G_STATUS_REG;

// Register Access
#define REGWRITE    0x0000

static const char *device = "/dev/spidev1.0";
static uint8_t mode;
static uint8_t bits = 16;
static uint32_t speed = 500000;
static uint16_t delay;

 

The initialize() function sets SPI interface and initialises register settings.

Attention: the DRV8711 uses the uncommon CS High mode. The IC listens to SPI commands only when the CS line is high.

It took me a while to find how to do that on a BB.

You have to set the mode accordingly:

    mode |= SPI_CS_HIGH;

 

 

int initialize() {
    int ret = 0;
    int fd;

    fd = open(device, O_RDWR);
    if (fd < 0)
        pabort("can't open device");

    /*
     * spi mode
     */
    mode |= SPI_CS_HIGH; // attention, the DRV8711 uses CS high
    ret = ioctl(fd, SPI_IOC_WR_MODE, &mode);
    if (ret == -1)
        pabort("can't set spi mode");

    ret = ioctl(fd, SPI_IOC_RD_MODE, &mode);
    if (ret == -1)
        pabort("can't get spi mode");

    /*
     * bits per word
     */
    ret = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits);
    if (ret == -1)
        pabort("can't set bits per word");

    ret = ioctl(fd, SPI_IOC_RD_BITS_PER_WORD, &bits);
    if (ret == -1)
        pabort("can't get bits per word");

    /*
     * max speed hz
     */
    ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);
    if (ret == -1)
        pabort("can't set max speed hz");

    ret = ioctl(fd, SPI_IOC_RD_MAX_SPEED_HZ, &speed);
    if (ret == -1)
        pabort("can't get max speed hz");

    printf("spi mode: %d\n", mode);
    printf("bits per word: %d\n", bits);
    printf("max speed: %d Hz (%d KHz)\n", speed, speed/1000);

    // drv8711
    // Set Default Register Settings
    // CTRL Register
    G_CTRL_REG.Address     = 0x0000;
    G_CTRL_REG.DTIME     = 0x0003;
    G_CTRL_REG.ISGAIN     = 0x0003;
    G_CTRL_REG.EXSTALL     = 0x0000;
//     G_CTRL_REG.MODE     = 0x0003;  // in 8th steps
    G_CTRL_REG.MODE     = 0x0000; // in full steps
    G_CTRL_REG.RSTEP     = 0x0000;
    G_CTRL_REG.RDIR     = 0x0000;
    G_CTRL_REG.ENBL     = 0x0001;

    // TORQUE Register
    G_TORQUE_REG.Address = 0x0001;
    G_TORQUE_REG.SIMPLTH = 0x0000;
    G_TORQUE_REG.TORQUE  = 0x00BA;

    // OFF Register
    G_OFF_REG.Address     = 0x0002;
    G_OFF_REG.PWMMODE     = 0x0000;
    G_OFF_REG.TOFF         = 0x0030;

    // BLANK Register
    G_BLANK_REG.Address = 0x0003;
    G_BLANK_REG.ABT     = 0x0001;
    G_BLANK_REG.TBLANK     = 0x0008;


    // DECAY Register.
    G_DECAY_REG.Address = 0x0004;
    G_DECAY_REG.DECMOD  = 0x0003;
    G_DECAY_REG.TDECAY     = 0x0010;

    // STALL Register
    G_STALL_REG.Address = 0x0005;
    G_STALL_REG.VDIV     = 0x0003;
    G_STALL_REG.SDCNT     = 0x0003;
    G_STALL_REG.SDTHR     = 0x0040;

    // DRIVE Register
    G_DRIVE_REG.Address = 0x0006;
    G_DRIVE_REG.IDRIVEP = 0x0000;
    G_DRIVE_REG.IDRIVEN = 0x0000;
    G_DRIVE_REG.TDRIVEP = 0x0001;
    G_DRIVE_REG.TDRIVEN = 0x0001;
    G_DRIVE_REG.OCPDEG     = 0x0001;
    G_DRIVE_REG.OCPTH     = 0x0001;

    // STATUS Register
    G_STATUS_REG.Address = 0x0007;
    G_STATUS_REG.STDLAT  = 0x0000;
    G_STATUS_REG.STD     = 0x0000;
    G_STATUS_REG.UVLO    = 0x0000;
    G_STATUS_REG.BPDF    = 0x0000;
    G_STATUS_REG.APDF    = 0x0000;
    G_STATUS_REG.BOCP    = 0x0000;
    G_STATUS_REG.AOCP    = 0x0000;
    G_STATUS_REG.OTS     = 0x0000;

    return fd;
}

 

I should split up the BB SPI init and the DRV8711 settings initialisation. They are unrelated activities. Maybe later ...

 

Then all registers are written.

 

void WriteAllRegisters(int fd) {
    uint16_t data;

    // Write CTRL Register
    data = REGWRITE | (G_CTRL_REG.Address << 12) | (G_CTRL_REG.DTIME << 10) | (G_CTRL_REG.ISGAIN << 8) |(G_CTRL_REG.EXSTALL << 7) | (G_CTRL_REG.MODE << 3) | (G_CTRL_REG.RSTEP << 2) | (G_CTRL_REG.RDIR << 1) | (G_CTRL_REG.ENBL);
    SPI_DRV8711_Write(fd, data);

    // Write TORQUE Register
    data = REGWRITE | (G_TORQUE_REG.Address << 12) | (G_TORQUE_REG.SIMPLTH << 8) | G_TORQUE_REG.TORQUE;
    SPI_DRV8711_Write(fd, data);

    // Write OFF Register
    data = REGWRITE | (G_OFF_REG.Address << 12) | (G_OFF_REG.PWMMODE << 8) | G_OFF_REG.TOFF;
    SPI_DRV8711_Write(fd, data);

    // Write BLANK Register
    data = REGWRITE | (G_BLANK_REG.Address << 12) | (G_BLANK_REG.ABT << 8) | G_BLANK_REG.TBLANK;
    SPI_DRV8711_Write(fd, data);

    // Write DECAY Register
    data = REGWRITE | (G_DECAY_REG.Address << 12) | (G_DECAY_REG.DECMOD << 8) | G_DECAY_REG.TDECAY;
    SPI_DRV8711_Write(fd, data);

    // Write STALL Register
    data = REGWRITE | (G_STALL_REG.Address << 12) | (G_STALL_REG.VDIV << 10) | (G_STALL_REG.SDCNT << 8) | G_STALL_REG.SDTHR;
    SPI_DRV8711_Write(fd, data);

    // Write DRIVE Register
    data = REGWRITE | (G_DRIVE_REG.Address << 12) | (G_DRIVE_REG.IDRIVEP << 10) | (G_DRIVE_REG.IDRIVEN << 8) | (G_DRIVE_REG.TDRIVEP << 6) | (G_DRIVE_REG.TDRIVEN << 4) | (G_DRIVE_REG.OCPDEG << 2) | (G_DRIVE_REG.OCPTH);
    SPI_DRV8711_Write(fd, data);

    // Write STATUS Register
    data = REGWRITE | (G_STATUS_REG.Address << 12) | (G_STATUS_REG.STDLAT << 7) | (G_STATUS_REG.STD << 6) | (G_STATUS_REG.UVLO << 5) | (G_STATUS_REG.BPDF << 4) | (G_STATUS_REG.APDF << 3) | (G_STATUS_REG.BOCP << 2) | (G_STATUS_REG.AOCP << 1) | (G_STATUS_REG.OTS);
    SPI_DRV8711_Write(fd, data);
}

 

The only function I had to write myself was the method to write 16 bit fields over SPI on Linux. It's based on the code of this blog: BeagleBone: Enable SPI with Overlay and from Command Line.

 

void SPI_DRV8711_Write(int fd, uint16_t data) {
    int ret;
    uint16_t tx[] = {data};

    struct spi_ioc_transfer tr = {
        .tx_buf = (unsigned long)tx,
        .rx_buf = (unsigned long)NULL,
        .len = (sizeof(tx)/sizeof(*tx))*2,
        .delay_usecs = delay,
        .speed_hz = speed,
        .bits_per_word = bits,
    };

    ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
    if (ret < 1)
        pabort("can't send spi message");
}

 

Here's the result captured by a logic analyser. The CS is not correct. The grab was made before I corrected the CS polarity.

 

image

I have the advantage that I've captured the traffic before on other microcontrollers. I can compare and validate.

The results are as expected.

 

Install the code to the bin directory in your home.

 

I'll now combine all previous work:

  • initialise PRU pins
  • run SPI init
  • control reset and sleep pins via Linux command line
  • run the PRU ocde to make the motor step

That's not a lot of work left. Hang on ...

 

 

Related blog:
BeagleBone Control Stepper Motors with PRU - Part 1: Intentions
BeagleBone Control Stepper Motors with PRU - Part 2: Test Driving Outputs
BeagleBone: Enable SPI with Overlay and from Command Line
BeagleBone Control Stepper Motors with PRU - Part 3: Hardware Provisioning and Wiring
BeagleBone Control Stepper Motors with PRU - Part 4: SPI Setup
BeagleBone Control Stepper Motors with PRU - Part 5: It Works
Attachments:
bb_LINUX_STEPPER_SPI_20190802.zip
6136.bb_LINUX_STEPPER_SPI_20190802.zip
  • Sign in to reply
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 © 2023 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