DRV8711 BOOST programming using Energia on MSP430F5529
I recently required the nice little DRV8711 stepper motor driver to run a large stepper motor. I especially like the ability to easily program it and use the internal microstepping indexer. If you have ever tried doing microstepping on a stepper motor, you would know that it is not that easy. The DRV8711 has an internal indexer which you can configure to do 1/2, 1/4….upto 1/256 stepping resolution. Additionally, the BOOSTER pack also has low Rds MOSFETs which do not get hot at all. If you prefer to create your own stepping sequence by manually turning the pins on and off, you can do so by disabling the internal indexer.
There is automatic fault and stall detection, so you do not have to worry about excess current running through the motor and burning the coils. Thanks to this mechanism, I was able to identify a faulty motor I had. One of the coils in the stepper motor had a burnt coil and that was causing excess current to be drawn because of ruined insulation (enamel coating). The auto-fault detection was stopping the motor from taking more than 1 step and then stalling it.
The status register gives full information about what fault has occurred. I did not feel the need to read the registers back as I was quite sure that my code was able to program the registers, but the undue stalling of motor made me quite frustrated and only then I decided to read back the registers. We will talk about this further down the line in this article. First let us have a look at how to program this little monster.
I am using Energia API because I am a lazy programmer that likes to have easy to use functions at hand to speed up the process.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
#include "Energia.h" #include<SPI.h> #include<msp430f5529.h> #include "usci_isr_handler.h" #include "HardwareSerial.h" void Initialize(); __interrupt void USCI0RX_ISR(void); #define CS BIT1 #define STEP BIT2 #define SLEEPn BIT6 #define DIR BIT1 #define RST BIT7 #define STALL BIT0 #define FAULT BIT2 int flag =0; int i,j; void Initialize(void); void getCurrentRegisters(); unsigned int WriteSPI(unsigned char dataHi, unsigned char dataLo); uint16_t ReadSPI(unsigned char address); String inputString; char in; void setup() { //output pins P8DIR |= CS; P4DIR |= (STEP|DIR); P2DIR |= (RST|STALL|FAULT); P6DIR |= SLEEPn; pinMode(P7_4, OUTPUT); digitalWrite(P2_2, HIGH); //nSLEEP = high digitalWrite(P2_0, HIGH); digitalWrite(P6_6, HIGH); digitalWrite(P4_1, HIGH); //dir = high digitalWrite(P2_7, LOW); //reset = low Serial.begin(115200); SPI.begin(); SPI.setClockDivider(2); delay(1); Initialize(); getCurrentRegisters(); inputString.reserve(8); digitalWrite(P7_4, HIGH); delayMicroseconds(10); digitalWrite(P7_4, LOW); } |
1 |
void Initialize(void) |
This function initializes all the registers on DRV8711 chip through SPI.
The entire function is shown below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
void Initialize() { //CTRL Register defaults unsigned char CTRLdataHi, CTRLdataLo; CTRLdataHi = 0x0C; CTRLdataLo = 0x45; WriteSPI(CTRLdataHi, CTRLdataLo); //TORQUE defaults unsigned char TORQUEHi, TORQUELo; TORQUEHi = 0x17; TORQUELo = 0xFF; WriteSPI(TORQUEHi, TORQUELo); //OFF defaults unsigned char OFFHi, OFFLo; OFFHi = 0x20; OFFLo = 0xF0; WriteSPI(OFFHi, OFFLo); //BLANK defaults unsigned char BLNKHi, BLNKLo; BLNKHi = 0x31; BLNKLo = 0xF0; WriteSPI(BLNKHi, BLNKLo); //DECAY defaults unsigned char DECAYHi, DECAYLo; DECAYHi = 0x41; DECAYLo = 0x10; WriteSPI(DECAYHi, DECAYLo); //STALL defaults unsigned char STALLHi, STALLLo; STALLHi = 0x53; STALLLo = 0x40; WriteSPI(STALLHi, STALLLo); //DRIVE defaults unsigned char DRIVEHi, DRIVELo; DRIVEHi = 0x60; DRIVELo = 0x0F; WriteSPI(DRIVEHi, DRIVELo); //STATUS defaults unsigned char STATUSHi, STATUSLo; STATUSHi = 0x70; STATUSLo = 0x00; WriteSPI(STATUSHi, STATUSLo); } |
The contents of each register can be modified as per your needs by carefully reading the data sheet.
1 |
unsigned int WriteSPI(unsigned char dataHi, unsigned char dataLo) |
As the function name says, it is used to write the register contents using SPI interface. You have to make sure that you use correct format to form your register word before writing. While writing, the MSB has to be low and the following 3 bits are register addresses. Thus, if you are writing the STALL register which has the address 0x05H, your first 4 bits will be 0101 and then your register content will follow. All registers are 16 bits in width.
When you want to read the register back, you have to form a word with MSB = 1 and the following three bits indicate the register address that you intend to read. So, for reading back the STALL register you will need your word to be 1101. Indeed, this is not the entire word, but you get the idea how it should begin.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
unsigned int WriteSPI(unsigned char dataHi, unsigned char dataLo) { // unsigned int readData = 0; // SPI.setBitOrder(MSBFIRST); // digitalWrite(P8_1, HIGH); // SPI.transfer(dataHi); // // SPI.transfer(dataLo); // digitalWrite(P8_1, LOW); unsigned int readData = 0; digitalWrite(P8_1,HIGH); UCB0TXBUF = dataHi; while (UCB0STAT & BUSY); readData |= (UCB0RXBUF << 8); UCB0TXBUF = dataLo; while (UCB0STAT & BUSY); readData |= UCB0RXBUF; digitalWrite(P8_1, LOW); readData &= 0x7FFF; return readData; } |
The above code gives you the entire insight into the function. The commented part shows that you can use the Energia SPI function to make your day easy, but I preferred to do it the other way. Additionally, the readData will contain the register value you intended to access only while reading the register, otherwise it would return null if you are writing to register.
1 |
void getCurrentRegisters() |
This function does exactly as it says, it fetches all the register values and prints it on the Serial terminal. Optionally, you can modify the code to read the registers and store it in a 16 bit wide variable if you prefer to do conditional programming or change the configuration automatically by taking feedback from the driver chip.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
void getCurrentRegisters() { Serial.print("\n-----------------------------------------------\n"); Serial.print("CTRL register "); Serial.print(WriteSPI(0x80, 0x00),HEX); Serial.print("\n"); Serial.print("TORQUE register "); Serial.print(WriteSPI(0x90, 0x00),HEX); Serial.print("\n"); Serial.print("OFF register "); Serial.print(WriteSPI(0xA0, 0x00),HEX); Serial.print("\n"); Serial.print("BLANK register "); Serial.print(WriteSPI(0xB0, 0x00),HEX); Serial.print("\n"); Serial.print("DECAY register "); Serial.print(WriteSPI(0xC0, 0x00),HEX); Serial.print("\n"); Serial.print("STALL register "); Serial.print(WriteSPI(0xD0, 0x00),HEX); Serial.print("\n"); Serial.print("DRIVE register "); Serial.print(WriteSPI(0xE0, 0x00),HEX); Serial.print("\n"); Serial.print("STATUS register "); Serial.print(WriteSPI(0xF0, 0x00),HEX); Serial.print("\n-----------------------------------------------\n"); } |
Now we arrive at the point when we have done initializing the register and got ourselves ready with necessary functions. All we have to do is get the motor actually RUNNING!
In order to do so, we have to send pulses to the STP pin on the chip. At every rising edge of the pulse, the indexer increments by one address location. So, we just have to make sure that we provide a pulse train for desired amount of time to cover the required angle or whatever you have to do. We can do that in the void loop()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
void loop() { //WriteSPI(0x70, 0x00); for(i=0; i<90; i++){ for(j=0; j<10275; j++){ digitalWrite(P4_2, LOW); delayMicroseconds(55); digitalWrite(P4_2, HIGH); delayMicroseconds(2); } } WriteSPI(0x0C, 0x47); //reverse the direction to return back for(i=0; i<90; i++){ for(j=0; j<10275; j++){ digitalWrite(P4_2, LOW); delayMicroseconds(55); digitalWrite(P4_2, HIGH); delayMicroseconds(2); } } WriteSPI(0x0C, 0x44); //Halt the motor while(1); } |
My application required me to cover a sweep of 90 degrees and return back. My motor was connected to a gear mechanism, so 90 degree sweep required about 10275 * 90 steps at 1/4 stepping.
I also reverse the direction of the motor once it has covered 90 degrees so it can come back to its original location.
Finally, I halt the motor and hook my process into infinite loop.
I hope you found this tutorial helpful. You can also choose to use an Arduino and use the same code for driving stepper motors. Do leave a response below so I can put more interest tutorials and articles in the future.
I really appreciate this code. I could not find a place to start until I found this. I am able to get a motor spinning with this code but the registers don’t appear to be set correctly. The 12bits of data read back from the registers does not match 12bits written into them. I did find that the results were different when I divided the SPI clock down by 16 instead of just 2.
Glad you found this useful. How much is the variation in the register read back? Are the bits significantly different on all the registers? You said results were different on a divide by 16. Did it perform well on 16 than 2?