[嵌入式開發模塊]GY25傾斜角度傳感器 驅動模塊

前言

幹活中用到了GY25傳感器模塊,寫了相應的驅動程序,放出來。

這個模塊本身的協議比較簡單,所以模塊的驅動也很小巧。

驅動被設計爲擁有指令器和接收機兩個部分,完全被動方式運行,與具體的平臺解耦,只支持串口通訊。

GY25簡介

概述

GY-25 是一款低成本傾斜度模塊。工作電壓3-5v 功耗小,體積小。其工作原理,是通過陀螺儀與加速度傳感器經過數據融合算法最後得到直接的角度數據。此傾斜度模塊以串口TTL 電平全雙工方式與上位機進行通信。該產品精度高,穩定性高。能夠在任意位置得到準確的角度,輸出的波特率有9600bps 與115200bps有連續輸出與詢問輸出兩種方式,可適應不同的工作環境。與所有的單片機及電腦連接。

通信協議

(1)串口通信參數(默認波特率值115200 bps,)

波特率:9600 bps 校驗位:N 數據位:8 停止位:1
波特率:115200 bps 校驗位:N 數據位:8 停止位:1
注意:波特率選擇可通過PCB 上焊接點選擇。

(2)模塊輸出格式,每幀包含8 個字節(十六進制):

①.Byte0: 0xAA 幀頭標誌
②.Byte1: 0x00~0xFF 航向角高8 位
③.Byte2: 0x00~0xFF 航向角低8 位
④.Byte3: 0x00~0xFF 俯仰角高8 位
⑤.Byte4: 0x00~0xFF 俯仰角低8 位
⑥.Byte5: 0x00~0xFF 橫滾角高8 位
⑦.Byte6: 0x00~0xFF 橫滾角低8 位
⑧.Byte7: 0x55 幀結束標誌

計算方法: 角度= 高8 位<<8|低8 位(結果爲實際角度乘以100)
例:一幀數據 <0xAA-0x00-0x64-0x03-0XE8-0x27-0x10-0x55>
表示:航向角=1.00 度
俯仰角=10.00 度
橫滾角=100.00 度

(3)命令字節,由外部控制器發送至模塊(十六進制)

①.0xA5+0x51: 查詢模式,直接返回角度值,需每次讀取都發送
②.0xA5+0x52: 自動模式,直接返回角度值,只需要初始化時發一次
③.0xA5+0x53: 自動模式,ASCII 碼輸出,便於直接電腦串口助手查看
④.0xA5+0x54: 校正模式,校正俯仰橫滾角0 度,需要保持水平時候發送
⑤.0xA5+0x55: 校正模式,校正航向0 度,航向任意角度清零

驅動文件

GY25Driver.h

/*
*******************************************************************************************
*
*                                    GY25 DRIVER MODULE
*                                       GY25驅動模塊
*
* File : GY25Driver.h
* By   : Lin Shijun(https://blog.csdn.net/lin_strong)
* Date:  2020/04/29
* version: V1.0
* History: 2020/04/29 V1.0 the prototype
* Note   : The GY25 driver is divided into two part, i.e. cmder and recver. The cmder is for
*          sending the GY25 command. The recver is for resolving data from the GY25.
********************************************************************************************
*/

#ifndef   GY25DRIVER_H
#define   GY25DRIVER_H

/*
******************************************************************************************
*                                          INCLUDE
******************************************************************************************
*/

#include <stdint.h>

/*
******************************************************************************************
*                                    DEBUG CONFIGURATION
******************************************************************************************
*/

// to enable debug messages in this module
// #define GY25_DEBUG  

/*
******************************************************************************************
*                                         CONSTANT
******************************************************************************************
*/

// Frame Header byte of data frame
#define GY25DF_HEADER  0xAA
// Frame Ender byte of data frame
#define GY25DF_ENDER   0x55
// length (in byte) of data frame
#define GY25DF_LENGTH  8

