Sei sulla pagina 1di 2

A Novice's Guide to the BeagleBone Platform and

Software Development Using C++


Eric D Schmidt
A crash course in working with the BeagleBone and its built-in A/D converter, GPIO, and I2C bus using C++
OS Images
There are a few flavors of official images you can choose to install on the BeagleBone. Generally, for IOT types of activities, I would recommend starting with a
“console” image, rather than a full blown LXDE desktop. You can always install X.Org and GUI libraries later if you are doing some kind of kiosk type of project.
Recently, “iot” image spins have been created, which basically contain all the packages that come with the LXDE version like bonescript and node.js, but without LXDE
or X.Org. “Console” images eliminate all the node.js and bonescript tooling, and weigh in at less than 50MB, but may lack some compilers and tooling you may need. Its
a Debian system, so you can always add what you need with apt-get.
You may want to install any necessary drivers for you OS if you would like to access the BeagleBone Black via SSH over the mini-USB connector: 192.168.7.2 . If you
are working with the BeagleBone Green variant, its a micro-USB connector and just about any cell phone charging cable will do. Otherwise, use the DHCP ethernet
address or connect a FTDI serial debugging cable. I haven't needed the HDMI output of the BeagleBone Black, so I've grown accustomed to working with them over SSH
and SFTP. If you are a Windows user, I would highly recommend MobaXterm as it makes it extremely easy to SSH and copy files via an integrated file explorer that
implements SFTP, otherwise Putty. Obviously a Linux user can do it all on the command line, but I have found PAC Manager to also be useful.

Built-in Hardware Options


In order to get the most out of the BeagleBone, you can stack “capes” on it in order to expose additional I/O. You should be warned though, that not all third-party capes
will work with one another, occasional you may run into a conflict in GPIO usage, particularity when dealing with LCD screen capes. If you stick to the capes defined in
the official overlay repository, you will have more success.
On the other hand, exposing additional I/O functionality built into the BeagleBone's processor is pretty easy. Refer to the official diagrams here in order to identify the pin
locations of all I/O. The functionality and accessibility of I/O can be controlled by editing /boot/uEnv.txt. This file controls the loading of various hardware overlays,
allowing operating system access to different sets of I/O or hardware configurations. For example, to access the internal 12-bit A/D converter, add the following built-in
overlay to the /boot/uEnv.txt file:
cape_enable=bone_capemgr.enable_partno=BB-ADC
On the next reboot the A/D pins will be accessible in the Linux file system at:
/sys/bus/iio/devices/iio:device0
You should see a series of files in_voltage0_raw...in_voltage6_raw, which correspond to the 7 channels of A/D conversion available to you. The maximum A/D reference
voltage is 1.8V which corresponds to a max 12-bit digital value of 4096, so taking a measurement just means applying your reference voltage to VDD_ADC (P8_32),
ground to GND_ADC (P9_34), and the output of whatever you are measuring to one of AIN0...AIN6.

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>

using namespace std;

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

GPIO Output LED Demo


A GPIO can be set to either output, if you want to write to it in order to flip the pin voltage from 0 to 3V, or input, if you want to detect a change in voltage applied to the
pin. We can demonstrate an output easily be connecting an LED and at least a 220 ohm resistor with one of the GPIO pins, and a DGND pin. In this example, I have an
LED connected on its shorter leg (cathode) to DGND, and the anode side to GPIO_30 (P9 pin 11).

To expose GPIO_30 control on the file system:


echo 30 > /sys/class/gpio/export
A new folder will appear, “gpio30”. Inside are a few files we can read/write to in order to control functionality. Writing a 1 or 0 to the value file will trigger voltage on the
pin and blink the LED on and off.
echo out > /sys/class/gpio/gpio30/direction
echo 1 > /sys/class/gpio/gpio30/value
echo 0 > /sys/class/gpio/gpio30/value
GPIO Input Button Demo
An example of an input GPIO would be sensing a button push. Apply 3V from VDD_3V3 (P9 pins 3 or 4) to one leg of the button, and a 1k ohm resistor connected to
DGND (pin 1 or 2) to the other leg. Run a wire from between the resistor and the button to GPIO_30. This “pull down” resistor setup will send the voltage to the input pin
when the button is depressed.

Set GPIO_30 as an input:


echo in > /sys/class/gpio/gpio30/direction
When you press the button, the file named "value: should have a value of 1, and a value of 0 when not pressed.
If you want to be efficient with the use of system resources, I would not recommend continuously looping code to read the value of the GPIO to look for value of 1.
Embedded developers frequently use “interrupts” to detect these kinds of events. Linux can wait on file system events using the poll() function. The poll function can
block for a specified amount of time, but will not significantly tax system resources while doing so. A C++ example of the button demonstration follows.
#include <iostream>
#include <stdio.h>
#include <poll.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>

using namespace std;

