最近買了個數(shù)字羅盤模塊,調(diào)通后發(fā)現(xiàn)很不錯,非常靈敏,測試的時候精度在1°以內(nèi)。連續(xù)測量模式下,最快測量、輸出速率可達75hz,模塊每次測量完畢并將數(shù)據(jù)更新至寄存器后,其DRDY引腳便產(chǎn)生一個低電平脈沖(可以配置一個外部中斷捕獲DRDY引腳的下降沿,并在中斷服務程序中讀取數(shù)據(jù)),在STM32中可以設置一個下降沿觸發(fā)的外部中斷,并在中斷服務程序中調(diào)用角度數(shù)據(jù)讀取函數(shù)。以下為操作該模塊的主要步驟。
一、IIC協(xié)議相關操作(單片機作為主機控制時鐘線)
宏定義:
//這里用到了STM32的位帶區(qū)操作,方便實現(xiàn)對一個位的操作
//PB13配置為OD輸出,同時外部給上拉電阻,這樣既可輸出信號給從機,也能
//在PB13為漏極開路狀態(tài)時接收從機的信號(STM32的IO配置為輸出模式時,
//IO口的電平也會不斷地被捕獲到輸入寄存器中)
//PB14配置為推挽輸出,PB15配置為浮空輸入
#defineW_SDAOPB13//PB13輸出寄存器
#defineW_SCLOPB14//PB14輸出寄存器
#defineR_DRDYIPB15//PB15輸入寄存器
#defineXmsb0//X軸數(shù)字量的高8位
#defineXlsb1//X軸數(shù)字量的低8位
#defineZmsb2//Z軸數(shù)字量的高8位
#defineZlsb3//Z軸數(shù)字量的低8位
#defineYmsb4//Y軸數(shù)字量的高8位
#defineYlsb5//Y軸數(shù)字量的低8位
附位帶宏定義:
#defineGPIOB_ODR_Addr(GPIOB_BASE+12)//0x40010C0C
#defineGPIOB_IDR_Addr(GPIOB_BASE+8)//0x40010C08
#defineBITBAND_Addr(Addr,num)((volatileunsignedlong*)(0x42000000+32*(Addr-0x40000000)+4*num))
#defineIPB13*BITBAND_Addr(GPIOB_IDR_Addr,13)
#defineOPB13*BITBAND_Addr(GPIOB_ODR_Addr,13)
#defineOPB14*BITBAND_Addr(GPIOB_ODR_Addr,14)
#defineIPB15*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;
發(fā)送一個字節(jié):
uint8_t_iic_SendByte(uint8_tdat)
{
uint8_ti;
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
return0;
}
else{//有應答
_delay();
W_SCL=0;//重新拉低SCL
return1;
}
}
_delay();
W_SDA=1;//SCL為高時,拉高SDA表示結束ICC傳輸,釋放總線
}
接收一個字節(jié):
uint8_t_iic_ReadByte(uint8_tAck)
{
uint8_ttemp,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,從器件開始放置數(shù)據(jù)
}
//發(fā)送應答信號
if(Ack)W_SDA=0;//拉低SDA表示應答
W_SCL=1;//拉高SCL,從器件接收應答信號
_delay();
W_SCL=0;//重新拉低SCL
W_SDA=1;//釋放SDA
returntemp;
}
二、配置HMC5883L模塊
voidHMC5883L_Init()
{
_iic_Start();
_iic_SendByte(0x3c);//寫操作
_iic_SendByte(0x00);//指針指向00,配置寄存器A
_iic_SendByte(0x78);//數(shù)據(jù)測量、輸出速率75hz
_iic_Start();//指針定位到02,模式寄存器
_iic_SendByte(0x3c);
_iic_SendByte(0x02);
_iic_SendByte(0x00);//連續(xù)測量模式
_iic_Stop();
}
三、讀取角度數(shù)據(jù)
接收三軸數(shù)據(jù),處理X,Y軸的數(shù)據(jù)并計算角度:
int16_tHMC5883L_ReadAngle()
{
staticuint8_ti;
staticuint8_tXYZ_Data[6];//用來存儲三個軸輸出的數(shù)字量
_iic_Start();
_iic_SendByte(0x3c);//發(fā)送HMC5883L的器件地址0x3c,寫操作
_iic_SendByte(0x03);//指針指向03,Xmsb寄存器
_iic_Start();
_iic_SendByte(0x3d);//改為讀操作
//依次讀取三個軸的數(shù)字量
for(i=0;i《5;i++)//前5次讀取發(fā)送應答信號
{
XYZ_Data[i]=_iic_ReadByte(1);
}
XYZ_Data[5]=_iic_ReadByte(0);//不應答
_iic_Stop();
returnatan2((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口,調(diào)用HMC5883L_Init()后,便可調(diào)用HMC5883L_ReadAngle()讀取角度值,0~360°。以下為測試時的截圖:
測試時,模塊比較靈敏且精確,稍微旋轉模塊便有精確的變化。由于該模塊是基于對地磁場的測量,此模塊容易受到其他磁場的干擾,比如將該模塊靠近直流電機時,便會因為電機內(nèi)的磁場而降低精度甚至失靈(之前做智能小車時就遇到這個問題,要將電機內(nèi)的磁場屏蔽起來才行)。
評論
查看更多