干活中用到了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 度,航向任意角度清零
/*
*******************************************************************************************
*
* 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
/*
******************************************************************************************
* 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
/*
*******************************************************************************************
*
* 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;
}
/*
*******************************************************************************************
*
* 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
#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
这样在项目中同时宏定义 _DEBUG 和 GY25_DEBUG 时才会打印调试信息。不需要的话删除相关语句就好。
想要了解相关技巧的话详见:
C语言宏配置的各种奇淫技巧
使用此模块基本分几步:
以下代码示例了在上电后发送校准指令,然后进入自动模式,之后不断读取数据并进行解析(假设使用标准输入输出上的串口和GY25通信):
#include "GY25Driver.h"
#include
#include
……
// 收到数据的回调函数
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