// Frame Header byte of command frame
#define GY25CF_HEADER          0xA5
// Query mode
#define GY25CMD_QUERY          0x51
// Automatic mode
#define GY25CMD_AUTO           0x52
// Automatic mode(ASCII)
#define GY25CMD_AUTO_ASCII     0x53
// Calibrate pitch and roll angle
#define GY25CMD_CALI_PITCHROLL 0x54
// Calibrate course angle
#define GY25CMD_CALI_COURSE    0x55

/*
******************************************************************************************
*                                        TYPE DEFINE
******************************************************************************************
*/

typedef struct GY25STRUCT_DATA{
  int16_t angleCourse;  // = 'real angle' * 100
  int16_t anglePitch ;  // = 'real angle' * 100
  int16_t angleRoll  ;  // = 'real angle' * 100
} GY25Data;

typedef void (* GY25FUNC_DATA)(GY25Data data);

/*
******************************************************************************************
*                                        INTERFACES
******************************************************************************************
*/

void GY25Cmder_Init(void (* outChannel)(uint8_t b));
void GY25Cmder_Query(void);
void GY25Cmder_EnterModeAuto(void);
void GY25Cmder_EnterModeAuto_ASCII(void);
void GY25Cmder_CalibratePitchRoll(void);
void GY25Cmder_CalibrateCourse(void);
void GY25Cmder_Destroy(void);


void GY25Recver_Init(GY25FUNC_DATA onRecvData);
// Call it to modify the call-back function.
void GY25Recver_RegisterCallback(GY25FUNC_DATA onRecvData);
void GY25Recver_CleanupBuffer(void);
// Feed the receiver every byte received so that receiver can notify user
// the resolved data for each frame.
void GY25Recver_Feed(uint8_t nextByte);
void GY25Recver_Destroy(void);

#endif  // of GY25DRIVER_H

GY25Recver.c

/*
*******************************************************************************************
*
*                                    GY25 RECEIVER MODULE
*                                    GY25驅動模塊 - 接收機
*
* File : GY25Recver.c
* By   : Lin Shijun(https://blog.csdn.net/lin_strong)
* Date:  2020/04/29
* version: V1.0
* History: 2020/04/29 V1.0 the prototype
* Note   : only for hex format
********************************************************************************************
*/

/*
*********************************************************************************************
*                                       INCLUDES
*********************************************************************************************
*/

#include "GY25Driver.h"
#include "BufferExternalArray.h"

#ifndef GY25_DEBUG
#undef _DEBUG
#endif

#include "DebugMsg.h"
/*
*********************************************************************************************
*                                       LOCAL FUNCTION
*********************************************************************************************
*/

#define ARRAYSIZE(arr)  (sizeof(arr)/ sizeof(arr[0]))
#define _bufGet(index) BufferUINT8Indexed_get((BufferUINT8Indexed)_buf, index)

/*
*********************************************************************************************
*                                       LOCAL VARIABLE
*********************************************************************************************
*/

static const char * _str_GY25Recver = "GY25Recver";

static GY25FUNC_DATA _onRecvData;

static uint8_t _frameBuf[GY25DF_LENGTH];
static BufferUINT8Array _buf;

/*
*********************************************************************************************
*                                 INTERFACE IMPLEMENTATION
*********************************************************************************************
*/

void GY25Recver_Init(GY25FUNC_DATA onRecvData){
  _onRecvData = onRecvData;
  _buf = BufferUINT8ExternalArray_Create(_frameBuf, GY25DF_LENGTH);
  if(_buf == NULL)
    for(;;)
      ;  // fatal error
}

void GY25Recver_RegisterCallback(GY25FUNC_DATA onRecvData){
  _onRecvData = onRecvData;
}

void GY25Recver_CleanupBuffer(void){
  Buffer_Cleanup((Buffer)_buf);
}

