zoukankan      html  css  js  c++  java
  • HMC5883L地磁传感器驱动

       霍尼韦尔 HMC5883L 是一种表面贴装的高集成模块,并带有数字接口的弱磁传感器芯片,应用于低成本罗盘和磁场检测领域。HMC5883L 包括最先进的高分辨率 HMC118X 系列磁阻传感器,并附带霍尼韦尔专利的集成电路包括放大器、自动消磁驱动器、偏差校准、能使罗盘精度控制在 1°~2°的 12 位模数转换器.简易的 I2C 系列总线接口。HMC5883L 是采用无铅表面封装技术,带有 16 引脚,尺寸为 3.0X3.0X0.9mm。HMC5883L 的所应用领域有手机、笔记本电脑、消费类电子、汽车导航系统和个人导航系统

       HMC5883主要有接口是IIC,同时提供一个数据中断引脚,一般而言,电路设计如下


      该传感器芯片的驱动并不复杂,主要是设置几个寄存器,如下所示

    //HMC5883寄存器定义
    //寄存器地址定义
    #define HMC_CONFIG_A_REG	0X00	//配置寄存器A
    //bit0-bit1 xyz是否使用偏压,默认为0正常配置
    //bit2-bit4 数据输出速率, 110为最大75HZ 100为15HZ 最小000 0.75HZ
    //bit5-bit5每次采样平均数 11为8次 00为一次
    
    #define HMC_CONFIG_B_REG	0X01	//配置寄存器B
    //bit7-bit5磁场增益 数据越大,增益越小 默认001
    
    #define HMC_MODE_REG		0X02	//模式设置寄存器
    //bit0-bit1 模式设置 00为连续测量 01为单一测量
    
    #define HMC_XMSB_REG		0X03	//X输出结果
    #define HMC_XLSB_REG		0X04
    
    #define HMC_ZMSB_REG		0X05	//Z输出结果
    #define HMC_ZLSB_REG		0X06
    
    #define HMC_YMSB_REG		0X07	//Y输出结果
    #define HMC_YLSB_REG		0X08
    
    #define HMC_STATUS_REG		0X09	//只读的状态
    //bit1 数据更新时该位自动锁存,等待用户读取,读取到一半的时候防止数据改变
    //bit0 数据已经准备好等待读取了,DRDY引脚也能用
    
    #define HMC_CHEAK_A_REG		0X0A	//三个识别寄存器,用于检测芯片完整性
    #define HMC_CHEAK_B_REG		0X0B
    #define HMC_CHEAK_C_REG		0X0C
    
    #define HMC_CHECKA_VALUE	0x48	//三个识别寄存器的默认值
    #define HMC_CHECKB_VALUE	0x34
    #define HMC_CHECKC_VALUE	0x33

    建议选择最大速率,八倍采样输出,同时该传感器支持读写地址指针自动变化,但是针对随机读取的驱动而言,该特性不重要

    另外,一般而言,地址为0x3c,驱动代码如下

    #include "hmc5883.h"
    #include "math.h"
    
    struct HMCVALUE hmcValue;
    u8 dataReady = 0;
    
    
    //IO方向设置
    #define HMC_SDA_IN()  {GPIOC->CRH&=0XFFFFFFF0;GPIOC->CRH|=8;}
    #define HMC_SDA_OUT() {GPIOC->CRH&=0XFFFFFFF0;GPIOC->CRH|=3;}
    
    //IO操作函数	 
    #define HMC_SCL    PCout(9) //SCL
    #define HMC_SDA    PCout(8) //SDA	 
    #define HMC_READ_SDA   PCin(8)  //输入SDA 
    
    #define HMC_DRDY	PCin(7)
    
    /**************************HMC5883 IIC驱动函数*********************************/
    
    static void Hmc5883IOInit(void)
    {
    	GPIO_InitTypeDef GPIO_InitStructure;
        RCC_APB2PeriphClockCmd(	RCC_APB2Periph_GPIOC, ENABLE );	
        
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8|GPIO_Pin_9;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ;   //推挽输出
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
        GPIO_Init(GPIOC, &GPIO_InitStructure);
        
    	//PC7 drdy引脚 
    	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING ;   //浮空输入
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
        GPIO_Init(GPIOC, &GPIO_InitStructure);
    	
        HMC_SCL = 1;
        HMC_SDA = 1;
    }
    
    
    
    //发送IIC起始信号
    static void ComStart(void)
    {
    	HMC_SDA_OUT();     //sda线输出
        HMC_SDA=1;	  	  
        HMC_SCL=1;
        DelayUs(5);
        HMC_SDA=0;//START:when CLK is high,DATA change form high to low 
        DelayUs(5);
        HMC_SCL=0;//钳住I2C总线,准备发送或接收数据
    }
    //发送IIC停止信号
    static void ComStop(void)
    {
    	HMC_SDA_OUT();//sda线输出
        HMC_SDA=0;//STOP:when CLK is high DATA change form low to high
        HMC_SCL=1;
        DelayUs(5);
        HMC_SDA=1;//发送I2C总线结束信号
        DelayUs(5);		
    }
    //等待ACK,为1代表无ACK 为0代表等到了ACK
    static u8 ComWaitAck(void)
    {
    	u8 waitTime = 0;
    	HMC_SDA_OUT();//sda线输出
    	HMC_SDA = 1;
    	DelayUs(5);
        HMC_SDA_IN();      //SDA设置为输入
    	HMC_SCL=1;
    	DelayUs(5);
    	while(HMC_READ_SDA)
    	{
    		waitTime++;
    		DelayUs(1);
    		if(waitTime > HMC_ACK_WAIT_TIME)
    		{
    			ComStop();
    			return 1;
    		}
    	}
    	HMC_SCL = 0;
    	return 0;
    	
    }
    
    static void ComSendAck(void)
    {
    	HMC_SCL = 0;
    	HMC_SDA_OUT();
        HMC_SDA = 0;
    	DelayUs(2);
        HMC_SCL = 1;
        DelayUs(5);
        HMC_SCL = 0;
        DelayUs(5);
    }
    
    static void ComSendNoAck(void)
    {
    	HMC_SCL = 0;
    	HMC_SDA_OUT();
        HMC_SDA = 1;
    	DelayUs(2);
        HMC_SCL = 1;
        DelayUs(5);
        HMC_SCL = 0;
        DelayUs(5);
    }
    //返回0 写入收到ACK 返回1写入未收到ACK
    static u8 ComSendByte(u8 byte)
    {
    	u8 t;   
        HMC_SDA_OUT(); 	
        for(t=0;t<8;t++)
        {              
            HMC_SDA=(byte&0x80)>>7;
            byte<<=1; 	   
            HMC_SCL=1;
            DelayUs(5); 
            HMC_SCL=0;	
            DelayUs(5);
        }	 
        return ComWaitAck();
    }
    
    static void ComReadByte(u8* byte)
    {
    	u8 i,receive=0;
        HMC_SDA_IN();//SDA设置为输入
        for(i=0;i<8;i++ )
        {
            receive <<= 1;
            HMC_SCL=1; 
            DelayUs(5);
            if(HMC_READ_SDA)receive++;
            HMC_SCL=0; 
            DelayUs(5); 
        }					  
        *byte = receive;
    }
    
    /**************************HMC5883 IIC驱动函数*********************************/
    
    
    //向HMC写入一个字节数据,失败返回1 成功返回0
    u8 Hmc5883WriteReg(u8 regValue,u8 setValue)
    {
    	u8 res;
        ComStart();                 	//起始信号
        res = ComSendByte(HMC_ADDR);    //发送设备地址+写信号
    	if(res)
    	{
    		#ifdef HMC_DEBUG
    		printf("file=%s,func=%s,line=%d
    ",__FILE__,__FUNCTION__,__LINE__);
    		#endif
    		return res;
    	}
        res = ComSendByte(regValue);    //内部寄存器地址
    	if(res)
    	{
    		#ifdef HMC_DEBUG
    		printf("file=%s,func=%s,line=%d
    ",__FILE__,__FUNCTION__,__LINE__);
    		#endif
    		return res;
    	}
        res = ComSendByte(setValue);    //内部寄存器数据
    	if(res)
    	{
    		#ifdef HMC_DEBUG
    		printf("file=%s,func=%s,line=%d
    ",__FILE__,__FUNCTION__,__LINE__);
    		#endif
    		return res;
    	}
        ComStop();                   	//发送停止信号
    	return res;
    }
    
    //**************************************
    //从I2C设备读取一个字节数据
    //**************************************
    u8 Hmc5883ReadReg(u8 regAddr,u8* readValue)
    {
        u8 res;
        ComStart();                 		//起始信号
        res = ComSendByte(HMC_ADDR);    	//发送设备地址+写信号
    	if(res)
    	{
    		#ifdef HMC_DEBUG
    		printf("file=%s,func=%s,line=%d
    ",__FILE__,__FUNCTION__,__LINE__);
    		#endif
    		return res;
    	}
        res = ComSendByte(regAddr);     	//发送存储单元地址,从0开始	
    	if(res)
    	{
    		#ifdef HMC_DEBUG
    		printf("file=%s,func=%s,line=%d
    ",__FILE__,__FUNCTION__,__LINE__);
    		#endif
    		return res;
    	}
        ComStart();                 		//起始信号
        res = ComSendByte(HMC_ADDR+1);  	//发送设备地址+读信号
    	if(res)
    	{
    		#ifdef HMC_DEBUG
    		printf("file=%s,func=%s,line=%d
    ",__FILE__,__FUNCTION__,__LINE__);
    		#endif
    		return res;
    	}
        ComReadByte(readValue);     		//读出寄存器数据
        ComSendNoAck();               		//发送非应答信号
        ComStop();                  		//停止信号
        return res;
    }
    
    
    static void HmcDefaultConfig(void)
    {
    	Hmc5883WriteReg(HMC_CONFIG_A_REG,HMC_DEFAULT_CONFIGA_VALUE);
    	Hmc5883WriteReg(HMC_CONFIG_B_REG,HMC_DEFAULT_CONFIGB_VALUE);
    	Hmc5883WriteReg(HMC_MODE_REG,HMC_DEFAULT_MODE_VALUE);
    }
    
    //配置HMC drdy引脚中断
    void HmcDrdyPinIntInit(void)
    {
    	EXTI_InitTypeDef EXTI_InitStructure;
        NVIC_InitTypeDef NVIC_InitStructure;
    	
    	//GPIOC.7 中断线以及中断初始化配置   下降沿触发
        GPIO_EXTILineConfig(GPIO_PortSourceGPIOC,GPIO_PinSource7);
        
        EXTI_InitStructure.EXTI_Line=EXTI_Line7;
        EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;	
        EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;	//下降沿中断
        EXTI_InitStructure.EXTI_LineCmd = ENABLE;
        EXTI_Init(&EXTI_InitStructure);	 	//根据EXTI_InitStruct中指定的参数初始化外设EXTI寄存器
    	
    	NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn;			//使能所在的外部中断通道
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = HMC_DRDY_PreemptionPriority;	//抢占优先级 
        NVIC_InitStructure.NVIC_IRQChannelSubPriority = HMC_DRDY_SubPriority;					//子优先级
        NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;								//使能外部中断通道
        NVIC_Init(&NVIC_InitStructure);
    }
    
    //对应的外部中断处理函数
    void EXTI9_5_IRQHandler()
    {
    	if(EXTI_GetFlagStatus(EXTI_Line7) == SET)
    	{
    		EXTI_ClearFlag(EXTI_Line7);
    		dataReady = 1;
    	}
    }
    //失败返回1,初始化通过之后基本通讯就没有问题了就可以不再判定通讯结果了
    u8 HmcInit(void)
    {
        u8 hmcCheckValue = 0x00;
    	u8 res = 0;
        Hmc5883IOInit();	//接口初始化
    	while(HMC_DRDY == 1);//等待数据引脚正常
    	HmcDrdyPinIntInit();//配置中断
        DelayMs(100);
    	//验证A识别
        res = Hmc5883ReadReg(HMC_CHEAK_A_REG,&hmcCheckValue);
    	if(res)
    	{
    		#ifdef HMC_DEBUG
    		printf("file=%s,func=%s,line=%d
    ",__FILE__,__FUNCTION__,__LINE__);
    		#endif
    	}
        if(hmcCheckValue != HMC_CHECKA_VALUE)	//自检通过
        {
            return 1;
        }
    	//验证B识别
    	res = Hmc5883ReadReg(HMC_CHEAK_B_REG,&hmcCheckValue);
    	if(res)
    	{
    		#ifdef HMC_DEBUG
    		printf("file=%s,func=%s,line=%d
    ",__FILE__,__FUNCTION__,__LINE__);
    		#endif
    	}
        if(hmcCheckValue != HMC_CHECKB_VALUE)	//自检通过
        {
            return 1;
        }
    	//验证C识别
    	res = Hmc5883ReadReg(HMC_CHEAK_C_REG,&hmcCheckValue);
    	if(res)
    	{
    		#ifdef HMC_DEBUG
    		printf("file=%s,func=%s,line=%d
    ",__FILE__,__FUNCTION__,__LINE__);
    		#endif
    	}
        if(hmcCheckValue != HMC_CHECKC_VALUE)	//自检通过
        {
            return 1;
        }
    	HmcDefaultConfig();
    	//全部验证通过
        return 0;
    }
    
    
    
    void Hmc5883Reflush(void)
    {
        u16 xValue = 0x00,yValue = 0x00,zValue = 0x00;
    	u8 temp;
    	Hmc5883ReadReg(HMC_XMSB_REG,&temp);
        xValue |= temp;
    	xValue <<=8;
    	Hmc5883ReadReg(HMC_XLSB_REG,&temp);
    	xValue |=  temp;
    	
    	Hmc5883ReadReg(HMC_ZMSB_REG,&temp);
    	zValue |=  temp;
    	zValue <<=8;
    	Hmc5883ReadReg(HMC_ZLSB_REG,&temp);
    	zValue |=  temp;
    	
    	Hmc5883ReadReg(HMC_YMSB_REG,&temp);
    	yValue |=  temp;
    	yValue <<=8;
    	Hmc5883ReadReg(HMC_YLSB_REG,&temp);
    	yValue |=  temp;
    	
    	hmcValue.hmcXvalue = (s16)xValue;
    	hmcValue.hmcYvalue = (s16)yValue;
    	hmcValue.hmcZvalue = (s16)zValue;
    	
    	hmcValue.hmcAxis = atan2((double)hmcValue.hmcYvalue,(double)hmcValue.hmcXvalue) * (180 / 3.14159265) + 180; // angle in degrees
    	#ifdef HMC_DEBUG
    	printf("x = %d 	 y = %d 	 z = %d 	 axis = %f
    ",xValue,yValue,zValue,hmcValue.hmcAxis);
    	#endif
    }
    
    
    

    头文件定义如下(中断有优先级请自行定义)

    #ifndef __HMC5883_H_
    #define __HMC5883_H_
    
    #include "common.h"
    #include "stm32f10x.h"
    #include "delay.h"
    #include "ioremap.h"
    #include "uart.h"
    
    #define HMC_DEBUG	1	//是否输出调试信息
    
    //没有主机改变地址的话地址是可以自动更新的,不过该驱动没有用这个模式
    
    
    //HMC5883寄存器定义
    //寄存器地址定义
    #define HMC_CONFIG_A_REG	0X00	//配置寄存器A
    //bit0-bit1 xyz是否使用偏压,默认为0正常配置
    //bit2-bit4 数据输出速率, 110为最大75HZ 100为15HZ 最小000 0.75HZ
    //bit5-bit5每次采样平均数 11为8次 00为一次
    
    #define HMC_CONFIG_B_REG	0X01	//配置寄存器B
    //bit7-bit5磁场增益 数据越大,增益越小 默认001
    
    #define HMC_MODE_REG		0X02	//模式设置寄存器
    //bit0-bit1 模式设置 00为连续测量 01为单一测量
    
    #define HMC_XMSB_REG		0X03	//X输出结果
    #define HMC_XLSB_REG		0X04
    
    #define HMC_ZMSB_REG		0X05	//Z输出结果
    #define HMC_ZLSB_REG		0X06
    
    #define HMC_YMSB_REG		0X07	//Y输出结果
    #define HMC_YLSB_REG		0X08
    
    #define HMC_STATUS_REG		0X09	//只读的状态
    //bit1 数据更新时该位自动锁存,等待用户读取,读取到一半的时候防止数据改变
    //bit0 数据已经准备好等待读取了,DRDY引脚也能用
    
    #define HMC_CHEAK_A_REG		0X0A	//三个识别寄存器,用于检测芯片完整性
    #define HMC_CHEAK_B_REG		0X0B
    #define HMC_CHEAK_C_REG		0X0C
    
    #define HMC_CHECKA_VALUE	0x48	//三个识别寄存器的默认值
    #define HMC_CHECKB_VALUE	0x34
    #define HMC_CHECKC_VALUE	0x33
    
    
    //HMC5883地址定义
    #define HMC_ADDR          0X3C          //写地址,读地址+1
    
    
    //HMC5883 初始化宏定义
    #define HMC_DEFAULT_CONFIGA_VALUE		0x78	 //75hz 8倍采样 正常配置
    #define HMC_DEFAULT_CONFIGB_VALUE		0x00	 //+-0.88GA增益
    #define HMC_DEFAULT_MODE_VALUE			0x00     //连续测量模式
    
    
    #define HMC_ACK_WAIT_TIME	200		//iic通讯时的ack等待时间
    
    //HMC5883驱动结构体定义
    typedef struct HMCVALUE 
    {
    	s16 hmcXvalue;
    	s16 hmcYvalue;
    	s16 hmcZvalue;
    	double hmcAxis;
    }HMCVALUE;
    
    
    
    u8 HmcInit(void);
    
    u8 Hmc5883WriteReg(u8 regValue,u8 setValue);
    
    u8 Hmc5883ReadReg(u8 regAddr,u8* readValue);
    
    void Hmc5883Reflush(void);
    
    
    
    extern struct HMCVALUE hmcValue;
    extern u8 dataReady;
    
    
    #endif
    
    
    
    
    
    
    

    引脚连接电路如下


  • 相关阅读:
    Matlab 绘制三维立体图(以地质异常体为例)
    Azure DevOps的variable group实现array和hashtable参数的传递
    Azure DevOps 利用rest api设置variable group
    Azure AADSTS7000215 其中一种问题的解决
    Power BI 实现实时更新Streaming Dataset
    AAD Service Principal获取azure user list (Microsoft Graph API)
    Matlab 沿三维任意方向切割CT图的仿真计算
    Azure Powershell script检测登陆并部署ARM Template
    Azure KeyVault设置策略和自动化添加secrets键值对
    Azure登陆的两种常见方式(user 和 service principal登陆)
  • 原文地址:https://www.cnblogs.com/dengxiaojun/p/4279443.html
Copyright © 2011-2022 走看看