stm32與地磁感測器HMC5883L
1.簡介
霍尼韋爾 HMC5883L 是一種表面貼裝的高集成模組,並帶有數字介面的弱磁感測器晶片,應用於低成本羅盤和磁場檢測領域。HMC5883L 包括最先進的高解析度 HMC118X 系列磁阻感測器,並附帶霍尼韋爾專利的積體電路包括放大器、自動消磁驅動器、偏差校準、能使羅盤精度控制在 1°~2°的 12 位模數轉換器.簡易的 I2 C 系列匯流排介面。HMC5883L 是採用無鉛表面封裝技術,帶有 16 引腳,尺寸為 3.0X3.0X0.9mm。HMC5883L 的所應用領域有手機、筆記型電腦電腦、消費類電子、汽車導航系統和個人導航系統。
HMC5883L 採用霍尼韋爾各向異性磁阻(AMR)技術,該技術的優點是其他磁感測器技術所無法企及。這些各向異性感測器具有在軸向高靈敏度和線性高精度的特點.感測器帶有的對於正交軸低敏感行的固相結構能用於測量地球磁場的方向和大小,其測量範圍從毫高斯到 8高斯(gauss)。 霍尼韋爾的磁感測器在低磁場感測器行業中是靈敏度最高和可靠性最好的感測器。
2.硬體連接
下圖為HMC5883L與STM32的連接圖,可以看出它是用的IIC通訊方式,注意這邊上拉電阻用的是10K的(官方推薦),我個人還是用4.7K進行測試讀寫。
3.暫存器介紹
提到暫存器就必須說一下,大家買HMC5883L的時候一定要注意!!!別錯買QMC5883L,兩者的暫存器地址是不一樣的哦。下面簡單介紹一下各個暫存器:
配置暫存器 A:
配置暫存器是用來配置該裝置設置的數據輸出速率和測量配置。 CRA0 通過 CRA7 表明位的位置,用 CAR 指示在配置暫存器中的位。 CRA7 指示數據流的第一位。括弧中的數目顯示是該位的默認值(不太可靠,僅供參考)。
上表為:配置暫存器A,下表尾配置暫存器A的位分配情況
下表的數據顯示在連續測量模式下的所有可選的輸出速率。所有這三個通道應在某一特定數據速率下測量。其他輸出速率可以通過控制單測量模式下的 DRDY 中斷引腳來獲得,最大速率為 160Hz。
上表為:數據輸出速率,下表為測量模式選擇
配置暫存器 B