void GY25Recver_Feed(uint8_t nextByte){
  GY25Data data;
  if(Buffer_isFull(_buf))
    BufferUINT8_FrontOut((BufferUINT8)_buf);
  BufferUINT8_BackIn((BufferUINT8)_buf, nextByte);
  // if we haven't received GY25DF_LENGTH bytes.
  if(!Buffer_isFull(_buf) || 
  // or it's not a frame.
    _bufGet(0) != GY25DF_HEADER || _bufGet(GY25DF_LENGTH - 1) != GY25DF_ENDER){
    _dbg_printf1("%s: got corrupt frame.\r\n", _str_GY25Recver);
    return;  // then bye.
  }
  // if found a data frame.
  data.angleCourse = (_bufGet(1) << 8) | _bufGet(2);
  data.anglePitch  = (_bufGet(3) << 8) | _bufGet(4);
  data.angleRoll   = (_bufGet(5) << 8) | _bufGet(6);
  _dbg_printf4("%s: got data-Course(%i),Pitch(%i),Roll(%i)\r\n", _str_GY25Recver, data.angleCourse, 
    data.anglePitch, data.angleRoll);
  if(_onRecvData)
    _onRecvData(data);
  Buffer_Cleanup((Buffer)_buf);
}

void GY25Recver_Destroy(){
  _onRecvData = NULL;
  Buffer_Destroy(_buf);
  _buf = NULL;
}

GY25Cmder.c

/*
*******************************************************************************************
*
*                                    GY25 COMMANDER MODULE
*                                    GY25驅動模塊 - 指令器
*
* File : GY25Cmder.c
* By   : Lin Shijun(https://blog.csdn.net/lin_strong)
* Date:  2020/04/29
* version: V1.0
* History: 2020/04/29 V1.0 the prototype
* Note   : 
********************************************************************************************
*/

/*
*********************************************************************************************
*                                       INCLUDES
*********************************************************************************************
*/

#include "GY25Driver.h"

/*
*********************************************************************************************
*                                  LOCAL FUNCTION DECLARATION
*********************************************************************************************
*/

static void _sendCmd(uint8_t cmdByte);

/*
*********************************************************************************************
*                                       LOCAL VARIABLE
*********************************************************************************************
*/
static void (* _out)(uint8_t b) = NULL;

/*
*********************************************************************************************
*                                 INTERFACE IMPLEMENTATION
*********************************************************************************************
*/

void GY25Cmder_Init(void (* outChannel)(uint8_t b)){
  _out = outChannel;
}

void GY25Cmder_Destroy(){
  _out = NULL;
}


void GY25Cmder_Query(void){
  _sendCmd(GY25CMD_QUERY);
}

void GY25Cmder_EnterModeAuto(void){
  _sendCmd(GY25CMD_AUTO);
}

void GY25Cmder_EnterModeAuto_ASCII(void){
  _sendCmd(GY25CMD_AUTO_ASCII);
}

void GY25Cmder_CalibratePitchRoll(void){
  _sendCmd(GY25CMD_CALI_PITCHROLL);
}

void GY25Cmder_CalibrateCourse(void){
  _sendCmd(GY25CMD_CALI_COURSE);
}
/*
*********************************************************************************************
*                                 LOCAL FUNCTION IMPLEMENTATION
*********************************************************************************************
*/
static void _sendCmd(uint8_t cmdByte){
  if(_out == NULL)
    return;
  _out(GY25CF_HEADER);
  _out(cmdByte);
}

依賴

接收器的實現依賴於我實現的環形緩衝區:
環形緩衝區/循環隊列C語言面向對象實現(二)Buffer類源碼

至於DebugMsg.h則是我自己使用的調試信息的模塊:

