Finish most of the uart stuff.
I've decided to push this, even though I can't test it.
That worries me a little. Use it with caution.
Implement GPIO stuff, and a main executable to receive uart
data. Also make some modifications to Brian's stuff, to get it to
build correctly.
Finally, add some testing stuff. Feel free to disregard that.
I probably won't be using it until I get debian flashed onto
my BeagleBone Black.
diff --git a/bbb_cape/src/bbb/bbb.gyp b/bbb_cape/src/bbb/bbb.gyp
index 9040140..ec617cc 100644
--- a/bbb_cape/src/bbb/bbb.gyp
+++ b/bbb_cape/src/bbb/bbb.gyp
@@ -22,5 +22,41 @@
'uart_receiver.cc',
],
},
+ {
+ 'target_name': 'uart_receiver_test',
+ 'type': 'executable',
+ 'dependencies': [
+ 'uart_receiver',
+ '<(EXTERNALS):gtest',
+ '<(AOS)/build/aos.gyp:logging',
+ ],
+ 'sources': [
+ 'uart_receiver_test.cc',
+ ],
+ },
+ {
+ 'target_name': 'uart_receiver_main',
+ 'type': 'executable',
+ 'dependencies': [
+ 'uart_receiver',
+ 'gpios',
+ '<(AOS)/common/common.gyp:time',
+ '<(AOS)/build/aos.gyp:logging',
+ '<(AOS)/atom_code/atom_code.gyp:init',
+ ],
+ 'sources': [
+ 'uart_receiver_main.cc',
+ ],
+ },
+ {
+ 'target_name': 'gpios',
+ 'type': 'static_library',
+ 'dependencies': [
+ '<(AOS)/build/aos.gyp:logging',
+ ],
+ 'sources': [
+ 'gpios.cc',
+ ],
+ },
],
}
diff --git a/bbb_cape/src/bbb/gpios.cc b/bbb_cape/src/bbb/gpios.cc
new file mode 100644
index 0000000..e2ab654
--- /dev/null
+++ b/bbb_cape/src/bbb/gpios.cc
@@ -0,0 +1,146 @@
+#include <cstdio>
+#include <cstdlib>
+
+#include "aos/common/logging/logging_impl.h"
+#include "gpios.h"
+
+namespace bbb {
+
+ Pin::Pin(int bank, int pin) : handle_(NULL), direction_(0),
+ kernel_pin_(bank * 32 + pin), exported_(false) {}
+
+ Pin::~Pin() {
+ // Unexport the pin.
+ if ((handle_ = fopen("/sys/class/gpio/unexport", "ab")) == NULL) {
+ LOG(WARNING, "Could not unexport gpio pin.\n");
+ // There's nothing intelligent we can really do here.
+ return;
+ }
+
+ char gpio_num[2];
+ snprintf(gpio_num, sizeof(gpio_num), "%d", kernel_pin_);
+ fwrite(gpio_num, sizeof(char), 2, handle_);
+ fclose(handle_);
+ }
+
+ int Pin::DoExport() {
+ char gpio_num[2];
+ snprintf(gpio_num, sizeof(gpio_num), "%d", kernel_pin_);
+
+ // Export the pin.
+ if ((handle_ = fopen("/sys/class/gpio/export", "ab")) == NULL) {
+ LOG(ERROR, "Could not export GPIO pin.\n");
+ return -1;
+ }
+
+ fwrite(gpio_num, sizeof(char), 2, handle_);
+ fclose(handle_);
+
+ exported_ = true;
+ return 0;
+ }
+
+ int Pin::DoPinDirSet(int direction) {
+ char buf[4], type_path[64];
+ snprintf(type_path, sizeof(type_path), "/sys/class/gpio/gpio%d/direction", kernel_pin_);
+
+ if ((handle_ = fopen(type_path, "rb+")) == NULL) {
+ LOG(ERROR, "Unable to set pin direction.\n");
+ return -1;
+ }
+
+ direction_ = direction;
+ switch (direction) {
+ case 1:
+ strcpy(buf, "in");
+ break;
+ case 2:
+ strcpy(buf, "low");
+ break;
+ case 0:
+ return 0;
+ default:
+ LOG(ERROR, "Invalid direction identifier %d.\n", direction);
+ direction_ = 0;
+ return -1;
+ }
+ fwrite(buf, sizeof(char), 3, handle_);
+ fclose(handle_);
+
+ return 0;
+ }
+
+ int Pin::MakeInput() {
+ if (!exported_) {
+ if (DoExport()) {
+ return -1;
+ }
+ }
+
+ return DoPinDirSet(1);
+ }
+
+ int Pin::MakeOutput() {
+ if (!exported_) {
+ if (DoExport()) {
+ return -1;
+ }
+ }
+
+ return DoPinDirSet(2);
+ }
+
+ int Pin::Write(uint8_t value) {
+ if (direction_ != 2) {
+ LOG(ERROR, "Only pins set as output can be written to.\n");
+ return -1;
+ }
+
+ char buf, val_path [64];
+ snprintf(val_path, sizeof(val_path), "/sys/class/gpio/gpio%d/value", kernel_pin_);
+ if (value != 0) {
+ value = 1;
+ }
+
+ if ((handle_ = fopen(val_path, "rb+")) == NULL) {
+ LOG(ERROR, "Unable to set pin value.\n");
+ return -1;
+ }
+
+ snprintf(&buf, sizeof(buf), "%d", value);
+ fwrite(&buf, sizeof(char), 1, handle_);
+ fclose(handle_);
+
+ return 0;
+ }
+
+ int Pin::Read() {
+ // NOTE: I can't find any docs confirming that one can indeed
+ // poll a pin's value using this method, but it seems that it
+ // should work. Really, the "right" (interrupt driven) way to
+ // do this is to set an event, but that involves messing with
+ // dev tree crap, which I'm not willing to do unless we need
+ // this functionality.
+
+ if (direction_ != 1) {
+ LOG(ERROR, "Only pins set as input can be read from.\n");
+ return -1;
+ }
+
+ char buf, val_path [64];
+ snprintf(val_path, sizeof(val_path), "/sys/class/gpio/gpio%d/value", kernel_pin_);
+
+ if ((handle_ = fopen(val_path, "rb")) == NULL) {
+ LOG(ERROR, "Unable to read pin value.\n");
+ return -1;
+ }
+
+ if (fread(&buf, sizeof(char), 1, handle_) <= 0) {
+ LOG(ERROR, "Failed to read pin value from file.\n");
+ return -1;
+ }
+ fclose(handle_);
+ return atoi(&buf);
+ }
+
+} // bbb
diff --git a/bbb_cape/src/bbb/gpios.h b/bbb_cape/src/bbb/gpios.h
new file mode 100644
index 0000000..1494da2
--- /dev/null
+++ b/bbb_cape/src/bbb/gpios.h
@@ -0,0 +1,40 @@
+#ifndef BBB_CAPE_SRC_BBB_CAPE_CONTROL_H_
+#define BBB_CAPE_SRC_BBB_CAPE_CONTROL_H_
+
+#include <cstdint>
+#include <cstring>
+
+// As it turns out, controlling the BBB's GPIO pins
+// from C++ is kind of a pain. The purpose of this
+// code is to provide a simple wrapper that masks
+// all the ugly stuff and exposes a nice API.
+
+// Based on example from
+// <http://learnbuildshare.wordpress.com/2013/05/29/beaglebone-black-digital-ouput/>
+
+namespace bbb {
+
+class Pin {
+ FILE *handle_;
+ int direction_;
+ int kernel_pin_;
+ bool exported_;
+
+ int DoPinDirSet(int direction);
+ int DoExport();
+
+public:
+ // Here, for example, if you wanted to use
+ // GPIO1_28, you would give the ctor
+ // 1, 28 as arguments.
+ Pin(int bank, int pin);
+ ~Pin();
+ int MakeInput();
+ int MakeOutput();
+ int Write(uint8_t value);
+ int Read();
+};
+
+} // bbb
+
+#endif
diff --git a/bbb_cape/src/bbb/uart_receiver.cc b/bbb_cape/src/bbb/uart_receiver.cc
index 981e40b..b7e64c4 100644
--- a/bbb_cape/src/bbb/uart_receiver.cc
+++ b/bbb_cape/src/bbb/uart_receiver.cc
@@ -20,8 +20,9 @@
namespace bbb {
-UartReceiver::UartReceiver(uint32_t baud_rate, size_t packet_size) :
- baud_rate_(baud_rate), packet_size_(packet_size) {
+UartReceiver::UartReceiver(uint32_t baud_rate) :
+ baud_rate_(baud_rate) {
+ packet_size_ = DATA_STRUCT_SEND_SIZE;
//packet_size_ should be a multiple of four.
int toadd = packet_size_ % 4;
LOG(DEBUG, "Increasing packet size by %d bytes.\n", toadd);
@@ -81,7 +82,7 @@
// We know the minimum size for packets.
// No use in having it do excessive syscalls.
options.c_cc[VMIN] = packet_size_;
- options.c_cc[VTIME] = 0;
+ options.c_cc[VTIME] = 1;
if (tcsetattr(fd_, TCSANOW, &options) != 0)
LOG(ERROR, "Tcsetattr failed.\n");
return -1;
@@ -128,7 +129,7 @@
// Copy packet data to output.
int filled = 0;
- readi -= 3;
+ readi -= 3; // We read a little into the next packet.
for (uint32_t i = pstarti; i < readi - 3; ++i) {
ptemp[i] = buf_[i];
++filled;
@@ -139,6 +140,7 @@
buf_[puti++] = buf_[i];
}
buf_used_ = stuffed_size_ - readi;
+ readi = 0;
// Cows algorithm always outputs something 4-byte aligned.
if (filled % 4) {
@@ -163,6 +165,8 @@
// Make sure the checksum checks out.
uint32_t checksum = static_cast<uint32_t>(ptemp_unstuffed[packet_size_ - 4]);
+ // Checksum only gets done on the actual datastruct part of the packet,
+ // so we'll discard everything after it.
memcpy(packet, ptemp_unstuffed, sizeof(DataStruct));
if (cape::CalculateChecksum((uint8_t *)packet, sizeof(DataStruct)) != checksum) {
LOG(WARNING, "Rejecting packet due to checksum failure.\n");
@@ -172,5 +176,5 @@
return 0;
}
-} //bbb
+} // bbb
diff --git a/bbb_cape/src/bbb/uart_receiver.h b/bbb_cape/src/bbb/uart_receiver.h
index ab6f019..36bb170 100644
--- a/bbb_cape/src/bbb/uart_receiver.h
+++ b/bbb_cape/src/bbb/uart_receiver.h
@@ -10,14 +10,14 @@
namespace bbb {
class UartReceiver {
- speed_t baud_rate_;
+ uint32_t baud_rate_;
size_t packet_size_, stuffed_size_;
int fd_;
uint32_t buf_used_;
char *buf_;
public:
- UartReceiver(uint32_t baud_rate, size_t packet_size);
+ UartReceiver(uint32_t baud_rate);
~UartReceiver();
// Opens file descriptor, etc.
int SetUp();
diff --git a/bbb_cape/src/bbb/uart_receiver_main.cc b/bbb_cape/src/bbb/uart_receiver_main.cc
new file mode 100644
index 0000000..272d454
--- /dev/null
+++ b/bbb_cape/src/bbb/uart_receiver_main.cc
@@ -0,0 +1,48 @@
+#include <cstdint>
+
+#include "aos/atom_code/init.h"
+#include "aos/common/logging/logging_impl.h"
+#include "aos/common/time.h"
+#include "gpios.h"
+#include "uart_receiver.h"
+
+using ::aos::time::Time;
+
+int main() {
+ aos::Init();
+
+ // time since last good packet before we reset
+ // the board.
+ Time kPacketTimeout = Time::InSeconds(1);
+
+ bbb::UartReceiver receiver = bbb::UartReceiver(3000000);
+ receiver.SetUp();
+ bbb::Pin reset_pin = bbb::Pin(1, 6);
+ reset_pin.MakeOutput();
+
+ DataStruct packet;
+ Time last_packet_time = Time::Now();
+ while (true) {
+ if (!last_packet_time.IsWithin(Time::Now(),
+ kPacketTimeout.ToNSec())) {
+
+ LOG(ERROR, "No good packets for too long. Resetting cape.\n");
+ reset_pin.Write(1);
+ ::aos::time::SleepFor(Time::InSeconds(1));
+ reset_pin.Write(0);
+
+ last_packet_time = Time::Now();
+ }
+
+ if (receiver.GetPacket(&packet)) {
+ LOG(INFO, "Could not get a packet.\n");
+ continue;
+ }
+ last_packet_time = Time::Now();
+
+ //TODO (danielp): Do stuff here with the data we got.
+ }
+
+ aos::Cleanup();
+ return 0;
+}
diff --git a/bbb_cape/src/bbb/uart_receiver_test.cc b/bbb_cape/src/bbb/uart_receiver_test.cc
new file mode 100644
index 0000000..601dbf6
--- /dev/null
+++ b/bbb_cape/src/bbb/uart_receiver_test.cc
@@ -0,0 +1,23 @@
+#include <termios.h>
+
+#include "gtest/gtest.h"
+#include "uart_receiver.h"
+
+namespace bbb {
+namespace {
+
+class UartReceiverTest : public ::testing::Test {
+protected:
+ UartReceiver test_instance_;
+
+public:
+ UartReceiverTest () : test_instance_(UartReceiver(3000000)) {}
+};
+
+TEST_F(UartReceiverTest, SetUpTest) {
+ // Test its ability to open a file descriptor and set a baud rate.
+ ASSERT_EQ(test_instance_.SetUp(), 0);
+}
+
+} //namespace
+} //bbb
diff --git a/bbb_cape/src/cape/cows.c b/bbb_cape/src/cape/cows.c
index 22bd056..396889c 100644
--- a/bbb_cape/src/cape/cows.c
+++ b/bbb_cape/src/cape/cows.c
@@ -5,8 +5,8 @@
// This implementation is based on
// <http://www.jacquesf.com/2011/03/consistent-overhead-byte-stuffing/>.
-uint32_t cows_stuff(const void *restrict source_in, size_t source_length,
- void *restrict destination_in) {
+uint32_t cows_stuff(const void *__restrict__ source_in, size_t source_length,
+ void *__restrict__ destination_in) {
const uint32_t *restrict source = (const uint32_t *)source_in;
uint32_t *restrict destination = (uint32_t *)destination_in;
size_t source_index = 0;
@@ -34,8 +34,8 @@
return destination_index;
}
-uint32_t cows_unstuff(const uint32_t *restrict source, size_t source_length,
- uint32_t *restrict destination) {
+uint32_t cows_unstuff(const uint32_t *__restrict__ source, size_t source_length,
+ uint32_t *__restrict__ destination) {
size_t source_index = 0;
size_t destination_index = 0;
uint32_t code;
diff --git a/bbb_cape/src/cape/cows.h b/bbb_cape/src/cape/cows.h
index 27cb556..3a9e6ab 100644
--- a/bbb_cape/src/cape/cows.h
+++ b/bbb_cape/src/cape/cows.h
@@ -4,6 +4,10 @@
#include <sys/types.h>
#include <stdint.h>
+#ifdef __cplusplus
+extern "C" {
+#endif
+
// This file implements something very similar to Consistent Overhead Byte
// Stuffing <http://en.wikipedia.org/wiki/Consistent_Overhead_Byte_Stuffing>. It
// uses that algorithm except with 4-byte chunks instead of individual bytes
@@ -30,4 +34,8 @@
uint32_t cows_unstuff(const uint32_t *__restrict__ source, size_t source_length,
uint32_t *__restrict__ destination);
+#ifdef __cplusplus
+} // extern C
+#endif
+
#endif // CAPE_COWS_H_
diff --git a/frc971/atom_code/atom_code.gyp b/frc971/atom_code/atom_code.gyp
index a004c4d..4803265 100644
--- a/frc971/atom_code/atom_code.gyp
+++ b/frc971/atom_code/atom_code.gyp
@@ -25,7 +25,7 @@
#'camera/camera.gyp:frc971',
'../../gyro_board/src/libusb-driver/libusb-driver.gyp:get',
'../input/input.gyp:gyro_sensor_receiver',
- '<(DEPTH)/bbb_cape/src/bbb/bbb.gyp:uart_receiver',
+ '<(DEPTH)/bbb_cape/src/bbb/bbb.gyp:*',
],
'copies': [
{