标签:
最近买了个数字罗盘模块,调通后发现很不错,非常灵敏,测试的时候精度在1°以内。连续测量模式下,最快测量、输出速率可达75hz,模块每次测量完毕并将数据更新至寄存器后,其DRDY引脚便产生一个低电平脉冲(可以配置一个外部中断捕获DRDY引脚的下降沿,并在中断服务程序中读取数据),在STM32中可以设置一个下降沿触发的外部中断,并在中断服务程序中调用角度数据读取函数。以下为操作该模块的主要步骤。
宏定义:
//这里用到了STM32的位带区操作,方便实现对一个位的操作 //PB13配置为OD输出,同时外部给上拉电阻,这样既可输出信号给从机,也能 //在PB13为漏极开路状态时接收从机的信号(STM32的IO配置为输出模式时, //IO口的电平也会不断地被捕获到输入寄存器中) //PB14配置为推挽输出,PB15配置为浮空输入 #define R_SDA IPB13 // PB13输入寄存器 #define W_SDA OPB13 // PB13输出寄存器 #define W_SCL OPB14 // PB14输出寄存器 #define R_DRDY IPB15 // PB15输入寄存器
#define Xmsb 0 //X轴数字量的高8位 #define Xlsb 1 //X轴数字量的低8位 #define Zmsb 2 //Z轴数字量的高8位 #define Zlsb 3 //Z轴数字量的低8位 #define Ymsb 4 //Y轴数字量的高8位 #define Ylsb 5 //Y轴数字量的低8位
附位带宏定义:
#define GPIOB_ODR_Addr (GPIOB_BASE+12) //0x40010C0C #define GPIOB_IDR_Addr (GPIOB_BASE+8) //0x40010C08 #define BITBAND_Addr(Addr,num) ((volatile unsigned long *)(0x42000000+32*(Addr-0x40000000)+4*num)) #define IPB13 *BITBAND_Addr(GPIOB_IDR_Addr,13) #define OPB13 *BITBAND_Addr(GPIOB_ODR_Addr,13) #define OPB14 *BITBAND_Addr(GPIOB_ODR_Addr,14) #define IPB15 *BITBAND_Addr(GPIOB_IDR_Addr,15)
启动IIC传输:
void _iic_Start() { W_SCL=1; W_SDA=1; _delay(); W_SDA=0; //SCL高时,拉低SDA,表示开始IIC传输,占用总线 _delay(); W_SCL=0; //控制SCL _delay(); }
停止IIC传输:
void _iic_Stop() { W_SCL=1; //释放SCL(由于没有其他主器件,SCL无需开漏) W_SDA=0; _delay(); W_SDA=1; //SCL为高时,拉高SDA表示结束ICC传输,释放总线 }
发送一个字节:
uint8_t _iic_SendByte(uint8_t dat) { uint8_t i; for(i=0;i<8;i++) { _delay(); W_SDA=dat>>7; //SCL拉高之前写SDA dat=dat<<1; _delay(); W_SCL=1; //拉高SCL,从器件开始读取SDA _delay(); W_SCL=0; //重新拉低SCL } W_SDA=1; //释放SDA W_SCL=1; //拉高SCL,读取从器件应答信号 // 等待应答 i=100; while(i&&R_SDA) {i--;_delay();} if(i==0) //无应答 { W_SCL=0; //重新拉低SCL return 0; } else { //有应答 _delay(); W_SCL=0; //重新拉低SCL return 1; } }
接收一个字节:
uint8_t _iic_ReadByte(uint8_t Ack) { uint8_t temp,i; W_SDA=1; //释放SDA _delay(); for(i=0;i<8;i++) { _delay(); W_SCL=1; //拉高SCL开始读取SDA temp=temp<<1; temp|=R_SDA; //SCL拉高之后读取SDA W_SCL=0; //拉低SCL,从器件开始放置数据 } //发送应答信号 if(Ack)W_SDA=0; //拉低SDA表示应答 W_SCL=1; //拉高SCL,从器件接收应答信号 _delay(); W_SCL=0; //重新拉低SCL W_SDA=1; //释放SDA return temp; }
void HMC5883L_Init() { _iic_Start(); _iic_SendByte(0x3c); //写操作 _iic_SendByte(0x00); //指针指向00,配置寄存器A _iic_SendByte(0x78); //数据测量、输出速率75hz _iic_Start(); //指针定位到02,模式寄存器 _iic_SendByte(0x3c); _iic_SendByte(0x02); _iic_SendByte(0x00); //连续测量模式 _iic_Stop(); }
接收三轴数据,处理X,Y轴的数据并计算角度:
int16_t HMC5883L_ReadAngle() { static uint8_t i; static uint8_t XYZ_Data[6]; //用来存储三个轴输出的数字量 _iic_Start(); _iic_SendByte(0x3c); // 发送HMC5883L的器件地址0x3c,写操作 _iic_SendByte(0x03); //指针指向03,X msb寄存器 _iic_Start(); _iic_SendByte(0x3d); //改为读操作 //依次读取三个轴的数字量 for(i=0;i<5;i++) //前5次读取发送应答信号 { XYZ_Data[i]=_iic_ReadByte(1); } XYZ_Data[5] =_iic_ReadByte(0); //不应答 _iic_Stop(); return atan2( (double)((int16_t)((XYZ_Data[Ymsb]<<8)+XYZ_Data[Ylsb]) ),(double)((int16_t)((XYZ_Data[Xmsb]<<8)+XYZ_Data[Xlsb])))*(180/3.14159265)+180; //计算角度,需要包含math.h头文件 }
配置好IO口,调用HMC5883L_Init()后,便可调用HMC5883L_ReadAngle()读取角度值,0~360°。
以下为测试时的截图:
测试时,模块比较灵敏且精确,稍微旋转模块便有精确的变化。由于该模块是基于对地磁场的测量,此模块容易受到其他磁场的干扰,比如将该模块靠近直流电机时,
便会因为电机内的磁场而降低精度甚至失灵(之前做智能小车时就遇到这个问题,要将电机内的磁场屏蔽起来才行)。
(完)
标签:
原文地址:http://www.cnblogs.com/2seek/p/4515841.html