#ifndef _DEBUG_MSG_H
#define _DEBUG_MSG_H
#include <stdio.h>
#ifdef _DEBUG
  #define _dbg_printf0(format)                   ((void)printf(format))
  #define _dbg_printf1(format,p1)                ((void)printf(format,p1))
  #define _dbg_printf2(format,p1,p2)             ((void)printf(format,p1,p2))
  #define _dbg_printf3(format,p1,p2,p3)          ((void)printf(format,p1,p2,p3))
  #define _dbg_printf4(format,p1,p2,p3,p4)       ((void)printf(format,p1,p2,p3,p4))
  #define _dbg_printf5(format,p1,p2,p3,p4,p5)    ((void)printf(format,p1,p2,p3,p4,p5))
  #define _dbg_printf6(format,p1,p2,p3,p4,p5,p6) ((void)printf(format,p1,p2,p3,p4,p5,p6))
  #define _dbg_printf7(format,p1,p2,p3,p4,p5,p6,p7) \
                ((void)printf(format,p1,p2,p3,p4,p5,p6,p7))
  #define _dbg_printf8(format,p1,p2,p3,p4,p5,p6,p7,p8) \
                ((void)printf(format,p1,p2,p3,p4,p5,p6,p7,p8))
  #define _dbg_printf9(format,p1,p2,p3,p4,p5,p6,p7,p8,p9) \
                ((void)printf(format,p1,p2,p3,p4,p5,p6,p7,p8,p9))
#else
  #define _dbg_printf0(format)
  #define _dbg_printf1(format,p1)
  #define _dbg_printf2(format,p1,p2)
  #define _dbg_printf3(format,p1,p2,p3)
  #define _dbg_printf4(format,p1,p2,p3,p4)
  #define _dbg_printf5(format,p1,p2,p3,p4,p5)
  #define _dbg_printf6(format,p1,p2,p3,p4,p5,p6)
  #define _dbg_printf7(format,p1,p2,p3,p4,p5,p6,p7)
  #define _dbg_printf8(format,p1,p2,p3,p4,p5,p6,p7,p8)
  #define _dbg_printf9(format,p1,p2,p3,p4,p5,p6,p7,p8,p9)
#endif

int dummyprintf(const char *format, ...);

#endif

這樣在項目中同時宏定義 _DEBUGGY25_DEBUG 時纔會打印調試信息。不需要的話刪除相關語句就好。

想要了解相關技巧的話詳見:
C語言宏配置的各種奇淫技巧

使用示例

使用此模塊基本分幾步:

  1. 初始化模塊
  2. 如需要發送指令則實現並註冊信道給指令器;註冊回調函數給接收器,這樣接收器在發現完整的數據幀時就會通過回調函數進行通知。
  3. 不斷接收數據,並喂(Feed)給接收器;如需要發指令,隨時調用指令器的接口。

以下代碼示例了在上電後發送校準指令,然後進入自動模式,之後不斷讀取數據並進行解析(假設使用標準輸入輸出上的串口和GY25通信):


#include "GY25Driver.h"
#include <stdio.h>
#include <stdint.h>
……
// 收到數據的回調函數
static void onDataResolved(GY25Data data);
// 發送信道
static void sendChannel(uint8_t b);

void main(){
  uint8_t b;
  ……
  GY25Recver_Init(onDataResolved);
  GY25Cmder_Init(sendChannel);
  
  // 延遲一小段時間
  (void)MyOS_DlyHMSM(0, 0, 0, 100);
  // 校準角度
  GY25Cmder_CalibratePitchRoll();
  // 延遲一小段時間
  (void)MyOS_DlyHMSM(0, 0, 0, 200);
  // 進入自動模式
  GY25Cmder_EnterModeAuto();
  for(;;){
    // 不斷得到下一個字節並餵給接收機
    // 接收機會通過回調函數實時傳遞解析出來的值
    b = (uint8_t)getchar();
    GY25Recver_Feed(b);
  }
}

static void onDataResolved(GY25Data data){
  // data中就是實時解析到的數據,具體怎麼使用是你的事
  // 注意裏頭的整型值是 真實角度 * 100 後的值
}

static void sendChannel(uint8_t b){
  // 往標準輸出輸出這個字節
  putchar((char)b);
}

注意:GY25可以選擇用ASCII格式輸出,但此模塊不支持也並不打算支持解析ASCII格式的數據幀。

更新歷史

2020/06/06 放出V1.0

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章