最近買了個數字羅盤模塊,調通后發現很不錯,非常靈敏,測試的時候精度在1°以內。連續測量模式下,最快測量、輸出速率可達75hz,模塊每次測量完畢並將數據更新至寄存器后,其DRDY引腳便產生一個低電平脈沖(可以配置一個外部中斷捕獲DRDY引腳的下降沿,並在中斷服務程序中讀取數據),在STM32中可以設置一個下降沿觸發的外部中斷,並在中斷服務程序中調用角度數據讀取函數。以下為操作該模塊的主要步驟。
一、IIC協議相關操作(單片機作為主機控制時鍾線)
宏定義:
//這里用到了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; }
二、配置HMC5883L模塊
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°。
以下為測試時的截圖:
測試時,模塊比較靈敏且精確,稍微旋轉模塊便有精確的變化。由於該模塊是基於對地磁場的測量,此模塊容易受到其他磁場的干擾,比如將該模塊靠近直流電機時,
便會因為電機內的磁場而降低精度甚至失靈(之前做智能小車時就遇到這個問題,要將電機內的磁場屏蔽起來才行)。
(完)