BeagleBone and Software Development Using C++ PDF
BeagleBone and Software Development Using C++ PDF
BeagleBone and Software Development Using C++ PDF
Potentiometer Demo
In this demonstration of a voltage divider, a fixed resistor of 1k ohms is wired in series with a potentiometer that can vary between 100 and 10k ohms.
The white wire at the connection between the components is the sample point which will change in voltage as the dial on the potentiometer is turned. When the
potentiometer is at its maximum resistance the sample voltage will be around R2/(R1+R2) * VADC = 10,000/(10,000+1,000) * 1.8 ~ 1.64 V. When the potentiometer is
set to a low resistance then the sample voltage will approach 0.1 V. My fixed resistor actually measured 0.98k ohms, and the potentiometer measured 10.4 k ohms at max
and 98 ohms at min.
You can read back the current ADC value on the bash command line quite simply:
Command: /sys/bus/iio/devices/iio:device0$ cat in_voltage0_raw
Output: 4093
So just take the ratio of this digital value over 4096 and multiply by 1.8 to obtain a voltage measurement. Here is the basic concept in C++ that will continuously sample
ADC0 every 500 milliseconds. Accuracy in my tests were within 5-10mV of the value measured on my multimeter.
#include <iostream>
#include <fstream>
#include <string>
#include <chrono>
#include <thread>
#include <iomanip>
int main()
{
const string adc0_dev_ = "/sys/bus/iio/devices/iio:device0/in_voltage0_raw";
const unsigned int MAX_ADC_BB_OUT = 4096;
const double MAX_VADC_BB = 1.8;
while (true)
{
//open the ADC0 pin as a file object
std::ifstream ain0(adc0_dev_);
if (ain0)
{
//read the raw value
string line;
if(getline(ain0, line))
{
auto raw = std::stoul( line );
double measured_voltage = ((double)raw / MAX_ADC_BB_OUT ) * MAX_VADC_BB; //12-bit ADC
cout << setprecision(3) << measured_voltage << " V" << endl;
}
}
this_thread::sleep_for(std::chrono::milliseconds(500));
}
return 0;
}
GPIO
Additional GPIO options can be exposed and manipulated on the command line in a similar way. The GPIOs are arranged in banks of 32 at the filesystem location:
/sys/class/gpio
int main()
{
// the button is connected to GPIO_30
int fd = open("/sys/class/gpio/gpio30/value", O_RDONLY);
while (true)
{
//1 ms-polling
int ready = poll(&fdset, 1, 1);
if (ready < 0)
{
cout << "poll() failed" << endl;
}
if (value == 1)
{
cout << "Button is pressed" << endl;
}
}
}
}
Device trees can also be built to load specific GPIO pin configurations at boot so you do not have to expose them via bash commands, they get pre-loaded for you.
Fortunately, people have created device tree generation tools to do this especially for the BeagleBone. You just need to make sure you enter the right pin information into
the generator. You can then compile the device tree and add them to the list of device trees on the “cape_enable=bone_capemgr.enable_partno=” line in /boot/uEnv.txt.
We will review this a little more in the next section.
Alternately, to read data from a target register, you just write a single byte containing the target address, and then read back a byte from that target.
char read_byte;
if(write(g_i2cFile, buffer, 1) == 1)
{
if(read(g_i2cFile, &read_byte, 1) !=1)
{
// successfully read a byte into read_byte;
}
}
The system implements a Proportional Integral Derivative (PID) controller. The controlled parameter is the rotational speed of a DC motor. The DC motor is fitted with a
quadrature encoder which provides both RPM and directional data.
The quadrature encoder outputs are connected to the P8 header on the BeagleBone. The DC motor is controlled with a Pulse Width Modulation
(PWM) signal from a pin on the P9 header. The PWM peripheral resides on PRU1. The quadrature decoder peripheral is located outside the PRU
and is accessed via the internal bus.
The PID loop is a controlled by a userspace program. The program communicates with the PRU via character devices instantiated with the
RemoteProc messaging driver.
A web server runs on the BeagleBone and allows a web browser to graph the control loop behavior.
Resources
The source code is shared on GitHub:
Greg-R/pru-pid-motor
Preparation
In this tutorial we are using a fresh Raspbian Stretch System (Version November 2017 – Release date 2017-11-29) with the latest updates:
Download and install the bcm2835 library. Note XXX is a placeholder for the latest version. For this guide version “1.52” was used. In this case,
XXX is replaced by “1.52”. You can check the latest version by scrolling to the bottom of the following page:
http://www.airspayce.com/mikem/bcm2835/
wget http://www.airspayce.com/mikem/bcm2835/bcm2835-XXX.tar.gz
tar zxvf bcm2835-XXX.tar
cd bcm2835-XXX
./configure
make
sudo make check
sudo make install
Create a project folder and download the latest TMC-API. Note X.XX is a placeholder for the latest version. For this guide version “3.02” was
used. In this case, X.XX is replaced by “3.02”. You can check the latest version by scrolling to the bottom of the following page:
https://www.trinamic.com/support/software/access-package/
mkdir ~/TMC_EXAMPLE
cd ~/TMC_EXAMPLE
wget https://www.trinamic.com/fileadmin/assets/Products/Eval_Software/TMC-API_Release_vX.XX.zip
unzip TMC-API_Release_vX.XX.zip
mv TMC-API_Release_vX.XX TMC-API
Wiring
The wiring is very simple. You will need 8 jumper wires. As a reference you can use the TMC5160-BOB_datasheet_Rev1.0.pdf. You will find the
signals that are on each pin.
TMC5160-BOB
Raspberry Pi Code
An example code to initialize the TMC5160 is shown below. These files need to be placed in the same project folder as the TMC-API, in this case
into ~/TMC_EXAMPLE. First, +3.3V is supplied to VCC_IO, the driver stages are enabled by pulling down DRV_ENN and the internal clock is
used by pulling down CLK16. Afterwards, the TMC5160 is initialized and a simple move to position cycle is executed. The sequence will rotate a
200 full stepper motor 10 revolutions clockwise and 10 revolutions counterclockwise – depending on the wiring of the stepper motor. Please refer
to the TMC5160 datasheet or the TMCL-IDE as a reference for the different registers.
You can also download the source files directly with your Pi:
cd ~/TMC_EXAMPLE
wget http://blog.trinamic.com/wp-content/uploads/2018/01/TMC5160_TMCAPI_EXAMPLE.tar.gz
tar zxvf TMC5160_TMCAPI_EXAMPLE.tar.gz
main.c
#include <stdio.h>
#include <wiringPi.h>
#include <bcm2835.h>
#include "SPI_TMC.h"
// Include the IC(s) you want to use
#include "TMC-API/tmc/ic/TMC5160/TMC5160.h"
#define MOTOR0 0 // Motor 0
void resetMotorDrivers(uint8 motor);
int main(int argc, char **argv) {
if (!bcm2835_init())
return 1;
wiringPiSetupGpio();
// Initiate SPI
bcm2835_spi_begin();
bcm2835_spi_setBitOrder(BCM2835_SPI_BIT_ORDER_MSBFIRST); // MSB first
bcm2835_spi_setDataMode(BCM2835_SPI_MODE3); // SPI Mode 3
bcm2835_spi_setClockDivider(BCM2835_SPI_CLOCK_DIVIDER_256); // 1 MHz clock
bcm2835_spi_chipSelect(BCM2835_SPI_CS0); // define CS pin
bcm2835_spi_setChipSelectPolarity(BCM2835_SPI_CS0, LOW); // set CS polarity to low
/***** TMC5160-BOB Example *****/
pinMode(2, OUTPUT);
digitalWrite(2, HIGH); // Apply VCC_IO voltage to Motor 0
pinMode(3, OUTPUT);
digitalWrite(3, LOW); // Use internal clock
pinMode(4, OUTPUT);
digitalWrite(4, LOW); // Enable driver stage
resetMotorDrivers(MOTOR0);
// MULTISTEP_FILT=1, EN_PWM_MODE=1 enables stealthChop™
tmc5160_writeInt(MOTOR0, TMC5160_GCONF, 0x0000000C);
// TOFF=3, HSTRT=4, HEND=1, TBL=2, CHM=0 (spreadCycle™)
tmc5160_writeInt(MOTOR0, TMC5160_CHOPCONF, 0x000100C3);
// IHOLD=10, IRUN=15 (max. current), IHOLDDELAY=6
tmc5160_writeInt(MOTOR0, TMC5160_IHOLD_IRUN, 0x00080F0A);
// TPOWERDOWN=10: Delay before power down in stand still
tmc5160_writeInt(MOTOR0, TMC5160_TPOWERDOWN, 0x0000000A);
// TPWMTHRS=500
tmc5160_writeInt(MOTOR0, TMC5160_TPWMTHRS, 0x000001F4);
// Values for speed and acceleration
tmc5160_writeInt(MOTOR0, TMC5160_VSTART, 1);
tmc5160_writeInt(MOTOR0, TMC5160_A1, 5000);
tmc5160_writeInt(MOTOR0, TMC5160_V1, 26843);
tmc5160_writeInt(MOTOR0, TMC5160_AMAX, 5000);
tmc5160_writeInt(MOTOR0, TMC5160_VMAX, 100000);
tmc5160_writeInt(MOTOR0, TMC5160_DMAX, 5000);
tmc5160_writeInt(MOTOR0, TMC5160_D1, 5000);
tmc5160_writeInt(MOTOR0, TMC5160_VSTOP, 10);
tmc5160_writeInt(MOTOR0, TMC5160_RAMPMODE, TMC5160_MODE_POSITION);
while(1)
{
// put your main code here, to run repeatedly:
printf("Reply of TMC5160: 0x%08x\n", tmc5160_readInt(MOTOR0, TMC5160_XACTUAL));
tmc5160_writeInt(MOTOR0, TMC5160_XTARGET, 0x0007D000);
//XTARGET = 512000 -> 10 revolutions with micro step = 256
delay(12000);
printf("Reply of TMC5160: 0x%08x\n", tmc5160_readInt(MOTOR0, TMC5160_XACTUAL));
tmc5160_writeInt(MOTOR0, TMC5160_XTARGET, 0x00000000); //XTARGET=0
delay(12000);
}
// End SPI communication
bcm2835_spi_end();
bcm2835_close();
return 0;
}
void resetMotorDrivers(uint8 motor)
{
if(!tmc5160_readInt(MOTOR0, TMC5160_VACTUAL))
{
digitalWrite(2, LOW); // Apply VCC_IO voltage to Motor 0
delay(10);
digitalWrite(2, HIGH); // Apply VCC_IO voltage to Motor 0
delay(10);
}
}
SPI_TMC.h
/*
* SPI_TMC.h
*
*Created on: 12.01.2018
*Author: MN
*/
#ifndef SPI_TMC_H
#define SPI_TMC_H
#include "TMC-API/tmc/helpers/API_Header.h"
void initSPI(void);
// TMC5160 SPI wrapper
void tmc5160_writeDatagram(uint8 motor, uint8 address, uint8 x1, uint8 x2, uint8 x3, uint8 x4);
void tmc5160_writeInt(uint8 motor, uint8 address, int value);
int tmc5160_readInt(u8 motor, uint8 address);
// General SPI functions
void tmc40bit_writeInt(u8 motor, uint8 address, int value);
int tmc40bit_readInt(u8 motor, uint8 address);
#endif /* SPI_TMC_H */
SPI_TMC:C
#include <bcm2835.h>
#include "SPI_TMC.h"
// TMC5160 SPI wrapper
void tmc5160_writeDatagram(uint8 motor, uint8 address, uint8 x1, uint8 x2, uint8 x3, uint8 x4)
{
int value = x1;
value <<= 8;
value |= x2;
value <<= 8;
value |= x3;
value <<= 8;
value |= x4;
tmc40bit_writeInt(motor, address, value);
}
void tmc5160_writeInt(uint8 motor, uint8 address, int value)
{
tmc40bit_writeInt(motor, address, value);
}
int tmc5160_readInt(u8 motor, uint8 address)
{
tmc40bit_readInt(motor, address);
return tmc40bit_readInt(motor, address);
}
// General SPI decription
void tmc40bit_writeInt(u8 motor, uint8 address, int value)
{
char tbuf[5];
tbuf[0] = address | 0x80;
tbuf[1] = 0xFF & (value>>24);
tbuf[2] = 0xFF & (value>>16);
tbuf[3] = 0xFF & (value>>8);
tbuf[4] = 0xFF & value;
bcm2835_spi_writenb(tbuf, 5);
}
int tmc40bit_readInt(u8 motor, uint8 address)
{
char tbuf[5], rbuf[5];
int value;
// clear write bit
tbuf[0] = address & 0x7F;
bcm2835_spi_transfernb (tbuf, rbuf, 5);
value =rbuf[1];
value <<= 8;
value |= rbuf[2];
value <<= 8;
value |= rbuf[3];
value <<= 8;
value |= rbuf[4];
return value;
}
Makefile
TARGET_EXEC ?= TMCAPI_EXAMPLE
BUILD_DIR ?= ./bin
CC = gcc
CXX = g++
# C Fags
CFLAGS += -Wall
CFLAGS += -g
LDFLAGS += -lbcm2835
LDFLAGS += -lwiringPi
# define the C source files
SRCS += main.c
SRCS += SPI_TMC.c
# used functions from TMC_API
SRCS += TMC-API/tmc/helpers/Debug.c
#SRCS += TMC-API/tmc/ic/TMC2130/TMC2130.c
#SRCS += TMC-API/tmc/ic/TMC2208/TMC2208.c
#SRCS += TMC-API/tmc/ic/TMC2224/TMC2224.c
#SRCS += TMC-API/tmc/ic/TMC2660/TMC2660.c
#SRCS += TMC-API/tmc/ic/TMC5130/TMC5130.c
#SRCS += TMC-API/tmc/ic/TMC5160/TMC5160.c
#SRCS += TMC-API/tmc/ic/TMC4330/TMC4330.c
#SRCS += TMC-API/tmc/ic/TMC4331/TMC4331.c
#SRCS += TMC-API/tmc/ic/TMC4361/TMC4361.c
#SRCS += TMC-API/tmc/ic/TMC4361A/TMC4361A.c
#SRCS += TMC-API/tmc/ic/TMC4670/TMC4670.c
#SRCS += TMC-API/tmc/ic/TMC4671/TMC4671.c
#SRCS += TMC-API/tmc/ic/TMC4672/TMC4672.c
#SRCS += TMC-API/tmc/ic/TMCC160/TMCC160.c
#SRCS += TMC-API/tmc/ic/TMC5041/TMC5041.c
#SRCS += TMC-API/tmc/ic/TMC5062/TMC5062.c
#SRCS += TMC-API/tmc/ic/TMC5072/TMC5072.c
OBJS := $(SRCS:%=$(BUILD_DIR)/%.o)
DEPS := $(OBJS:.o=.d)
$(BUILD_DIR)/$(TARGET_EXEC): $(OBJS)
$(CC) $(OBJS) -o $@ $(LDFLAGS)
# assembly
$(BUILD_DIR)/%.s.o: %.s
$(MKDIR_P) $(dir $@)
$(AS) $(ASFLAGS) -c $< -o $@ $(LDFLAGS)
# c source
$(BUILD_DIR)/%.c.o: %.c
$(MKDIR_P) $(dir $@)
$(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@ $(LDFLAGS)
# c++ source
$(BUILD_DIR)/%.cpp.o: %.cpp
$(MKDIR_P) $(dir $@)
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $< -o $@ $(LDFLAGS)
.PHONY: clean
clean:
$(RM) -r $(BUILD_DIR)
-include $(DEPS)
MKDIR_P ?= mkdir -p
cd ~/TMC_EXAMPLE
make
Now you are able to execute this example.
sudo ~/TMC_EXAMPLE/bin/TMCAPI_EXAMPLE
Be aware that the motor runs as soon as you execute the program.
Related Pages
TMCL-IDE
TMC5160
TMC5160-BOB
TRINAMIC Technology Access Package-TMC-API
www.trinamic.com
https://www.codeproject.com/
https://www.hackster.io/Greg-R/pru-pid-motor-speed-controller-with-beaglebone-green-ccb805
https://github.com/Greg-R/pru-pid-motor
https://blog.trinamic.com/2018/02/19/stepper-motor-with-tmc5160/
Download source and examples
edgar.guarecuco54@gmail.com
Made with LibreOffice Writer by Edgar Guarecuco.
MSc Electronic Engineering Degree at UNEXPO. Barquisimeto. Venezuela. CNC Creativa.
With pride from the Bolivarian Republic of Venezuela.
Calle 15 entre carreras 28 y 29 # 28-71.Barquisimeto. ZIP Code 3001.República Bolivariana de Venezuela.