- It is chambered for the equally rare.500 Tranter cartridge that drove a blunt nosed 350 grain bullet at 700 fps. This five shot revolver retains 90% nickel on the frame. The cylinder and butt cap nickel is mostly gone but the knurled bead and all line and foliate engraving is sharp. Checkering is all there but flat.
- ASME code pressure ratings are available from the factory upon request. Standard test pressure is 250 psig air under water. For pressures above 250 psig hydrostatic tests are performed. Ratings for carbon steel apply for -20°F to 500°F and are based on a 4 to 1 greater safety factor without corrosion 3.
- Tranter then introduced his 4th Model Tranter revolvers (circa 1856), which used a single trigger, and had what would today be called a conventional “double action” lock work. The Tranter patent revolver was the primary competitor with the Adams and Adams-Beaumont patent revolvers in England, and was also exported widely.
Note that irrespective of the movies Zulu (1964) and Zulu Dawn (1979), in 1879 at Isandlwana and Rorke’s Drift, it was revolvers like this, or the earlier Webley or Tranter.577/.600s, that the officers like Lts. Bromhead and Chard would have had, rather than the much later.455in Webley top-breaks shown in the films.
My last post, part 4 in the GPIO Programming series, showed some ways to program GPIO from Python. Python offers a number of advantages as a programming language including a short learning curve, no need for compilation, and the availability of many add-on modules. However, true compiled languages like C or C++ can offer some advantages in embedded applications, such as higher performance. Because you are working closer to the level of the hardware you don't have to worry about what a JIT compiler or interpreter might be doing. If desired, you can look at the compiler's generated code or even use in line assembly language. There is also no need to ship full source code to end users.
That said, you still should not expect to do hard real-time programming under a standard Linux-based system. But you often can do soft real-time, such as reading buttons and slow sensors, driving LEDs, motors, etc. In this installment we'll look at a couple of ways to control GPIO hardware from the C programming language. The examples will also be valid C++ programs.
Sysfs Example
As covered earlier in part 3 of this series, you can access GPIO pins through the file system using the sysfs interface. This is straightforward to do from C or C++. Here is an example program that toggles a GPIO pin every 100 milliseconds:
/*
Example of programming GPIO from C or C++ using the sysfs interface on
a Raspberry Pi.
Will toggle GPIO24 (physical pin 18) at a 100 millisecond rate for 10
seconds and then exit.
Jeff Tranter <<a href='mailto:[email protected]'>[email protected]</a>>
*/
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
// Export the desired pin by writing to /sys/class/gpio/export
int fd = open('/sys/class/gpio/export', O_WRONLY);
if(fd -1){
perror('Unable to open /sys/class/gpio/export');
exit(1);
}
if(write(fd, '24', 2)!=2){
perror('Error writing to /sys/class/gpio/export');
exit(1);
}
close(fd);
// Set the pin to be an output by writing 'out' to /sys/class/gpio/gpio24/direction
fd = open('/sys/class/gpio/gpio24/direction', O_WRONLY);
if(fd -1){
perror('Unable to open /sys/class/gpio/gpio24/direction');
exit(1);
}
if(write(fd, 'out', 3)!=3){
perror('Error writing to /sys/class/gpio/gpio24/direction');
exit(1);
}
close(fd);
fd = open('/sys/class/gpio/gpio24/value', O_WRONLY);
if(fd -1){
perror('Unable to open /sys/class/gpio/gpio24/value');
exit(1);
}
// Toggle LED 50 ms on, 50ms off, 100 times (10 seconds)
for(int i =0; i <100; i++){
if(write(fd, '1', 1)!=1){
perror('Error writing to /sys/class/gpio/gpio24/value');
exit(1);
}
usleep(50000);
if(write(fd, '0', 1)!=1){
perror('Error writing to /sys/class/gpio/gpio24/value');
exit(1);
}
usleep(50000);
}
close(fd);
// Unexport the pin by writing to /sys/class/gpio/unexport
fd = open('/sys/class/gpio/unexport', O_WRONLY);
if(fd -1){
perror('Unable to open /sys/class/gpio/unexport');
exit(1);
}
if(write(fd, '24', 2)!=2){
perror('Error writing to /sys/class/gpio/unexport');
exit(1);
}
close(fd);
// And exit
return0;
}
Example of programming GPIO from C or C++ using the sysfs interface on
a Raspberry Pi.
Will toggle GPIO24 (physical pin 18) at a 100 millisecond rate for 10
seconds and then exit.
Jeff Tranter <<a href='mailto:[email protected]'>[email protected]</a>>
*/
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
// Export the desired pin by writing to /sys/class/gpio/export
int fd = open('/sys/class/gpio/export', O_WRONLY);
if(fd -1){
perror('Unable to open /sys/class/gpio/export');
exit(1);
}
if(write(fd, '24', 2)!=2){
perror('Error writing to /sys/class/gpio/export');
exit(1);
}
close(fd);
// Set the pin to be an output by writing 'out' to /sys/class/gpio/gpio24/direction
fd = open('/sys/class/gpio/gpio24/direction', O_WRONLY);
if(fd -1){
perror('Unable to open /sys/class/gpio/gpio24/direction');
exit(1);
}
if(write(fd, 'out', 3)!=3){
perror('Error writing to /sys/class/gpio/gpio24/direction');
exit(1);
}
close(fd);
fd = open('/sys/class/gpio/gpio24/value', O_WRONLY);
if(fd -1){
perror('Unable to open /sys/class/gpio/gpio24/value');
exit(1);
}
// Toggle LED 50 ms on, 50ms off, 100 times (10 seconds)
for(int i =0; i <100; i++){
if(write(fd, '1', 1)!=1){
perror('Error writing to /sys/class/gpio/gpio24/value');
exit(1);
}
usleep(50000);
if(write(fd, '0', 1)!=1){
perror('Error writing to /sys/class/gpio/gpio24/value');
exit(1);
}
usleep(50000);
}
close(fd);
// Unexport the pin by writing to /sys/class/gpio/unexport
fd = open('/sys/class/gpio/unexport', O_WRONLY);
if(fd -1){
perror('Unable to open /sys/class/gpio/unexport');
exit(1);
}
if(write(fd, '24', 2)!=2){
perror('Error writing to /sys/class/gpio/unexport');
exit(1);
}
close(fd);
// And exit
return0;
}
Note that if you want to try running this program, it is recommended to run it as root, otherwise you may run into file locking timing issues with udev that cause access errors.
I did some benchmarking of this example on a Raspberry Pi 3B. With delays removed, it would toggle the GPIO pin at a rate of about 350 kilohertz. This gives some idea of what can be done at this level of programming. Without using other programming tricks, there is no guarantee that the program won't be periodically preempted to run other tasks.
The size of the binary when optimized and stripped was about 6K. and it had few dependencies on other shared libraries. This is several orders of magnitude smaller than Python, for example.
The Raspberry Pi also has hardware support for PWM on some pins, and other features that can help optimize programs like this, and there are other performance optimization techniques like direct GPIO register access. We may look at this in a future post.
WiringPi
Tranters Pistols
WiringPi [1] is a GPIO access library for the Raspberry Pi. Written in C, it is usable from C or C++ or any language than can call C APIs. Third party bindings for a number a number of languages including Ruby, Python, and Perl are also available.
Released under a GNU LGPLv3 license. it is available from Raspbian Linux or you can build it from source.
It supports a similar API to the Wiring IDE supported on Arduino microcontrollers, making it easier to port hardware control code between the Raspberry Pi and Arduino platforms.
As well as the Raspberry Pi GPIO pins, it also supports some add-on boards like the PiFace and Gertboard.
The gpio command we looked at in an earlier blog post is part of WiringPi.
The library provides basic functions to read and write pins, including pullup and pulldown resistors and hardware PWM. It also provides routines for software delays down to the microsecond level. There is also support for higher level protocols including serial, SPI, and I2C.
In practice, WiringPi is very easy to use. Functions are defined in the header file <wiringPi.h>. You call a setup function to initialize the library, set the GPIO pin modes, and then call methods to read or write. The trickiest issue is probably getting the pin numbers correct - WiringPi uses a pin numbering convention based on the Broadcom SOM channel names, not physical pins. The documentation covers this (and the command gpio readall will also show it in tabular form). There is also a function you can call to convert physical pin numbers to WiringPi numbers.
Here is a simple example that toggles an LED connected to pin 18. It should be straightforward to understand from the source code:
/*
Example of programming GPIO from C or C++ using the WiringPi library
on a Raspberry Pi.
Will continuously toggle GPIO24 (physical pin 18) at a 500 millisecond
rate.
Jeff Tranter <<a href='mailto:[email protected]'>[email protected]</a>>
*/
#include <wiringPi.h>
int main(void)
{
// Red LED: Physical pin 18, BCM GPIO24, and WiringPi pin 5.
constint led =5;
wiringPiSetup();
pinMode(led, OUTPUT);
while(1){
digitalWrite(led, HIGH);
delay(500);
digitalWrite(led, LOW);
delay(500);
}
return0;
}
Example of programming GPIO from C or C++ using the WiringPi library
on a Raspberry Pi.
Will continuously toggle GPIO24 (physical pin 18) at a 500 millisecond
rate.
Jeff Tranter <<a href='mailto:[email protected]'>[email protected]</a>>
*/
#include <wiringPi.h>
int main(void)
{
// Red LED: Physical pin 18, BCM GPIO24, and WiringPi pin 5.
constint led =5;
wiringPiSetup();
pinMode(led, OUTPUT);
while(1){
digitalWrite(led, HIGH);
delay(500);
digitalWrite(led, LOW);
delay(500);
}
return0;
}
Here is an example of reading an input and showing its level:
/*
Example of programming GPIO from C or C++ using the WiringPi library
on a Raspberry Pi.
Will read a pushbutton switch on GPIO6 (physical pin 31) every 500
milliseconds and report the status. seconds and then exit.
Jeff Tranter <<a href='mailto:[email protected]'>[email protected]</a>>
*/
#include <stdio.h>
#include <wiringPi.h>
int main(void)
{
// Switch: Physical pin 31, BCM GPIO6, and WiringPi pin 22.
constint button =22;
wiringPiSetup();
pinMode(button, INPUT);
while(1){
if(digitalRead(button) LOW){
fprintf(stderr, 'Switch is pressedn');
}else{
fprintf(stderr, 'Switch is releasedn');
}
delay(500);
}
return0;
}
Example of programming GPIO from C or C++ using the WiringPi library
on a Raspberry Pi.
Will read a pushbutton switch on GPIO6 (physical pin 31) every 500
milliseconds and report the status. seconds and then exit.
Jeff Tranter <<a href='mailto:[email protected]'>[email protected]</a>>
*/
#include <stdio.h>
#include <wiringPi.h>
int main(void)
{
// Switch: Physical pin 31, BCM GPIO6, and WiringPi pin 22.
constint button =22;
wiringPiSetup();
pinMode(button, INPUT);
while(1){
if(digitalRead(button) LOW){
fprintf(stderr, 'Switch is pressedn');
}else{
fprintf(stderr, 'Switch is releasedn');
}
delay(500);
}
return0;
}
This final example will cycle through three LEDs in a binary pattern. If the pushbutton is pressed, it will turn off all LEDs and exit.
/*
Example of programming GPIO from C or C++ using the WiringPi library
on a Raspberry Pi.
Will cycle through three LEDs in a binary pattern. If the pushbutton is
pressed, it will turn off all LEDs and exit.
Jeff Tranter <<a href='mailto:[email protected]'>[email protected]</a>>
*/
#include <stdio.h>
#include <stdlib.h>
#include <wiringPi.h>
int main(void)
{
// Red LED: Physical pin 18, BCM GPIO24, and WiringPi pin 5.
constint led1 =5;
// Green LED: Physical pin 22, BCM GPIO25, and WiringPi pin 6.
constint led2 =6;
// Yellow LED: Physical pin 29, BCM GPIO5, and WiringPi pin 21.
constint led3 =21;
// Switch: Physical pin 31, BCM GPIO6, and WiringPi pin 22.
constint button =22;
wiringPiSetup();
pinMode(led1, OUTPUT);
pinMode(led2, OUTPUT);
pinMode(led3, OUTPUT);
pinMode(button, INPUT);
fprintf(stderr, 'Running on Raspberry Pi revision %dn', piBoardRev());
while(1){
for(int i =0; i <8; i++){
if(i &1){
digitalWrite(led1, HIGH);
}else{
digitalWrite(led1, LOW);
}
if(i &2){
digitalWrite(led2, HIGH);
}else{
digitalWrite(led2, LOW);
}
if(i &4){
digitalWrite(led3, HIGH);
}else{
digitalWrite(led3, LOW);
}
delay(100);
}
if(digitalRead(button) LOW){
digitalWrite(led1, LOW);
digitalWrite(led2, LOW);
digitalWrite(led3, LOW);
exit(1);
}
}
}
Example of programming GPIO from C or C++ using the WiringPi library
on a Raspberry Pi.
Will cycle through three LEDs in a binary pattern. If the pushbutton is
pressed, it will turn off all LEDs and exit.
Jeff Tranter <<a href='mailto:[email protected]'>[email protected]</a>>
*/
#include <stdio.h>
#include <stdlib.h>
#include <wiringPi.h>
int main(void)
{
// Red LED: Physical pin 18, BCM GPIO24, and WiringPi pin 5.
constint led1 =5;
// Green LED: Physical pin 22, BCM GPIO25, and WiringPi pin 6.
constint led2 =6;
// Yellow LED: Physical pin 29, BCM GPIO5, and WiringPi pin 21.
constint led3 =21;
// Switch: Physical pin 31, BCM GPIO6, and WiringPi pin 22.
constint button =22;
wiringPiSetup();
pinMode(led1, OUTPUT);
pinMode(led2, OUTPUT);
pinMode(led3, OUTPUT);
pinMode(button, INPUT);
fprintf(stderr, 'Running on Raspberry Pi revision %dn', piBoardRev());
while(1){
for(int i =0; i <8; i++){
if(i &1){
digitalWrite(led1, HIGH);
}else{
digitalWrite(led1, LOW);
}
if(i &2){
digitalWrite(led2, HIGH);
}else{
digitalWrite(led2, LOW);
}
if(i &4){
digitalWrite(led3, HIGH);
}else{
digitalWrite(led3, LOW);
}
delay(100);
}
if(digitalRead(button) LOW){
digitalWrite(led1, LOW);
digitalWrite(led2, LOW);
digitalWrite(led3, LOW);
exit(1);
}
}
}
I did a few performance measurements on the code in demo2.c. Running the loop with no delays produced a GPIO pin cycling at a rate of almost 12 MHz on a Raspberry Pi 3. The main reason for the high performance is that it uses direct register access to avoid the kernel/user space context switching and other overhead needed with sysfs.
Using delays of delayMicroseconds(10) produced a very consistent and accurate 50 kHz square wave.
Conclusions
Tranter Revolver History
These examples, implemented in C or C++, are more representative of how commercial and industrial embedded applications are usually written. The resource requirements are significantly lower and performance is higher than when using Python or other interpreted languages. The cost is the additional complexity in programming and building.
I highly recommend using WiringPi as it is high performance and provides a nice API for programming. It's main downside is being tied to the Raspberry Pi platform.
Shortly before I wrote this blog post, the new Raspberry Pi 4 was released. This model is approximately three times faster and has a number of features and performance improvements. Initial benchmarks indicate it is significantly faster at GPIO handling than the Pi 3. The new model needs new version of the WiringPi, which was available on release day of the Raspberry Pi 4, but not yet included in Raspbian Linux as a package.
William Tranter Revolver
References
.500 Tranter Cartridge
- WiringPi library web site: http://wiringpi.com
- Link to code examples: https://github.com/tranter/blogs/tree/master/gpio/part5