TinyCHR6dm is a simplified interface for the CH Robotics CHR-6dm AHRS

Dependents:   AVC_20110423

Files at this revision

API Documentation at this revision

Comitter:
shimniok
Date:
Wed Apr 13 23:32:04 2011 +0000
Commit message:
Initial release

Changed in this revision

TinyCHR6dm.cpp Show annotated file Show diff for this revision Revisions of this file
TinyCHR6dm.h Show annotated file Show diff for this revision Revisions of this file
types.h Show annotated file Show diff for this revision Revisions of this file
diff -r 000000000000 -r 983f66650cd5 TinyCHR6dm.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/TinyCHR6dm.cpp	Wed Apr 13 23:32:04 2011 +0000
@@ -0,0 +1,191 @@
+#include <mbed.h>
+#include "TinyCHR6dm.h"
+#include "string.h"
+
+int chksum(uint8 pt, uint8 n, char data[])
+{
+    int sum = pt + n;
+    
+    for (int i=0; i < n; i++) {
+        sum += data[i];
+    }
+    
+    return sum;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// PUBLIC FUNCTIONS
+////////////////////////////////////////////////////////////////////////////////
+
+TinyCHR6dm::TinyCHR6dm(): _dataReady(false), _statusReady(false), _state(WAIT_S)
+{
+    _state = WAIT_S;
+}
+
+   
+void TinyCHR6dm::parse(char c)
+{
+
+    switch (_state) {
+        case WAIT_S :
+            if (debug) debug->printf("WAIT_S\n");
+            if (c == 's') _state = WAIT_N;
+            break;
+        case WAIT_N :
+            if (debug) debug->printf("WAIT_N\n");
+            _state = (c == 'n') ? WAIT_P : WAIT_S;
+            break;
+        case WAIT_P :
+            if (debug) debug->printf("WAIT_P\n");
+            _state = (c == 'p') ? RX_TYPE : WAIT_S;
+            break;
+        case RX_TYPE :
+            pt = c;
+            _state = RX_N;
+            if (debug) debug->printf("PT = %02x\n", pt);
+            break;
+        case RX_N :
+            n = (uint8) c;
+            d = 0;
+            _state = (n < MAX_BYTES) ? RX_PACKET : WAIT_S;
+            if (debug) debug->printf("N = %d\n", n);
+            break;
+        case RX_PACKET :
+            if (debug) debug->printf("RX_PACKET\n");
+            if (d >= n || d >= MAX_BYTES) {
+                _state = PROCESS_PACKET;
+            } else {
+                if (debug) debug->printf("data[%d] = %02x\n", d, c);
+                data[d++] = c;
+            }
+            break;
+        case PROCESS_PACKET :
+            if (debug) debug->printf("PROCESS_PACKET\n");
+            process_packet();
+            _state = WAIT_S;
+            break;
+        default :
+            _state = WAIT_S;
+            break;
+    }
+
+    return;
+}
+
+
+float TinyCHR6dm::readYaw(void) {
+    return TinyCHR6dm::_yaw;
+}
+
+bool TinyCHR6dm::dataReady(void) {
+    return TinyCHR6dm::_dataReady;
+}
+
+void TinyCHR6dm::resetReady(void) {
+    TinyCHR6dm::_dataReady = false;
+}
+
+
+
+void TinyCHR6dm::send_packet(Serial *serial, uint8 pt, uint8 n, char data[])
+{
+    uint checksum;    
+    char p[MAX_BYTES];
+
+    p[0] = 's';
+    p[1] = 'n';
+    p[2] = 'p';
+    p[3] = (char) pt;
+    p[4] = (char) n;
+    
+    /** Checksum
+     * Datasheet:
+     * After the CHR6-dm receives a full  acket, it checks to ensure that the checksum
+     * given in the last two bytes matches the sum of all preceding bytes in the packet. 
+     */
+    checksum = 's'+'n'+'p'+pt+n;
+    for (int i=0; i < n; i++) {
+        p[i+5] = data[i];
+        checksum += data[i];
+    }
+    if (debug) debug->printf("Checksum: %04x\n", checksum);
+    p[5+n]   = (checksum >> 8);       // checksum MSB
+    p[5+n+1] = (checksum & 0x0ff);   // checksum LSB
+
+    if (debug) debug->printf("Checksum: %02x %02x\n", p[5+n], p[5+n+1]);
+
+    if (serial) {
+        for (int i=0; i < 5+n+2; i++) {
+            serial->putc((int) p[i]);
+            if (debug) debug->printf("%02x\n", p[i]);
+        }
+    }
+    
+}
+
+int TinyCHR6dm::status(void) {
+    _statusReady = false;
+    return _status;
+}
+
+bool TinyCHR6dm::statusReady(void) {
+    return _statusReady;
+}
+
+char *TinyCHR6dm::statusString(int status) {
+    char *s[7] = { "unknown",
+                   "PT_COMMAND_COMPLETE",
+                   "PT_COMMAND_FAILED",
+                   "PT_BAD_CHECKSUM",
+                   "PT_BAD_DATA_LENGTH",
+                   "PT_UNRECOGNIZED_PACKET",
+                   "PT_BUFFER_OVERFLOW" };
+
+    return (status >= PT_COMMAND_COMPLETE && status <= PT_BUFFER_OVERFLOW ) ? s[status - PT_COMMAND_COMPLETE + 1] : s[0];
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// PRIVATE FUNCTIONS
+////////////////////////////////////////////////////////////////////////////////
+
+void TinyCHR6dm::process_packet(void) 
+{
+    
+    switch (pt) {
+        /** SENSOR_DATA 
+         * Datasheet:
+         * If all channels are active, then data is given in the following order: { yaw, pitch, roll, yaw_rate, 
+         * pitch_rate, roll_rate, mag_z, mag_y, mag_x, gyro_z, gyro_y, gyro_x, accel_z, accel_y, accel_x }.  
+         * Data bytes D3 and D4 correspond to the yaw angle, D5 and D6 to the pitch angle, etc.  Data is 
+         * returned as 16-bit two's complement integers. 
+         *
+         * When one or more channel is inactive, then the data is returned in the same order, but skipping the 
+         * inactive channels.  For example, if all magnetic field and rate gyro channels are disabled, then the 
+         * data is given in the following order: { yaw, pitch, roll, accel_z, accel_y, accel_x } 
+         */
+        case PT_SENSOR_DATA :
+            if (debug) debug->printf("SENSOR_DATA, YAW FLAG: %02x\n", data[0]&YAW_FLAG);
+            
+            if ((data[0] & YAW_FLAG) == YAW_FLAG) {
+                int yaw = (int) (data[2] << 8 | data[3]);
+                float yawf = 360.0 * (float) yaw / 32768.0;
+                while (yawf < 0)      yawf += 360.0;
+                while (yawf >= 360.0) yawf -= 360.0;
+                TinyCHR6dm::_yaw = yawf;
+                if (debug) debug->printf("Yaw: %.2f\n", yawf);
+            }
+            _dataReady = true;
+            break;
+        case PT_COMMAND_COMPLETE :
+        case PT_COMMAND_FAILED :
+        case PT_BAD_CHECKSUM :
+        case PT_BAD_DATA_LENGTH :
+            if (debug) debug->printf("PT = %02x\n", pt);
+            _status = pt;
+            _statusReady = true;
+            break;
+        default :
+            if (debug) debug->printf("Packet type %02x was not processed\n", pt);
+            break;
+    }
+}
diff -r 000000000000 -r 983f66650cd5 TinyCHR6dm.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/TinyCHR6dm.h	Wed Apr 13 23:32:04 2011 +0000
@@ -0,0 +1,233 @@
+#include <mbed.h>
+#include "types.h"
+
+/** Rx packet types (commands) */
+#define SET_ACTIVE_CHANNELS             0x80
+#define SET_SILENT_MODE                 0x81
+#define SET_BROADCAST_MODE              0x82
+#define SET_GYRO_BIAS                   0x83
+#define SET_ACCEL_BIAS                  0x84
+#define SET_ACCEL_REF_VECTOR            0x85
+#define AUTO_SET_ACCEL_REF              0x86
+#define ZERO_RATE_GYROS                 0x87
+#define SELF_TEST                       0x88
+#define SET_START_CAL                   0x89
+#define SET_PROCESS_COVARIANCE          0x8A
+#define SET_MAG_COVARIANCE              0x8B
+#define SET_ACCEL_COVARIANCE            0x8C
+#define SET_EKF_CONFIG                  0x8D
+#define SET_GYRO_ALIGNMENT              0x8E
+#define SET_ACCEL_ALIGNMENT             0x8F
+#define SET_MAG_REF_VECTOR              0x90
+#define AUTO_SET_MAG_REF                0x91
+#define SET_MAG_CAL                     0x92
+#define SET_MAG_BIAS                    0x93
+#define SET_GYRO_SCALE                  0x94
+#define EKF_RESET                       0x95
+#define RESET_TO_FACTORY                0x96
+#define WRITE_TO_FLASH                  0xA0
+#define GET_DATA                        0x01
+#define GET_ACTIVE_CHANNELS             0x02
+#define GET_BROADCAST_MODE              0x03
+#define GET_ACCEL_BIAS                  0x04
+#define GET_ACCEL_REF_VECTOR            0x05
+#define GET_GYRO_BIAS                   0x06
+#define GET_GYRO_SCALE                  0x07
+#define GET_START_CAL                   0x08
+#define GET_EKF_CONFIG                  0x09
+#define GET_ACCEL_COVARIANCE            0x0A
+#define GET_MAG_COVARIANCE              0x0B
+#define GET_PROCESS_COVARIANCE          0x0C
+#define GET_STATE_COVARIANCE            0x0D
+#define GET_GYRO_ALIGNMENT              0x0E
+#define GET_ACCEL_ALIGNMENT             0x0F
+#define GET_MAG_REF_VECTOR              0x10
+#define GET_MAG_CAL                     0x11
+#define GET_MAG_BIAS                    0x12
+
+/** Tx packet types (responses) */
+#define PT_COMMAND_COMPLETE             0xB0
+#define PT_COMMAND_FAILED               0xB1
+#define PT_BAD_CHECKSUM                 0xB2
+#define PT_BAD_DATA_LENGTH              0xB3
+#define PT_UNRECOGNIZED_PACKET          0xB4  
+#define PT_BUFFER_OVERFLOW              0xB5
+#define PT_STATUS_REPORT                0xB6
+#define PT_SENSOR_DATA                  0xB7
+#define PT_GYRO_BIAS_REPORT             0xB8
+#define PT_GYRO_SCALE_REPORT            0xB9
+#define PT_START_CAL_REPORT             0xBA
+#define PT_ACCEL_BIAS_REPORT            0xBB
+#define PT_ACCEL_REF_VECTOR_REPORT      0xBC
+#define PT_ACTIVE_CHANNEL_REPORT        0xBD
+#define PT_ACCEL_COVARIANCE_REPORT      0xBE
+#define PT_MAG_COVARIANCE_REPORT        0xBF
+#define PT_PROCESS_COVARIANCE_REPORT    0xC0
+#define PT_STATE_COVARIANCE_REPORT      0xC1
+#define PT_EKF_CONFIG_REPORT            0xC2
+#define PT_GYRO_ALIGNMENT_REPORT        0xC3
+#define PT_ACCEL_ALIGNMENT_REPORT       0xC4
+#define PT_MAG_REF_VECTOR_REPORT        0xC5
+#define PT_MAG_CAL_REPORT               0xC6
+#define PT_MAG_BIAS_REPORT              0xC7
+#define PT_BROADCAST_MODE_REPORT        0xC8
+
+/** EKF sensor enable flags */
+#define Mag_EN                          0x01
+#define Accel_EN                        0x02
+
+/** flags for SENSOR_DATA, ACTIVE_CHANNEL_REPORT */
+/** D1 */
+#define YAW_FLAG                0x80
+#define PITCH_FLAG              0x40
+#define ROLL_FLAG               0x20
+#define YAW_RATE_FLAG           0x10
+#define PITCH_RATE_FLAG         0x08
+#define ROLL_RATE_FLAG          0x04
+#define MX_FLAG                 0x02
+#define MY_FLAG                 0x01
+/** D2 */
+#define MZ_FLAG                 0x80
+#define GX_FLAG                 0x40
+#define GY_FLAG                 0x20
+#define GZ_FLAG                 0x10
+#define AX_FLAG                 0x08
+#define AY_FLAG                 0x04
+#define AZ_FLAG                 0x02
+#define ZERO_FLAG               0x01
+
+#define MAX_BYTES 64
+
+/** TinyCHR6dm is a simplified interface for the CH Robotics CHR-6dm AHRS
+ *  http://www.chrobotics.com/index.php?main_page=product_info&cPath=1&products_id=2
+ *
+ * Written by Michael Shimniok http://www.bot-thoughts.com/
+ *
+ * In the style of TinyGPS, this library parses a subset of the CHR-6dm packets to provide
+ * a minimal set of data and status messages to the caller.  Also provides utility to send
+ * commands to the AHRS.  This is an early release and a work in progress, so the interface
+ * may change significantly over the next couple of versions.  Also, the current version
+ * only supports reading yaw data.  More may be supported in the future.
+ * @code
+ * #include "mbed.h"
+ * #include "TinyCHR6dm.h"
+ *
+ * Serial pc(USBTX, USBRX);
+ * Serial ahrs(p26, p25);
+ * TinyCHR6dm ahrsParser;
+ * DigitalOut myled(LED1);
+ * Timer t;
+ *
+ * void recv() {
+ *   while (ahrs.readable())
+ *      ahrsParser.parse(ahrs.getc());
+ * }
+ *
+ * int main() {
+ *   char data[128];
+ *   int status;
+ *           
+ *   t.reset();
+ *   t.start();
+ *   pc.baud(115200);
+ *   ahrs.baud(115200);
+ *   ahrs.attach(recv, Serial::RxIrq);
+ *
+ *
+ *   wait(0.5);
+ *
+ *   // Configure AHRS
+ *   data[0] = Accel_EN;
+ *   ahrsParser.send_packet(&ahrs, SET_EKF_CONFIG, 1, data);
+ *   wait(0.5);
+ *   status = ahrsParser.status();
+ *   pc.printf("Status: %02x %s\n", status, ahrsParser.statusString(status));
+ *   
+ *   ahrsParser.send_packet(&ahrs, ZERO_RATE_GYROS, 0, 0);
+ *   wait(0.5);
+ *   status = ahrsParser.status();
+ *   pc.printf("Status: %02x %s\n", status, ahrsParser.statusString(status));
+ *
+ *   ahrsParser.send_packet(&ahrs, EKF_RESET, 0, 0);
+ *   wait(0.5);
+ *   status = ahrsParser.status();
+ *   pc.printf("Status: %02x %s\n", status, ahrsParser.statusString(status));
+ *
+ *   while(1) {
+ *     int millis = t.read_ms();
+ *     if ((millis % 500) == 0) {
+ *       while (!ahrsParser.dataReady());
+ *       pc.printf("yaw: %.2f\n", ahrsParser.readYaw());
+ *     }
+ *   }
+ * }
+ * @endcode
+ */
+class TinyCHR6dm {
+    public :
+        Serial *debug;
+    
+        /** Creates an protocol parser for CHR-6dm device
+         */
+        TinyCHR6dm();
+
+        /** Parse a character of data from CHR-6dm
+         * @param c is the character to be parsed
+         */
+        void parse(char c);
+        
+        /** Get the current yaw (course)
+         * @returns the current inertia-frame yaw aka course
+         */
+        float readYaw(void);
+        
+        /** Determine if yaw data is ready
+         *
+         * @param return true right after parsing SENSOR_DATA from AHRS
+         */
+        bool dataReady(void);
+        
+        /** Reset the data ready flag
+         */        
+        void resetReady(void);
+
+        /** Create a packet using specified PT, N, and data and adding
+         *  'snp' header and 2-byte checksum
+         */
+        void send_packet(Serial *serial, uint8 pt, uint8 n, char data[]);
+        
+        /** Return latest status packet type sent by the AHRS, resets statusReady() to false
+         *
+         * @param returns the status as an integer corresponding to the TX packet type
+         */
+        int status(void);
+       
+        /** Indicates whether a status message is ready to be read with status()
+         *
+         * @param returns true if a status message has been received since the last call to status()
+         */
+        bool statusReady(void);
+       
+        /** Converts status to readable text string
+         *
+         * @param returns pointer to static string containing status message text
+         */
+        char *statusString(int status);
+       
+    private :
+
+        enum _states { WAIT_S, WAIT_N, WAIT_P, RX_TYPE, RX_N, RX_PACKET, PROCESS_PACKET };
+        
+        /** Process the packet once the data bytes are read
+         */
+        void process_packet(void);
+        int _status;
+        float _yaw;
+        bool _dataReady;
+        bool _statusReady;
+        char data[MAX_BYTES];
+        uint8 pt;
+        uint n;
+        int d;
+        int _state;
+};
\ No newline at end of file
diff -r 000000000000 -r 983f66650cd5 types.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/types.h	Wed Apr 13 23:32:04 2011 +0000
@@ -0,0 +1,7 @@
+#ifndef __TinyCHR6dm_TYPES_H
+#define __TinyCHR6dm_TYPES_H
+
+typedef unsigned char uint8;
+typedef unsigned int uint;
+
+#endif
\ No newline at end of file