一、 概述 GY-26是一款低成本平面数字罗盘模块。输入电压低,功耗小,体积小。其工作原理是通过磁传感器中两个相互垂直轴同时感应地球磁场的磁分量,从而得出方位角度,此罗盘以RS232协议,及IIC协议与其他设备通信。该产品精度高,稳定性高。并切具有重新标定的功能,能够在任意位置得到准确的方位角,其输出的波特率是9600bps,有连续输出与询问输出两种方式,具有磁偏角补偿功能,可适应不同的工作 环境。 二、 产品特点 (1)、体积小 四、技术参数 (2)、高性价比 名称 |
参数
| 测量范围 |
0°~ 360°
| 分辨率 |
0.1 °
| 测量精度 |
1 °
| 重复精度 |
1 °
| 响应频率 |
25 HZ
| 工作电压 |
3~5 V
| 工作电流 |
5 V--15mA 3V--8 mA
| 工作温度 |
-20°~ 85°
| 储存温度 |
-40°~ 125°
| 尺寸 |
25.6mm×25.6mm
|
(3)、串口及IIC输出格式 三、产品应用 (1)、手持式仪器仪表 (2)、机器人导航、定位 (3)、航行系统 (4)、船用自动舵 (5)、八木天线定位 (6)、车载GPS导线 (7)、航模定向
四、串口通信协议
(1)、串口通信参数 波特率:9600 bps 校验位:N 数据位:8 停止位:1 (2)、模块输出格式,每帧包含8个字节: ①.Byte0: 0x0D (ASCII码 回车) ②.Byte1: 0x0A (ASCII码换行) ③.Byte2: 0x30~0x33角度百位 (ASCII 0~3) ④.Byte3: 0x30~0x39 角度十位 (ASCII 0~9) ⑤.Byte4: 0x30~0x39 角度个位 (ASCII 0~9) ⑥.Byte5: 0x2E (ASCII码小数点) ⑦.Byte6: 0x30~0x39角度小数位(ASCII0~9) ⑧.Byte7: 0x00~0xFF校验和 (仅低8bit) 注:校验和 Byte7 =(Byte0+Byte1+…….Byte6)结果仅取低8bit 例:一帧数据<0x0D-0x0A-0x33-0x35-0x39-0x2E-0x36-0x1C> = 359.6° (3)、命令字节,由外部控制器发送至模块(十六进制) ①. 0x31:进行一次角度测量--------返回值参考输出格式 ②.0xC0:校准磁场开始---------返回值参考输出格式(000.0度) ③.0xC1 :校准磁场结束--------------返回值参考硬铁补偿‘ ②’ ④.0xA0-0XAA-0XA5-0XC5 : 恢复出厂设定----------------------返回值参考输出格式(000.0度) ⑤. 0xA0-0XAA-0XA5-IIC_ADDR : 模块的IIC地址修改-------------返回值参考输出格式(000.0度) IIC地址范围参考“IIC通信协议(3)” ⑥. 0x03 + 磁偏高8位 : 磁偏角设定,正北方向设定--返回值参考输出格式(000.0度) ⑦. 0x04 + 磁偏低8位 : 磁偏角设定,正北方向设定--返回值参考输出格式(000.0度)
例:发送0XC0至模块时,模块应答以下1帧 <0x0D-0x0A-0x30-0x30-0x30-0x2E-0x30-0x05>表示成功 Led灯亮起,进入校准
例:发送0x03,0x00,0x04,0x64至模块时,模块应答以下1帧(4次) <0x0D-0x0A-0x30-0x30-0x30-0x2E-0x30-0x05>表示成功 磁偏角被修改为10.0度
五、IIC通信协议 (1)、进行IIC通信时,模块相当于24C04,EEPROM存储器,通信非常简单。 模块内部对应地址的数据(相当EEPROM各地址的数据),映射各参数 请参考下表 模块内部地址 |
地址的数据含义
| 0x00 |
未用到
| 0x01 |
角度值高8位
| 0x02 |
角度值低8位
| 0x03 |
磁偏角高8位
| 0x04 |
磁偏角低8位
| 0x05 |
未用到
| 0x06 |
未用到
| 0x07 |
校准等级值
|
实际当前角度值为上表“角度值高8位”与“角度值低8位”合成的16位数据,数据范围0-3599(因为分辨率为0.1°) 实际当前磁偏角值为“磁偏角高8位”与“磁偏角低8位”合成的16位数据,数据范围0-3599(因为分辨率为0.1°)
(2)、模块的命令表, 经IIC总线写入模块中
命令值 |
作用
| 0x00+ 0x31 |
角度测量
| 0x00+ 0xC0 |
校准
| 0x00+ 0xC1 |
停止校准
| 0x00+(0xA0+0XAA+0XA5+0XC5) |
恢复出厂设置
| 0x00+(0xA0+0XAA+0XA5+IIC_ADDR) |
IIC地址修改
| 0x03 + 磁偏高8位值 |
磁偏角修改
| 0x04 + 磁偏低8位值 |
磁偏角修改
|
磁偏角的值,范围0-3599(因为分辨率为0.1°),是由2个8位的数据组成,当修改模块磁偏角时,分为高8位值,低8位值,写入模块。 (3)、模块的IIC地址可修改,出厂默认是0xe0。模块上电时led对应IIC地址闪动,闪动的次数由模块上电led亮起,led灭掉开始记。若模块上电立即向模块发送命令,则led不闪动。向模块写入地址时,模块只接收以下16个数据地址,其他无效
模块地址 |
Led闪动次数
| 0xe0 |
0
| 0xe2 |
1
| 0xe4 |
2
| 0xe6 |
3
| 0xe8 |
4
| 0xea |
5
| 0xec |
6
| 0xee |
7
| 0xf0 |
8
| 0xf2 |
9
| 0xf4 |
10
| 0xf6 |
11
| 0xf8 |
12
| 0xfa |
13
| 0xfc |
14
| 0xfe |
15
|
六、术语说明 (1)、磁偏角
磁北线与真北线之间的夹角(如图),地球表面任一点的磁子午圈同地理子午圈的夹角。不同的地点磁偏角不同,同一地点不同的时间磁偏角也不同。模块的磁偏角=原始角度+设定磁偏角度,即顺时针加上。例如:磁偏角为0的100度,此时更改磁偏角为10度,那么模块输出为100+10=110度。 file:///C:/Users/FSY/AppData/Local/Temp/msohtml1/02/clip_image004.jpg
(2)、硬铁补偿,磁场校准 当电子指南针使用的环境发生改变时,由于受到周围磁场的影响,输出的角度也将会受到干扰,为了消除干扰,需要进行环境磁场校准。本模块的校准方法有三种种 如下:
①. 在模块的第9(CAL)引脚,接一按键至电源负极(GND),当第一次按下按键时,进入校准状态,LED常亮起。保持模块水平,缓慢旋转1周(旋转1周时间大约1分钟)。再次按下按键LED灭,校准结束。 ②. 控制器串口发送数据0XC0至模块后LED常亮起,模块应答成功数据。保持模块水平,缓慢旋转1周(旋转1周时间大约1分钟)。发送数据0XC1至模块,LED灭,校准结束。此时模块模块应答本次校准的等级(0级~9级,级数越大说明校准越好),应答一帧格式为: Byte0: 0x0D (ASCII码回车) Byte1: 0x0A (ASCII码换行) Byte2: 0x30 (ASCII码0 ) Byte3: 0x30 (ASCII码0 ) Byte4: 0x30 (ASCII码0 ) Byte5: 0x2E (ASCII码小数点) Byte6: 0x30~0x39 校准等级 (ASCII 0~9) Byte7: 0x00~0xFF校验和 (仅低8bit) 注:校验和 Byte7 =(Byte0+Byte1+…….Byte6)结果仅取低8bit 例:发送0XC1至模块时,模块应答以下1帧 <0x0D-0x0A-0x30-0x30-0x30-0x2E-0x39-0x0E>表示成功校准等级9
③. 控制器经IIC总线发送数据0x00+ 0xC0至模块后LED常亮起保持模块水平,缓慢旋转1周(旋转1周时间大约1分钟)。发送数据0x00+0XC1至模块,LED灭,校准结束。
七、结束 (1)、模块参数都是基于一个标准5V状态下测试的。建议使用纹波较小的电源供给。
(2)、模块测量时LED的闪动对应测量的频率。校准时LED常亮。 (3)、模块测量时和校准时,保持水平,将获得最好的准确度,测量时应远离磁体尽量30厘米以上。 (4)、模块内部有记忆功能,可掉电记忆校准系数及磁偏角。
(5)、恢复出厂设定,即恢复出厂时的校准系数及磁偏角度,并不会修改模块当前的IIC地址。 (6)、模块输入输出的高低电平3-5V,可以直接与单片机串口,单片机IO连接,可以直接与PL2303,CH340,FT232等芯片连接,但不能与电脑串口直接连接 。 原理图:
C程序 - // GY-26电子指南针模块测试程序
- // 使用单片机:STC89C52
- // 晶振: 11.0592 M
- // 液晶屏:LCD1602
- #include<REG52.H>
- #include<math.h> //Keil library
- #include<stdio.h> //Keil library
- #include<INTRINS.H>
- #define uchar unsigned char
- #define uint unsigned int
- #define DataPort P0 //LCD1602 数据端口
- sbit LCM_RS=P2^0; //LCD1602 控制端口
- sbit LCM_RW=P2^1; //LCD1602 控制端口
- sbit LCM_EN=P2^2; //LCD1602 控制端口
- sbit KEY_1 =P2^3;
- sbit KEY_2 =P2^4;
- sbit KEY_3 =P2^5;
- sbit KEY_4 =P2^6;
- //********定义变量*****************************
- uchar BUF[8]; //数据缓存区
- uchar cnt; //接收数据的累计值
- //********定义函数*****************************
- void delay(unsigned int k);
- void InitLcd();
- void WriteDataLCM(uchar dataW);
- void WriteCommandLCM(uchar CMD,uchar Attribc);
- void DisplayOneChar(uchar X,uchar Y,uchar DData);
- void SeriPushSend(uchar send_data);
- void ReadKey(void);
- void display(void);
- //*********************************************
- //串口中断
- void serial_serve(void) interrupt 4
- {
- if(RI==1){
- RI=0;
- BUF[cnt]=SBUF;
- cnt++;
- // if(cnt==7)flag=1;
- }
- }
- //*********************************************
- //串口初始化
- //9600 bps @ 11.059 MHz
- void init_uart()
- {
- TMOD=0x21;
- TH1=0xfd;
- TL1=0xfd;
- SCON=0x50;
- PS=1; //串口中断设为高优先级别
- TR0=1; //启动定时器
- TR1=1;
- ET0=1; //打开定时器0中断
- ES=1;
- }
- //*********LCD1602初始化**********************
- void InitLcd()
- {
- WriteCommandLCM(0x38,1);
- WriteCommandLCM(0x08,1);
- WriteCommandLCM(0x01,1);
- WriteCommandLCM(0x06,1);
- WriteCommandLCM(0x0c,1);
- }
- //**********检测忙信号************************
- void WaitForEnable(void)
- {
- DataPort=0xff;
- LCM_RS=0;LCM_RW=1;_nop_();
- LCM_EN=1;_nop_();_nop_();
- while(DataPort&0x80);
- LCM_EN=0;
- }
- //**********写命令至LCD***********************
- void WriteCommandLCM(uchar CMD,uchar Attribc)
- {
- if(Attribc)WaitForEnable();
- LCM_RS=0;LCM_RW=0;_nop_();
- DataPort=CMD;_nop_();
- LCM_EN=1;_nop_();_nop_();LCM_EN=0;
- }
- //**********写数据至LCD************************
- void WriteDataLCM(uchar dataW)
- {
- WaitForEnable();
- LCM_RS=1;LCM_RW=0;_nop_();
- DataPort=dataW;_nop_();
- LCM_EN=1;_nop_();_nop_();LCM_EN=0;
- }
- //*********写一个字符数据到指定的目标***********
- void DisplayOneChar(uchar X,uchar Y,uchar DData)
- {
- Y&=1;
- X&=15;
- if(Y)X|=0x40;
- X|=0x80;
- WriteCommandLCM(X,0);
- WriteDataLCM(DData);
- }
- //**********延时函数***************
- void delay(unsigned int k)
- {
- unsigned int i,j;
- for(i=0;i<k;i++)
- {
- for(j=0;j<121;j++)
- {;}
- }
- }
- //*********串口数据发送******************
- void SeriPushSend(uchar send_data)
- {
- SBUF=send_data;
- while(!TI);TI=0;
- }
- //*********按键检测**********************
- void ReadKey(void)
- {
- EA=0;
- if(KEY_1==0){
- delay(50);
- if(KEY_1==0){
- SeriPushSend(0XC0); //发送校准命令。
- display();
- while(KEY_1==0); //等待按键放开
- }
- }
- if(KEY_2==0){
- delay(50);
- if(KEY_2==0){
- SeriPushSend(0XC1); //发送停止校准命令。
- display();
- while(KEY_2==0); //等待按键放开
- }
- }
- if(KEY_3==0){
- delay(50);
- if(KEY_3==0){
- SeriPushSend(0XA0); //发送恢复出厂命令。
- display();
- SeriPushSend(0XAA); //发送恢复出厂命令。
- display();
- SeriPushSend(0XA5); //发送恢复出厂命令。
- display();
- SeriPushSend(0XC5); //发送恢复出厂命令。
- display();
- while(KEY_3==0); //等待按键放开
- }
- }
- if(KEY_4==0){
- delay(50);
- if(KEY_4==0){
- SeriPushSend(0X3); //发送设定磁偏角高8位命令。
- display();
- SeriPushSend(0X0); //发送磁偏角高8位的数据。
- display();
- SeriPushSend(0X4); //发送设定磁偏角低8位命令
- display();
- SeriPushSend(0X64); //发送磁偏角低8位的数据。此时磁偏角被设定为10.0度
- display();
- while(KEY_4==0); //等待按键放开
- }
- }
- /*
- if(KEY_5==0){
- delay(50);
- if(KEY_4==0){
- SeriPushSend(0XAE); //发送当前角度为0度命令。
- display();
- while(KEY_4==0); //等待按键放开
- }
- }
- */
- EA=1;
-
- }
- //*******************数据接收并显示**********************
- void display(void)
- {
- uchar SUM;
- uint i ;
- for (i=0;i<3000;i++); //传送数据延时
- if(BUF[0]==0X0D&&BUF[1]==0X0A){ //帧头判断
- SUM=BUF[6]+BUF[5]+BUF[4]+BUF[3]+BUF[2]+BUF[1]+BUF[0]; //校验和
- if (BUF[7]==SUM){ //校验和判断
- DisplayOneChar(0,0,BUF[2]); //角度百位值 写入LCD,因为是字符类型,所以不用转换,直接显示
- DisplayOneChar(1,0,BUF[3]); //角度十位值 写入LCD
- DisplayOneChar(2,0,BUF[4]); //角度个位值 写入LCD
- DisplayOneChar(3,0,BUF[5]); //小数点
- DisplayOneChar(4,0,BUF[6]); //角度小数位值 写入LCD
- } //end if
- }
- }
- //***************************************
- //主程序
- void main()
- { uint i ;
- delay(500); //延时
- InitLcd(); //初始化LCD
- init_uart(); //初始化串口
- EA=1; //开总中断
- while(1){ //循环
- cnt=0; //接收数据的累计值
- SeriPushSend(0X31); //发送1帧读取命令。
- display();
- ReadKey(); //按键检测
- for (i=0;i<12000;i++); //一定时间的延时
- } //end while
- }
复制代码
PIC原理图
GY-26电子指南针PIC代码IIC
- /*****************************************
- * 基于PIC单片机GY-26模块通信程序 *
- * 功 能:IIC通信读取数据并显示 *
- * 时钟频率:4M晶振 *
- * 设 计:电应普公司 *
- * 修改日期:2010年10月26日 *
- * 编译环境:MPLAB-IDEv8.60+HI-TECHv9.60 *
- * 实验环境:PIC16F877A+1602 *
- * 使用端口:RC7,RC6,RC5,RC4,RC3,PORTD *
- * 参 考:网友24c02IIC程序,1602程序 *
- *****************************************/
- #include <pic.h> //调用头文件
- #include <stdio.h>
- __CONFIG(0x3F32); //芯片配置字
- #define uchar unsigned char
- #define uint unsigned int
- #define ulint unsigned long int
- #define GY_26_ADDR 0xE0 //定义电子指南针器件地址(出厂默认为0XE0)
- #define LCD_DATA_PORT PORTD //定义LCD的D0-D7所在端口
- #define LCD_DATA_POUT TRISD= 0x00 //定义LCD数据端口为输出
- #define LCD_DATA_PIN TRISD= 0xFF //定义LCD数据端口为输入(在读LCD状态时用到)
- #define LCD_Control_OUT TRISC&=0X1F //定义LCD控制端口输出
- #define LCD_EN RC7 //LCD对应端口
- #define LCD_RW RC6 //LCD对应端口
- #define LCD_RS RC5 //LCD对应端口
- //函数申明**********************************
- void delay_20us(uint nus);
- void nms_delay(uint nms);
- void mcu_init(void);
- void lcd_init(void);
- void LCD_en_write(void);
- void wait_LCD_Ready(void);
- void set_LCD_xy(uchar x, uchar y );
- void LCD_write_onechar(uchar COMM,uchar DAT);
- void LCD_write_string(uchar X,uchar Y,uchar *string);
- uint get_GY_26(uchar read_begin_address,uchar GY_26_cmd);
- void conversion(uint i);
- //iic使用函数********************************
- void iic_Start(void){SEN=1;while(SEN);} //启动iic
- void iic_Stop (void){PEN=1;while(PEN);} //停止iic
- void iic_rest (void){RSEN=1;while(RSEN);} //重启iic
- void iic_Write8Bit(uchar x){SSPIF=0;SSPBUF=x;while(!SSPIF);SSPIF=0;}//向iic总线写8bit数据
- uchar iic_read8Bit(void){RCEN=1;while(!BF);ACKEN=1;while(ACKEN);return SSPBUF;}//向iic总线读8bit数据
- //数组****************************************
- uchar START_DISPLAY1[16]={"Compass: "};//LCD初始化显示数据
- uchar START_DISPLAY2[16]={" Temp : "};//LCD初始化显示数据
- uchar display[7]={0,0,0,'.',0,0Xdf,0}; //显示数据数组
- /*********************************************
- 数据转换,十六进制数据转换成10进制
- 输入十六进制范围:0x0000-0x270f(0-9999)
- 结果分成个十百千位,以ascii存入显示区
- **********************************************/
- void conversion(unsigned int i)
- {
- display[0]=i/1000+0x30 ;
- i=i%1000; //取余运算
- display[1]=i/100+0x30 ;
- i=i%100; //取余运算
- display[2]=i/10+0x30 ;
- i=i%10; //取余运算
- display[4]=i+0x30;
- }
- //单片机初始化**************
- void mcu_init(void)
- {
- TRISC=0XFF; //IIC通信引脚设为输入
- SSPSTAT = 0x80; //IIC设置
- SSPCON = 0x38; //IIC设置
- SSPCON2 = 0x00; //IIC设置
- SSPADD = 50; //IIC设置
- }
- //LCD初始化***************************
- void lcd_init(void)
- {
- nms_delay(100);
- LCD_DATA_POUT; //LCD数据口设为输出
- LCD_Control_OUT;
- LCD_write_onechar(0x38,0);
- LCD_write_onechar(0x08,0);
- LCD_write_onechar(0x01,0);
- LCD_write_onechar(0x06,0);
- LCD_write_onechar(0x0C,0);
- }
- //LCD写使能******************************
- void LCD_en_write(void)
- {
- LCD_EN=1;
- // delay_20us(1); //E脉冲周期最小150ns
- LCD_EN=0;
- }
- //等待LCD空闲****************************
- void wait_LCD_Ready(void)
- {
- uchar temp1;
- LCD_DATA_PORT=0XFF;
- LCD_DATA_PIN; //准备判断LCD忙标志
- LCD_RS=0;
- LCD_RW=1; //读
- LCD_EN=1;
- delay_20us(1);
- while((temp1=PORTD & 0X80));
- LCD_EN=0;
- LCD_RS=1;
- LCD_DATA_POUT;
- }
- //LCD写一个字符**********************
- void LCD_write_onechar(uchar COMM,uchar DAT)
- {
- wait_LCD_Ready(); //等待LCD空闲
- LCD_RW=0; //写
- if(COMM==0){ //写数据
- LCD_RS=1; //RS高电平向LCD写数据
- LCD_DATA_PORT=DAT;
- }
- else{ //写命令
- LCD_RS=0;
- LCD_DATA_PORT=COMM;
- }
- LCD_en_write();
- delay_20us(1);
- LCD_RW=1;
- LCD_RS=1;
- }
- //LCD写字符到指定坐标******************************
- void LCD_write_string(uchar X,uchar Y,uchar *string)
- {
- set_LCD_xy( X, Y ); //设置LCD显示坐标
- while (*string)
- {
- LCD_write_onechar(0,*string );
- string ++; //指向下一显示字符地址
- }
- }
- //LCD写坐标设置**********************************
- void set_LCD_xy(uchar x, uchar y )
- {
- uchar DIS_address;
- if (y == 0)
- DIS_address = 0x80 + x; //第一行X列
- else
- DIS_address = 0xc0 + x;
- LCD_write_onechar( DIS_address, 0 ); //第二行X列
- }
- //延时大约20us***************************************
- void delay_20us(uint nus)
- { uchar i;
- while(--nus){
- for(i=2;i>0;i--);
- }
- }
- //延时大约1ms***************************************
- void nms_delay(uint nms)
- {
- while(nms--)
- delay_20us(50);
- }
- //从IIC总线获得电子指南针GY-26数据********************
- //从开始地址连续读出两个8bit的值,并合成16bit数据
- uint get_GY_26(uchar read_begin_address,uchar GY_26_cmd)
- { unsigned int temp;
- iic_Start(); //iic启动时序
- iic_Write8Bit(GY_26_ADDR); //写指南针器件地址
- iic_Write8Bit(0); //写命令
- iic_Write8Bit(GY_26_cmd); //写指南针命令
- iic_Stop(); //iic写停止时序
- nms_delay(55); //延时,可执行其他程序
- iic_Start(); //iic启动时序
- ACKDT = 0; //允许应答ACK
- iic_Write8Bit(GY_26_ADDR); //写指南针器件地址
- iic_Write8Bit(read_begin_address); //写指南针内部数据地址
- iic_rest(); //iic重启时序
- iic_Write8Bit(GY_26_ADDR+1); //指南针器件地址+1=读
- temp=iic_read8Bit(); //读出8位数据
- temp<<=8; //保存数据
- ACKDT = 1; //允许应答NO_ACK
- temp+=iic_read8Bit(); //读出8位数据
- iic_Stop(); //发送停止时序
- return temp; //返回本次测量数值
- }
- //主程序************************************************
- void main(void)
- { unsigned int CMP_DATA,TEMP_DATA;
- mcu_init(); //单片机初始化
- lcd_init(); //lcd1602初始化
- LCD_write_string(0,0,START_DISPLAY1);//LCD初始化显示数据
- LCD_write_string(0,1,START_DISPLAY2);//LCD初始化显示数据
- while(1)
- {
- CMP_DATA=get_GY_26(1,0x31); //读取角度
- conversion(CMP_DATA); //数据转换出个,十,百,千 位
- LCD_write_string(9,0,display); //显示角度,从第0行第9列开始
- TEMP_DATA=get_GY_26(5,0x35); //读取温度
- conversion(TEMP_DATA); //数据转换出个,十,百,千 位
- LCD_write_string(9,1,display); //显示角度,从第1行第9列开始
- }
- }
复制代码
|