下表描述增益設置。使用以下「增益」一欄將counts轉換成Guass。在總共磁場強度引起所有數據輸出存儲器中一個溢位(飽和)時選擇較低的增益值(高GN#值)。
模式暫存器
上表為:模式暫存器位分配,下表為:操作模式
數據輸出 X 暫存器 A 和B

下面的Y和Z和X類似就不介紹了。
4.軟體設計
用到IIC通訊必須要注意一下HMC5883L的控制時序,各位注意修改,我這邊直接用的HAL庫自帶的延時,下面看一下IIC的程式:


//--------------------------------------------i2c------------------------------------------- // 設置SDA為輸出方向,對於雙向I/O需切換為輸出 void SDA_D_OUT() { GPIO_InitTypeDef GPIO_InitStruct = {0}; GPIO_InitStruct.Pin = GPIO_PIN_9; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); } // 設置SDA為輸入方向,對於雙向I/O需切換為輸入 void SDA_D_IN() { GPIO_InitTypeDef GPIO_InitStruct = {0}; GPIO_InitStruct.Pin = GPIO_PIN_9; GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pull = GPIO_NOPULL; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); } /************************************** 起始訊號 **************************************/ void HMC5883_Start() { SDA_D_OUT(); SDA_H; //拉高數據線 SCL_H; //拉高時鐘線 HAL_Delay(1); //延時 SDA_L; //產生下降沿 HAL_Delay(1); //延時 SCL_L; //拉低時鐘線 } /************************************** 停止訊號 **************************************/ void HMC5883_Stop() { //SDA_D_OUT(); SCL_H; //拉高時鐘線 SDA_L; //拉低數據線 HAL_Delay(1); //延時 SDA_H; //產生上升沿 HAL_Delay(1); //延時 } /************************************** 發送應答訊號 入口參數:ack (0:ACK 1:NAK) **************************************/ void HMC5883_SendACK(uint8_t ack) { SDA_D_OUT(); if(ack==1) { SDA_H; }else { SDA_L; } SCL_H; //拉高時鐘線 HAL_Delay(1); //延時 SCL_L; //拉低時鐘線 HAL_Delay(1); //延時 } /************************************** 接收應答訊號 **************************************/ uint8_t HMC5883_RecvACK() { SDA_D_IN(); SCL_H; //拉高時鐘線 HAL_Delay(10); //延時 CY = READ_SDA_IN; //進位標誌 讀應答訊號 SCL_L; //拉低時鐘線 HAL_Delay(1); //延時 return CY; } //等待從機應答訊號 //返回值:1 接收應答失敗 // 0 接收應答成功 uint8_t HMC5883L_I2C_Wait_Ack(void) { uint8_t tempTime=0; SDA_D_IN(); SDA_H; HAL_Delay(1); SCL_H; HAL_Delay(1); while(READ_SDA_IN) { tempTime++; if(tempTime>250) { HMC5883_Stop(); return 1; } } SCL_L; return 0; } /************************************** 向IIC匯流排發送一個位元組數據 **************************************/ void HMC5883_SendByte(uint8_t dat) { uint8_t i; SDA_D_OUT(); SCL_L; //拉低時鐘開始數據傳輸 for (i=0; i<8; i++) //8位計數器 { //dat <<= 1; //移出數據的最高位 if( dat & 0x80 ) { SDA_SET; } else { SDA_CLR; } HAL_Delay(1); //延時 dat <<= 1; //移出數據的最高位 SCL_H; //拉高時鐘線 HAL_Delay(1); //延時 SCL_L; //拉低時鐘線 } HMC5883_RecvACK(); } /************************************** 從IIC匯流排接收一個位元組數據 **************************************/ uint8_t HMC5883_RecvByte() { uint8_t i; uint8_t dat = 0; SDA_D_IN(); SDA_H; //使能內部上拉,準備讀取數據, for (i=0; i<8; i++) //8位計數器 { dat <<= 1; SCL_H; //拉高時鐘線 HAL_Delay(1); //延時 if(READ_SDA_IN)dat++; //dat <<= 1; //dat |= SDA; //讀數據 SCL_L; //拉低時鐘線 HAL_Delay(1); //延時 } return dat; } //************************寫入單位元組數據*************************** //void Single_Write_QMC5883(uint8_t REG_Address,uint8_t REG_data) //{ // QMC5883_Start(); //起始訊號 // QMC5883_SendByte(Slave_Address); //發送設備地址+寫訊號 // //if(QMC5883L_I2C_Wait_Ack()) // //{ // // QMC5883_Stop(); // // printf("error\r\n"); // //} // QMC5883_SendByte(REG_Address); //內部暫存器地址,請參考中文pdf // QMC5883_SendByte(REG_data); //內部暫存器數據,請參考中文pdf // QMC5883_Stop(); //發送停止訊號 //} //************************讀取單位元組數據************************* uint8_t Single_Read_HMC5883(uint8_t REG_Address) { uint8_t REG_data; HMC5883_Start(); //起始訊號 HMC5883_SendByte(Slave_Address); //發送設備地址+寫訊號 HMC5883_SendByte(REG_Address); //發送存儲單元地址,從0開始 HMC5883_Start(); //起始訊號 HMC5883_SendByte(Slave_Address+1); //發送設備地址+讀訊號 REG_data=HMC5883_RecvByte(); //讀出暫存器數據 HMC5883_SendACK(1); HMC5883_Stop(); //停止訊號 return REG_data; } //****************************************************** //連續讀出QMC5883內部角度數據,地址範圍0x00~0x05 //****************************************************** void Multiple_Read_HMC5883(void) { uint8_t i=0; HMC5883_Start(); //起始訊號 HMC5883_SendByte(Slave_Address); //發送設備地址+寫訊號 HMC5883_SendByte(0x03); //發送存儲單元地址,從0x00開始 HMC5883_Start(); //起始訊號 HMC5883_SendByte(Slave_Address+1); //發送設備地址+讀訊號 for (i=0; i<6; i++) //連續讀取6個地址數據,存儲中BUF { BUF[i] = HMC5883_RecvByte(); //BUF[0]存儲數據 if (i == 5) { HMC5883_SendACK(1); //最後一個數據需要回非應答NOACK } else { HMC5883_SendACK(0); //應答ACK } } x=(BUF[0]<<8)|(BUF[1]); if(x>32767) x=0xffff-x+1; z=(BUF[2]<<8)|(BUF[3]); if(z>32767) z=0xffff-z+1; y=(BUF[4]<<8)|(BUF[5]); if(y>32767) y=0xffff-y+1; HMC5883_Stop(); //停止訊號 HAL_Delay(10); } //2初始化HMC5883,根據需要請參考pdf進行修改**** void HMC5883L_Init() { HMC5883_Start(); HMC5883_SendByte(0x3c); //發送設備地址+寫訊號 HMC5883_SendByte(0x00); //內部暫存器地址,請參考中文pdf HMC5883_SendByte(0x78); //內部暫存器數據,請參考中文pdf HMC5883_Start(); HMC5883_SendByte(0x3c); //發送設備地址+寫訊號 HMC5883_SendByte(0x01); //內部暫存器地址,請參考中文pdf HMC5883_SendByte(0x00); //內部暫存器數據,請參考中文pdf HMC5883_Start(); HMC5883_SendByte(0x3c); //發送設備地址+寫訊號 HMC5883_SendByte(0x02); //內部暫存器地址,請參考中文pdf HMC5883_SendByte(0x00); //內部暫存器數據,請參考中文pdf HMC5883_Stop(); }
IIC通訊
預定義和主程式如下:


// 設置系統參數命令 #define SCL_SET HAL_GPIO_WritePin(GPIOB,GPIO_PIN_8,GPIO_PIN_SET) #define SCL_H HAL_GPIO_WritePin(GPIOB,GPIO_PIN_8,GPIO_PIN_SET) #define SCL_CLR HAL_GPIO_WritePin(GPIOB,GPIO_PIN_8,GPIO_PIN_RESET) #define SCL_L HAL_GPIO_WritePin(GPIOB,GPIO_PIN_8,GPIO_PIN_RESET) #define SCL_D_OUT {} // 設置SCL為輸出方向,對於雙向I/O需切換為輸出 #define SDA_SET HAL_GPIO_WritePin(GPIOB,GPIO_PIN_9,GPIO_PIN_SET) #define SDA_H HAL_GPIO_WritePin(GPIOB,GPIO_PIN_9,GPIO_PIN_SET) #define SDA_CLR HAL_GPIO_WritePin(GPIOB,GPIO_PIN_9,GPIO_PIN_RESET) #define SDA_L HAL_GPIO_WritePin(GPIOB,GPIO_PIN_9,GPIO_PIN_RESET) #define CH455_SDA_IN HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_9) // 讀取SDA輸入電平 #define READ_SDA_IN HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_9) // 讀取SDA輸入電平 #define Slave_Address 0x3C //定義器件在IIC匯流排中的從地址 write
預定義


int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_USART1_UART_Init(); printf("===========\r\n"); HMC5883L_Init(); for(int i=0;i<13;i++) { printf("%d:%d\r\n",i,Single_Read_HMC5883(i)); } while (1) { Multiple_Read_HMC5883(); //連續讀取三軸角度數據,存儲在BUF中 printf("x:%d\r\ny:%d\r\nz:%d\r\n",x,y,z); printf("=================================\r\n"); HAL_Delay(1000); } }
主程式
下面看一下串口輸出結果:
上面輸出的0~12是相對應的暫存器值,10~12應該是固定值,xyz是讀出的三軸場強值。