int main()
{
// the button is connected to GPIO_30
int fd = open("/sys/class/gpio/gpio30/value", O_RDONLY);

struct pollfd fdset;

memset((void*) &fdset, 0, sizeof(fdset));


fdset.fd = fd;
fdset.events = POLLPRI;
fdset.revents = 0;
char c[1];

while (true)
{
//1 ms-polling
int ready = poll(&fdset, 1, 1);

if (ready < 0)
{
cout << "poll() failed" << endl;
}

// polling will trigger on the rising edge of the GPIO


// (transition from 0 -> 1)
if ((fdset.revents & POLLPRI))
{
c[0] = 0;
lseek( fdset.fd, 0, 0 );
int size = read(fdset.fd, c, 1);
int value = -1;
if (size != -1)
{
value = atoi(c);
}

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.

Additional Expansion Options Using I2C – Adding a Real Time Clock


The BeagleBone does not have a built-in battery backed-up RTC, and a number of the existing tutorials on adding one are out of date or load the clock rather late in the
boot process and require hacks in rc.local to keep it synced. Your best bet is to pick an RTC that has built in linux driver support, or has a module that is easy to compile
without rebuilding the entire kernel.
Linux uses “device trees” to describe and initialize non-discoverable hardware. Devices that are connected to I2C or SPI buses generally fall into this category. In order to
properly use an add-on I2C RTC clock with the BeagleBone, you will want to load a device tree for it in uEnv.txt. If you are not using the standard BeagleBone RTC
cape, you may have to generate your own.
I have had success with an Epson RX-8900, it's small, cheap, reasonably accurate, and does not give off a lot of EMF. This RTC is a bit of a special case, in that its I2C
interface is register-compatible with another device driver that is already built into the stock OS image “rtc-rv8803”. So we can tell Linux to load that driver module on
boot by adding rtc-rv8803 to the list of devices in /etc/modules.
We now need to create a device tree so the OS knows what the I2C and pin configuration should be to communicate with the RTC. We can just use the BeagleBone RTC
cape BB-RTC-01-00A0.dts file as a starting point, and change all references from its chip manufacturer to whatever you are using, just make sure you define the right pins
for the I2C bus you are using. If you use i2c1, then you only need to change the driver name. Change the line:
compatible = "maxim,ds1338";
to
compatible = "epson,rv8803";
If you are using i2c2, then remember the corresponding pins are instead P9_19 and P9_20. I have attached an example of an overlay I use on i2c2. Finally, change the
part-number line “part-number = "BB-RTC-01";” to a new name, and save it. You can then compile it to a device tree. If you pull down the BeagleBone overlays via git,
you can just add it to the src/arm directory, and follow the simple directions in that repository to rebuild and reinstall all the latest overlays.
Basically:
git clone https://github.com/beagleboard/bb.org-overlays
cd ./bb.org-overlays
./dtc-overlay.sh
./install.sh
Just add the new DTC name to the “cape_enable=bone_capemgr.enable_partno” line in uEnv.txt, and the RTC driver will load from boot.
The last step is to make sure Linux uses /dev/rtc1 as its primary RTC device. If you look in /dev, you will see that the /dev/rtc file is a symlink to /dev/rtc0, which is the
built-in BeagleBone RTC that has no battery backup. The Linux component “udev” is responsible for this at boot, to switch it to use rtc1, create a udev rule by issuing the
following command as root:
echo "SUBSYSTEM=="rtc", KERNEL=="rtc1", SYMLINK+="rtc", OPTIONS+="link_priority=10", TAG+="systemd"" > /etc/udev/rules.d/55-i2c-rtc.rules
Systemd has built in functionality for syncing system clocks with NTP servers. To be sure its enabled issue the command:
timedatectl set-ntp true
Once it has synced up the system time, you can then set the RTC and read back its time with:
hwclock -w -f /dev/rtc1
hwclock -r -f /dev/rtc1
From here on out, the OS should automatically sync up /dev/rtc with the system time.

Direct Control of I2C Devices


I would just briefly note, just because you might not have a driver available to you, you can still work with I2C devices directly in code very easily in Linux. In fact,
sometimes you are better off rolling your own code, depending on your application needs. Once again, its similar to reading and writing on the file system.
Step one, open a file that corresponds to the I2C bus you want to communicate on:
g_i2cFile = open("/dev/i2c-2", O_RDWR);
Step two, write the address of the device you want to talk to using “ioctl”. Every device on the bus should have a unique address.
auto res = ioctl(g_i2cFile, I2C_SLAVE, DEVICE_ADDR);
Step three, write the data to the device on the target register:
const int BUFSIZE = 2;
char buffer[BUFSIZE];
buffer[0] = REG_ADDR_TARGET;
buffer[1] = data_byte;

if(write(g_i2cFile, buffer, BUFSIZE) != BUFSIZE)


{
//error
}

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;
}
}

I have included a simple C++ class that implements this functionality.

Options for Compiling Software on the BeagleBone


Cross Compiler via QEMU
One of the easiest ways to compile software for the BeagleBone on a different machine is via QEMU. On another machine running Debian:
apt-get install qemu-user-static
Now you need to get a copy of the BeagleBone file system you are using. Use rsync or tar up the image.
mkdir -p /opt/beagle-root
cd /opt/beagle-root
rsync -Wa --progress --exclude=/proc --exclude=/sys --exclude=/home/debian --delete bms@192.168.7.2:/*.
Copy the static ARM binary that provides emulation to this location:
cp $(which qemu-arm-static) /opt/beagle-root/usr/bin
At this point, you can enter the commands:
cd /opt
sudo chroot beagle-root /usr/bin/qemu-arm-static /bin/bash
You will then be running BeagleBone ARM binaries on the new system, through QEMU. You can now build any software just like normal. Any libs or binaries you create
can be copied back onto the BeagleBone and will run normally.
Swap Space Tip
If you are patient and want to compile larger software packages directly on the BeagleBone, you may run up against memory limitations. In such a case, try creating some
temporary swap space. Here is a bash script I use instead of calling “make” directly:
#!/bin/sh -e
#
# compile software directly on BB if swap space is needed
#
free
#64 MB swap file
sudo dd if=/dev/zero of=/var/swap.img bs=1024 count=65535
sudo mkswap /var/swap.img
sudo swapon /var/swap.img
free
make
sudo swapoff /var/swap.img
sudo rm /var/swap.img
exit 0
Conclusion
Most of the information presented here I discovered over a number of months of digging through documentation and blogs, and a lot of trial and error. Hopefully, this
gives you a bit of a head start. To dive into some of these topics in greater detailed I have cited a number of sources from which I leaned this material.
Images
Official releases: https://beagleboard.org/latest-images
Bleeding-edge and console images: http://elinux.org/Beagleboard:BeagleBoneBlack_Debian
Kernels, drivers and device trees
Latest boot scripts and flashing utilities: https://github.com/RobertCNelson/boot-scripts.git
Supported cape overlays: https://github.com/beagleboard/bb.org-overlays.git
Kernel: https://github.com/beagleboard/linux.git
BeagleBone I/O and pin layout: http://beagleboard.org/support/bone101
Device Overlays: https://github.com/beagleboard/bb.org-overlays.git
Ethernet over USB drivers: http://beagleboard.org/getting-started
RTC Examples
Official BeagleBone RTC Cape: http://elinux.org/CircuitCo:RTC_Cape
Epson RX-8900 Docs: https://support.epson.biz/td/api/doc_check.php?dl=brief_RX8900CE&lang=en
Battery-backed BeagleBone RTC Guide: http://blog.fraggod.net/2015/11/25/replacing-built-in-rtc-with-i2c-battery-backed-one-on-beaglebone-black-from-boot.html
Detailed Demos
A/D Converter: https://www.linux.com/learn/how-get-analog-input-beaglebone-black
GPIO Ouput LED: http://www.dummies.com/how-to/content/setting-beaglebone-gpios-as-outputs.html
GPIO Input Button: https://learn.adafruit.com/connecting-a-push-button-to-beaglebone-black/overview
Useful SSH and SFTP Tools
MobaXterm: http://mobaxterm.mobatek.net/
Putty: http://www.putty.org/
PAC Manager: https://sourceforge.net/projects/pacmanager/

Motor Speed Control using BeagleBone PRU


Greg Raven created this motor speed controller based on a Texas Instruments demonstration project. His implementation uses RemoteProc and RPMsg framework to
connect the PRUs (Programmable Real-time Units) to Linux userspace. The PRUs are 32-bit RISC processors which offload real-time tasks from Linux running on the
ARM processor.

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

The project details are shared on Hackster.io:


PRU PID Motor Speed Controller

The main documentation file [PDF]:


Motor Speed Control Using Beaglebone PRU

Driving Stepper Motors with the new TMC5160 by using


TRINAMIC’s API on a Raspberry Pi
This start-up guide explains how to connect your Raspberry Pi 3 to the TMC5160-BOB – the breakout board of the brand new TMC5160. We’ll
be using the internal Motion Controller and +3V3 logic supply for the TMC5160. The wiring will be limited to the basic functionality to
communicate via SPI. The usage of the TRINAMIC’s API – TMC-API – is minimizing the software effort.

Preparation
In this tutorial we are using a fresh Raspbian Stretch System (Version November 2017 – Release date 2017-11-29) with the latest updates:

sudo apt update


sudo apt upgrade

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.

Signal Raspberry Pi 3 TMC5160-BOB


VCC_IO GPIO02 (3) VCC_IO (1)
GND GND (39) GND (2)
SPI Chip select (CS) SPI0 CE0 N (24) CSN (3)
SPI CLK SPI0 SCLK (23) SCK (4)
MOSI SPI0 MOSI (19) SDI (5)
MISO SPI0 MISO (21) SDO (6)
TMC5160 CLK GPIO04 (7) CLK (8)
TMC5160 DRV_ENN GPIO03 (5) DRV_ENN (9)

TMC5160-BOB

Raspberry Pi 3 GPIO Header – Source: https://www.element14.com/community/docs/DOC-73950/l/raspberry-pi-3-model-b-gpio-40-pin-block-pinout

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

Compiling and running the code


Use the following command to compile the code.

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.

Potrebbero piacerti anche