
地磁场(Earth’s magnetic field)是源自地球内部的一种天然磁性现象,主要由地球核心的液态外核运动产生的电流所引起,形成了一个类似磁偶极子的磁场分布。这个磁场分布在地球表面及空间中,提供了指南针指向和多种导航系统的基础。
磁力计(Magnetometer)是一种测量磁场强度和方向的传感器,它可以检测到地球磁场在X、Y、Z三个正交坐标轴方向上的分量大小。对于一个固定的地理位置,地磁场矢量可以分解为两个主要组成部分:
-
与当地水平面平行的分量:也被称为水平分量,它在地球表面上呈现出东-西和南-北方向的变化。水平分量可以帮助确定地理方位,例如,指南针上的指针就是在地磁场水平分量的作用下指示北方。
-
与当地水平面垂直的分量:也被称为垂直分量或磁倾角分量,它反映了地磁场线相对于当地水平面的倾斜程度。这个分量有助于确定地磁场的精确方向和强度。
磁性材料(如铁磁物质)在地磁场的作用下会发生磁化,从而感受到磁场力的作用,这也是许多基于地磁场定位和导航技术得以实现的物理基础。现代磁力计广泛应用于航空、航海、地质勘探、地图测绘、智能手机定位等多个领域。
一、ST480MC简介

ST480MC是由深迪半导体(Senodia Technologies)制造的一款高精度三轴磁力计,主要用于测量地球磁场或其他环境磁场在三维空间中的强度和方向。这款磁传感器具有以下关键特性:
-
测量范围:它能测量的磁场强度范围为±48高斯(Gauss),涵盖了地球磁场强度的典型变化范围。
-
分辨率:在X轴和Y轴上,其分辨率为0.15微特斯拉(uT)每最小可寻址位(LSB),而在Z轴上分辨率稍低,为0.25 uT/LSB,确保了较高的测量精度。
-
工作温度范围:能在-40至85摄氏度的宽温环境下稳定工作,适应各种户外和室内条件下的应用需求。
-
通讯方式:支持I²C和SPI两种主流的串行接口,使得它能方便地与各类微控制器(MCU)集成,便于数据读取和配置控制。
-
供电电压:工作电压范围是2.2至3.6伏特,兼容常见的电池供电设备电源电压。
-
16位ADC采样:内置的16位模数转换器(ADC)提供高精度的数据转换能力。
-
内部温度传感器:具备集成的温度传感器,可以实时监测芯片工作环境的温度,有利于补偿因温度变化引起的磁敏感元件输出漂移。
-
应用场景:因其小巧、精准和低功耗的特点,ST480MC常被应用于需要精确磁场测量和方向感知的场合,如电子罗盘、智能手机的指南针功能、平板电脑、手持导航设备以及其他需要磁定向或地磁信号检测的智能硬件产品中。

以下是ST480MC磁力计各引脚的详细说明:
-
A1 - VDD:电源引脚,用于提供2.2至3.6伏特的工作电压。
-
A2 - A0:器件地址引脚,用于在多设备I²C总线系统中区分不同器件,通过连接至高电平或低电平,可以设置不同的7位I²C地址,注意这个引脚不能悬空。
-
A3 - TST:测试引脚,通常在正常工作状态下连接到100kΩ的下拉电阻拉低,用于在特殊测试条件下控制或配置芯片。
-
B1 - VSS:电源地引脚,连接到系统的接地端。
-
B3 - SCL:I²C通信时钟线,通过这个引脚与微控制器的相应SCL引脚相连,实现I²C协议的时钟同步。
-
C1 - VDDIO:数字接口电源引脚,用于为数字接口部分提供电压,同样为2.2至3.6伏特。
-
C2 - NC:未连接(No Connection),在正常使用时不接任何元件。
-
C3 - SDA:I²C通信数据线,与微控制器的SDA引脚连接,用于双向传输数据。
通过以上引脚配置和连接,微控制器可以通过I²C总线与ST480MC磁力计进行通信,获取地磁场在三维空间中的精确测量数据。
1.1、ST480MC 的工作模式

ST480MC磁力计的工作模式可以进一步解释如下:
-
单次测量模式:
在该模式下,ST480MC处于低功耗待机状态,当MCU(微控制器)通过I²C接口发送命令请求一次测量时,ST480MC开始采集XYZ轴的地磁场数据。采集完成后,ST480MC会触发INT(中断)引脚变为高电平,通知MCU数据已经准备好可以读取。在MCU读取完数据并清除中断后,ST480MC将再次回到空闲状态。 -
突发模式(Burst Mode):
在突发模式下,ST480MC会连续不断地进行XYZ轴磁场数据的转换,并将转换结果保存在内部寄存器中。只要数据转换还在进行,INT引脚会维持低电平状态。一旦所有数据转换完毕,INT引脚将变为高电平,表明新的测量结果已经准备好可供读取。此时,MCU可以通过I²C接口一次性读取连续多次转换的结果。 -
唤醒模式:
在唤醒模式下,ST480MC不仅连续监测磁场强度,而且会根据预先设定的阈值来控制INT引脚的状态。只有当任意一轴(或多个轴)的磁场强度变化超过阈值时,INT引脚才会变为高电平,以唤醒MCU或通知MCU发生了显著的磁场变化。在磁场强度没有超出阈值时,INT引脚则维持在低电平状态。这种方式有助于降低系统功耗,仅在必要时才唤醒系统进行数据处理。

大多数磁力计传感器都有类似的寄存器结构,下面是一些常见的寄存器类型和功能:
-
T_STARTUP:这是磁力计启动后的初始化时间,通常指的是磁力计从上电到可以正常工作的准备时间。
-
命令间的时间:在发送不同命令之间需要留有一定的间隔时间,确保磁力计能够正确响应并处理每个命令。
-
器件转换数据时间:
- T_CONVM:单轴(X、Y 或 Z)磁场数据的转换时间,计算公式为
(2 + 2^DIG_FILT) * 2^OSR * 0.064 ms,其中DIG_FILT是数字滤波器级别,OSR是过采样率。 - T_CONVT:温度传感器数据的转换时间,计算公式为
2^OSR2 * 0.192 ms,其中OSR2是温度传感器的过采样率。
- T_CONVM:单轴(X、Y 或 Z)磁场数据的转换时间,计算公式为
-
顺序:
- 发送 单次测量模式命令(SM命令) 到磁力计,使其开始进行一次数据采集。
- 数据采集完成后,发送 读取数据命令(RM命令) 来读取转换好的磁场和温度数据。
-
读取数据的顺序:通常按照
T (温度),X (X轴磁场),Y (Y轴磁场),Z (Z轴磁场)的顺序读取。 -
T_STBY:从IDLE状态到待机状态所需时间,大约为250微秒(us)。
-
T_ACTIVE:从待机状态到激活状态所需时间,大约为8微秒(us)。
-
m*T_CONV:从IDLE状态到数据完全准备好所需要的时间,这个时间取决于
m(测量次数)和单次转换时间T_CONV。如果进行全检测(包括X、Y、Z轴磁场和温度),且m=1,则这个时间为单次测量的总时间,包括转换时间和可能的额外延迟时间。如果进行了多次测量,则需要乘以测量次数m。
总的来说,在操作ST480MC磁力计时,需要合理安排命令发送的时间间隔,并根据所需的转换时间等待数据准备就绪后再进行读取,确保获取准确的数据。
1.2、ST480MC 寄存器介绍
此类传感器设备会有多个寄存器用于配置和读取数据。下面列出了一般磁力计可能包含的部分关键寄存器及其功能:
-
控制寄存器(Control Register):用于设置工作模式(单次测量、突发模式、唤醒模式等)、数字滤波器、采样率、中断使能等。
-
状态寄存器(Status Register):记录设备的状态信息,例如数据就绪标志、中断标志等。
-
数据寄存器(Data Registers):
- 磁力计数据寄存器(Magnetic Data Registers):通常包含三个,分别存储X、Y、Z轴的磁场强度数据。
- 温度数据寄存器(Temperature Data Register):存储温度传感器测量的温度值。
-
配置寄存器(Configuration Register):用于设置地址、采样模式、测量范围、中断阈值等参数。
-
中断控制寄存器(Interrupt Control Register):设置中断类型(如数据就绪、阈值触发等)及其触发条件。
-
中断源寄存器(Interrupt Source Register):记录中断发生的来源,用于查询哪些中断已被触发。
如果需要ST480MC的具体寄存器定义和操作细节,请查阅该产品的官方datasheet或联系Senodia公司获取详细文档。

ST480MC磁力计的寄存器介绍如下:
-
地址0x00:
- [7:2]:RESERVED_LOW,保留位,不可用。
- BIT 15-0:BIST(Built-In Self Test,内置自检)和Z轴增益选择(Zseries Gain Sel)、HALLCONF(霍尔配置)等位。
-
地址0x01:
- INT/TRIGCOMM_MODEWOC:中断/触发通信模式选择位,与无磁(Without Compensation, WOC)有关。
- DIFFEXT_TRIG:差分外部触发位。
- TCMP_EN:温度比较器使能位。
- BURST_SEL (zyxt):突发模式选择位,决定XYZT哪个轴参与突发模式。
- BURST_DATA_RATE:突发模式数据速率选择位。
-
地址0x02:
- OSR2:温度传感器的过采样率。
- RES_XYZ:XYZ轴的分辨率选择位。
- DIG_FILT:数字滤波器设置位。
- OSR:磁场传感器的过采样率。
-
地址0x03:
- SENS_TC_HT:灵敏度温度高阈值设置位。
- SENS_TC_LT:灵敏度温度低阈值设置位。
-
地址0x04-0x06:
- OFFSET_X、OFFSET_Y、OFFSET_Z:分别对应XYZ轴的偏移补偿值。
-
地址0x07:
- WOXY_THRESHOLD:WOXY轴(可能是WX或WY轴,视具体产品定义而定)的阈值。
-
地址0x08:
- WOZ_THRESHOLD:WOZ轴(WZ轴)的阈值。
-
地址0x09:
- WOT_THRESHOLD:WOT(温度)的阈值。
请注意,这些寄存器的具体功能和位布局可能因ST480MC产品的不同版本或厂商文档差异而略有不同,请务必参照官方提供的数据手册以获取最准确的信息。
1.3、ST480MC 命令

以下是ST480MC磁力计的部分命令及其功能描述:
-
Start Burst Mode (SB):命令字节为0001,用于启动突发模式。在该模式下,磁力计会连续采集并存储磁场和温度数据,不需额外命令即可连续读取数据。参数zyxt (1) 表示XYZT四轴是否都参与测量,1表示全部参与。
-
Start Wake-up on Change Mode (SW):命令字节为0010,用于启动唤醒模式。当磁场强度变化超过预设阈值时,磁力计会自动唤醒并准备数据,触发中断。
-
Start Single Measurement Mode (SM):命令字节为0011,用于启动单次测量模式。执行该命令后,磁力计会进行一次磁场和温度测量,完成后进入待机状态,等待读取命令。
-
Read Measurement (RM):命令字节为0100,用于读取之前采集到的磁场和温度数据。参数zyxt (1) 表示读取哪些轴的数据。
-
Read Register (RR):命令字节为0101,用于读取指定寄存器的内容。需要跟随两个字节,分别表示寄存器地址的高位字节和低位字节。
-
Write Register (WR):命令字节为0110,用于向指定寄存器写入数据。后面跟随四个字节,分别是寄存器地址的高位字节和低位字节,以及要写入数据的高位字节和低位字节。
-
Exit Mode (EM):命令字节为1000,用于退出当前工作模式,返回到默认的待机状态。
-
Memory Recall (HR):命令字节为1101,用于从内部非易失性存储器中读取先前存储的配置或校准数据。
-
Memory Store (HS):命令字节为1110,用于将当前配置或校准数据存储到内部非易失性存储器中。
-
Reset (RT):命令字节为1111,用于复位磁力计,使其恢复到初始状态。
请注意,以上命令的详细操作可能需要结合具体的产品手册或数据表进行解读和实施。
二、ST480MC时序介绍
2.1、ST480MC寻址

在I²C总线通信中,ST480MC磁力计的寻址方式如下:
-
设备地址:设备地址由固定位和硬件选择位组成。在ST480MC中,固定位为0x0C,硬件选择位通常通过A0引脚设置,用于在同一个总线上区分多个相同型号的设备。例如,如果A0引脚接地,则设备地址可能是0x0C(忽略最低位的读/写位)。
-
通讯地址:通讯地址除了包含设备地址之外,还包括一个数据传输方向位。在I²C通信中,这一位决定了此次通信是读操作还是写操作。
-
写操作地址:当进行写操作时,最低位为0,因此结合上面的设备地址0x0C,ST480MC的完整写操作地址为0x18。
-
读操作地址:当进行读操作时,最低位为1,因此结合设备地址0x0C,ST480MC的完整读操作地址为0x19。
-
总结一下,在实际I²C通信中,若要对ST480MC进行写操作,主设备会广播0x18作为目的地址,然后发送要写入的数据;若要进行读操作,主设备首先通过写操作把读取的寄存器地址送到0x18,随后切换到0x19地址发起读请求,从设备(ST480MC)会将指定寄存器的数据返回给主设备。
2.2、发送时序
2.2.1、发送命令时序

在与ST480MC磁力计进行通信时,发送命令的时序遵循I²C协议的基本规则,并且每个命令都有其特定的输入和输出字节数。以下是发送命令的基本流程:
-
开始信号(Start Signal):主设备首先发出一个起始信号,标志着一次通信的开始。
-
地址帧:紧接着,主设备发送命令的地址,其中包括命令的七位地址和一位写入(Write)标志位。例如,若要写入命令,则发送地址为0x48。
-
命令字节(Command Byte):主设备接着发送命令字节,该字节包含了控制ST480MC执行特定操作的指令信息。
-
状态字节(State Byte):ST480MC在接收到命令后,首先回传一个状态字节。这个字节包含了多种状态信息,如BURST_MODE、WOC_MODE、SM_MODE、ERROR、SED、RS、D1和D0位。
- MODE bits:标识ST480MC当前的工作模式。
- ERROR bit:当命令被拒绝或检测到无法纠正的错误时,该位被置1。
- SED bit:标记非易失性内存中检测到并纠正的单比特错误。
- RS bit:在硬复位或软复位后,该位被置1以提示主设备ST480MC刚经历了一次复位,读取第一个状态字节后该位将被清除,直到下次复位为止。
- D[1:0] bits:在发送RM命令后,这两个位决定了ST480MC响应的数据字节数。
-
数据交换:对于RR(Read Result)和RM(Read Measurement)这样的读取命令,主设备在发送完命令后,还需通过发送读取信号来读取状态字节和后续的输出数据。输出数据的数量由状态字节中的D[1:0]位决定。
-
速率字节(Rate Byte):在某些情况下,ST480MC可能需要速率字节来设置采样率等参数。速率字节的数据是以补码形式提供的,其中高位字节的bit7被视为符号位。
总之,在与ST480MC通信时,主设备需要严格按照规定的时序和命令格式进行操作,并根据状态字节中的信息做出相应的响应。
2.2.2、读/写 寄存器时序

读/写ST480MC磁力计寄存器时序详细步骤如下:
读取测量数据(Read Measurement, RM)
-
读取Z轴速率数据:
- 主设备发送起始信号(Start)。
- 主设备写入ST480MC的I²C地址(Address[7:1])和写入标志位(Write bit)。
- 主设备写入读取测量命令(RM命令)。
- 主设备发送重复启动信号(Repeated Start)。
- 主设备写入ST480MC的I²C地址和读取标志位(Read bit)。
- ST480MC回应状态字节(State Byte)。
- 主设备接收Z轴速率的高位字节(Z Rate High Byte)。
- 主设备接收Z轴速率的低位字节(Z Rate Low Byte)。
- 主设备发送停止信号(Stop)。
-
读取全部数据(Temperature Rate, RateX, RateY, RateZ):
- 执行上述步骤1-5。
- 接下来,主设备依次接收温度速率的高位和低位字节、X轴速率的高位和低位字节、Y轴速率的高位和低位字节,直至最后接收Z轴速率的高位和低位字节。
- 在接收完所有数据后,主设备发送停止信号。
读取寄存器(Read Register, RR)
- 读取易失性内存数据:
- 主设备发送起始信号。
- 主设备写入ST480MC的I²C地址和写入标志位。
- 主设备写入读取寄存器命令(RR命令)。
- 主设备接着写入要读取的寄存器地址。
- 主设备发送重复启动信号。
- 主设备写入ST480MC的I²C地址和读取标志位。
- ST480MC回应状态字节。
- 主设备接收寄存器数据的高位字节。
- 主设备接收寄存器数据的低位字节。
- 主设备发送停止信号。
以上时序描述了从主设备向ST480MC发送命令和地址,以及从ST480MC接收状态和数据的全过程,符合I²C协议的标准操作流程。
2.2.3、读取测量数据时序

读取ST480MC磁力计测量数据时序详细步骤如下:
读取单一轴(例如Z轴)测量数据时序:
-
开始信号(Start): 主设备发出I²C总线的开始信号,启动一次通信。
-
写地址阶段:
- 地址字节:主设备发送ST480MC的7位I²C地址(Address[7:1]),并设置最低位为0,表示这是一个写操作。
- 写命令字节:主设备接着发送读取测量命令(RM命令)。
-
重启信号(Repeated Start): 主设备发出I²C总线的重启信号,准备进行读操作。
-
读地址阶段:
- 地址字节:主设备再次发送ST480MC的7位I²C地址,这次最低位设置为1,表示这是一个读操作。
-
接收状态字节:
- 状态字节:从设备(ST480MC)返回一个状态字节,包含了当前设备状态信息。
-
读取数据:
- Z轴速率高位字节:主设备接收Z轴测量数据的高位字节。
- Z轴速率低位字节:主设备接收Z轴测量数据的低位字节。
-
结束信号(Stop): 主设备在读取完数据后发出结束信号,终止此次通信。
读取全部测量数据(Temperature Rate, RateX, RateY, RateZ)时序:
遵循上述相同的开始、写命令和重启信号步骤后,主设备将连续接收四组高低字节的数据,按照以下顺序:
- 温度速率的高位字节和低位字节
- X轴速率的高位字节和低位字节
- Y轴速率的高位字节和低位字节
- Z轴速率的高位字节和低位字节
在接收完最后一组数据后,主设备发出结束信号以终止此次通信。
三、ST480MC驱动步骤

在基于ST480MC磁力计开发驱动程序时,可以按照以下步骤进行:
-
初始化ST480MC
- 调用I²C接口初始化函数
iic_init(),配置I²C通信所需的时钟频率、地址、中断等基本参数。 - 调用一个专门的函数发送复位命令到ST480MC,进行软件复位操作。
- 可选地,编写读写寄存器的通用函数,以便在后续步骤中根据不同需求读取或修改ST480MC内部寄存器。
- 调用I²C接口初始化函数
-
编写ST480MC读取N字节函数
- 根据I²C协议的时序要求,编写一个函数,该函数按照
(S + (S_A + W) + A + CMD + A + Sr + (S_A + R) + A + R_D + A/NA + P)的时序来读取N字节数据。 - 其中,
S代表起始信号,S_A+W表示写地址,A代表确认信号,CMD是发送的命令字节,Sr代表重复起始信号,S_A+R表示读地址,R_D是读取的数据,A/NA代表数据确认/非确认信号,P是停止信号。
- 根据I²C协议的时序要求,编写一个函数,该函数按照
-
编写ST480MC读取三轴数据函数
- 调用步骤2编写的读取N字节函数,发送单次测量命令(SM命令),然后发送读取数据命令(RM命令)。
- 按照规定的数据读取顺序读取温度、X轴、Y轴和Z轴的测量数据。
-
编写ST480MC读取温度数据函数
- 类似于步骤3,调用读取N字节函数发送相应的命令读取温度数据。
-
编写ST480MC多次数据获取函数
- 编写一个函数用于连续调用步骤3的读取三轴数据函数,获取多次原始数据。
- 对获取的多组原始数据进行平均处理,以提高数据的准确性和稳定性。
综上所述,开发ST480MC驱动程序时,需根据设备的通信协议和功能特点,逐步构建各个层次的接口函数,从而实现对磁力计的全面控制和数据采集。


四、编程实战
【STM32+cubemx】0029 HAL库开发:HMC5883L磁力计的应用(电子指南针)https://blog.csdn.net/little_grapes/article/details/127895372

磁力计校准的目的主要是消除由于机械安装误差、周围环境磁场干扰以及器件自身固有的偏移等因素造成的测量偏差,确保磁力计能够准确测量地磁场的方向和强度。
校准方法:
-
平面校准法:在校准过程中,首先针对XY轴进行校准。将磁力计放置在水平面上,并让它围绕自身的Z轴做360°匀速旋转,记录每一位置下X和Y轴的读数。通过分析这些读数,找到最大值和最小值,计算出偏移量。
- X轴偏移量:Xoffset = (Xmax + Xmin) / 2
- Y轴偏移量:Yoffset = (Ymax + Ymin) / 2
-
修正读数:将原始读数减去偏移量,得到校准后的相对准确读数。
- 校准后X轴值:XH = X - Xoffset
- 校准后Y轴值:YH = Y - Yoffset
-
计算方位角:利用校准后的XH和YH计算磁力计的方位角α,用于确定设备面对的方向。
-
方位角计算公式:α = arctan(YH / XH) * 180 / π
-
根据XH和YH的正负号,将方位角划分到四个象限中:
- 第一象限:X > 0,Y > 0,0° < α < 90°
- 第二象限:X < 0,Y > 0,90° < α < 180°
- 第三象限:X < 0,Y < 0,180° < α < 270°
- 第四象限:X > 0,Y < 0,270° < α < 360°
-
立体八字校准法和十面校准法则是在平面校准的基础上增加了对Z轴和其他三维方向的校准,以进一步提高磁力计在三维空间中的测量精度。这类方法通常需要在更多的预定姿态下收集数据,并据此计算出三维空间中的偏移和比例因子,进行全面校准。
源码
st480mc.c
#include "./BSP/IIC/myiic.h"
#include "./BSP/ST480MC/st480mc.h"
#include "./SYSTEM/delay/delay.h"
#include "./SYSTEM/usart/usart.h"
#include <math.h>
/**
* @brief 从ST480MC读取N字节数据
* @note ST480MC的命令发送, 也是用该函数实现(不带参数的命令, 也会有一个状态寄存器需要读取)
* @param cmd : 命令
* @param length: 读取长度
* @param buf : 数据存储buf
* @retval 0, 操作成功
* 其他, 操作失败
*/
uint8_t st480mc_read_nbytes(uint8_t cmd, uint8_t length, uint8_t *buf)
{
uint8_t i;
iic_start();
iic_send_byte((ST480MC_ADDR << 1) | 0X00); /* IIC地址最低位是0, 表示写入 */
if (iic_wait_ack())
{
iic_stop();
return 1;
}
iic_send_byte(cmd); /* 写命令 */
iic_wait_ack();
iic_start();
iic_send_byte((ST480MC_ADDR << 1) | 0x01); /* IIC地址最低位是1, 表示读取 */
iic_wait_ack();
for (i = 0; i < length; i++) /* 循环读取 数据 */
{
buf[i] = iic_read_byte(i == (length - 1) ? 0:1);
}
iic_stop();
return 0;
}
/**
* @brief ST480MC写寄存器
* @param reg : 寄存器地址
* @param data : 写入的值
* @retval 0, 操作成功
* 其他, 操作失败
*/
uint8_t st480mc_write_register(uint8_t reg, uint16_t data)
{
uint8_t temp = 0;
iic_start();
iic_send_byte((ST480MC_ADDR << 1) | 0X00); /* IIC地址最低位是0, 表示写入 */
if (iic_wait_ack())
{
iic_stop();
return 1;
}
iic_send_byte(ST480MC_WRITE_REG); /* 发送写寄存器命令 */
iic_wait_ack();
iic_send_byte(data >> 8); /* 发送高字节数据 */
iic_wait_ack();
iic_send_byte(data & 0XFF); /* 发送低字节数据 */
iic_wait_ack();
iic_send_byte(reg << 2); /* 发送寄存器地址(低2位默认是0) */
iic_wait_ack();
iic_start();
iic_send_byte((ST480MC_ADDR << 1) | 0x01); /* IIC地址最低位是1, 表示读取 */
iic_wait_ack();
temp = iic_read_byte(0); /* 读取数据 */
iic_stop();
if (temp & 0X10)
{
return 1;
}
return 0;
}
/**
* @brief ST480MC读寄存器
* @param reg : 寄存器地址
* @retval 读取到的值
* 0XFFFF, 则可能表示错误
*/
uint16_t st480mc_read_register(uint8_t reg)
{
uint8_t buf[3];
uint8_t i;
iic_start();
iic_send_byte((ST480MC_ADDR << 1) | 0X00); /* IIC地址最低位是0, 表示写入 */
iic_wait_ack();
iic_send_byte(ST480MC_READ_REG); /* 发送读寄存器命令 */
iic_wait_ack();
iic_send_byte(reg << 2); /* 发送寄存器地址(低2位默认是0) */
iic_wait_ack();
iic_start();
iic_send_byte((ST480MC_ADDR << 1) | 0x01); /* IIC地址最低位是1, 表示读取 */
iic_wait_ack();
for (i = 0; i < 3; i++) /* 循环读取 数据 */
{
buf[i] = iic_read_byte(1);
}
iic_stop();
if (buf[0] & 0X10)
{
return 0XFFFF;
}
return ((uint16_t)buf[1] << 8) | buf[2]; /* 返回寄存器数据(16位) */
}
/**
* @brief 初始化ST480MC接口
* @param 无
* @retval 0, 成功
* 其他, 异常
*/
uint8_t st480mc_init(void)
{
uint8_t status;
uint8_t res = 0XFF;
uint8_t retry = 10;
iic_init();
while ((retry --) && res) /* 多次尝试, 直到 res == 0, 即等到ST480MC有 ACK反应 */
{
res = st480mc_read_nbytes(ST480MC_RESET, 1, &status); /* 发送复位命令,并读状态 */
delay_ms(20);
}
return res;
}
/**
* @brief ST480MC使用单次模式 读取一次磁力计数据(只读 X,Y,Z轴数据),读取一次至少15ms以上!
* @note 一次性读取XYZT,需要时间为: Tstby + Tactive + m * Tconvm + Tconvt
* Tstby : 从IDLE状态到待机状态时间, 250us
* Tactive : 待机到激活状态时间, 8us
* Tconvm : 单轴转换时间, (2 + 2^DIG_FILT) * 2^OSR * 0.064 ms, 其中 DIG_FILT默认为6, OSR默认为0
* Tconvt : 温度转换时间, 2^OSR2 * 0.192ms, OSR2默认为0
* 因此, 一次性读取XYZT所需总时间为 = 0.25 + 0.008 + 3 * (2 + 2^2) * 2^0 * 0.064 + 2^0 * 0.192 ≈ 4.6ms
* 但是, ST480MC有个IDLE TO DATA READY的时间: Tconv, 这个一般需要16ms, 所以, 一次转换到数据读取, 基本要20ms左右
*
* @param pmagx : X轴磁力计原始值指针
* pmagy : Y轴磁力计原始值指针
* pmagz : Z轴磁力计原始值指针
* @retval 0, 成功
* 其他, 异常
*/
uint8_t st480mc_read_magdata(int16_t *pmagx, int16_t *pmagy, int16_t *pmagz)
{
uint8_t buf[7];
st480mc_read_nbytes(ST480MC_SINGLE_MODE & 0XFE, 1, buf); /* 发送单次测量命令(不测量温度) */
delay_ms(25); /* 延时15ms,基本能争取读取数据 */
st480mc_read_nbytes(ST480MC_READ_DATA & 0XFE, 7, buf); /* 发送读取数据命令(不读取温度) */
if (buf[0] & 0X10) /* 读取数据异常 */
{
return buf[0]; /* 发生错误了 */
}
else
{
*pmagx = (short int)(buf[1] << 8) | buf[2]; /* 组合数据 */
*pmagy = (short int)(buf[3] << 8) | buf[4]; /* 组合数据 */
*pmagz = (short int)(buf[5] << 8) | buf[6]; /* 组合数据 */
}
return 0;
}
/**
* @brief ST480MC使用单次模式 读取一次温度传感器, 读取一次至少15ms以上!
* @note 一次性读取XYZT,需要时间为: Tstby + Tactive + m * Tconvm + Tconvt
* Tstby : 从IDLE状态到待机状态时间, 250us
* Tactive : 待机到激活状态时间, 8us
* Tconvm : 单轴转换时间, (2 + 2^DIG_FILT) * 2^OSR * 0.064 ms, 其中 DIG_FILT默认为6, OSR默认为0
* Tconvt : 温度转换时间, 2^OSR2 * 0.192ms, OSR2默认为0
* 因此, 一次性读取XYZT所需总时间为 = 0.25 + 0.008 + 3 * (2 + 2^6) * 2^0 * 0.064 + 2^0 * 0.192 ≈ 4.6ms
* 但是, ST480MC有个IDLE TO DATA READY的时间: Tconv, 这个一般需要16ms, 所以, 一次转换到数据读取, 基本要20ms左右
*
* @param ptemp : 温度值(℃)指针
* @retval 0, 成功
* 其他, 异常
*/
uint8_t st480mc_read_temperature(float *ptemp)
{
uint8_t buf[9];
st480mc_read_nbytes(ST480MC_SINGLE_MODE, 1, buf); /* 发送单次测量命令(含温度) */
delay_ms(25); /* 延时15ms,基本能争取读取数据 */
st480mc_read_nbytes(ST480MC_READ_DATA, 9, buf); /* 发送读取数据命令(含温度) */
if (buf[0] & 0X10) /* 读取数据异常 */
{
return buf[0]; /* 发生错误了 */
}
else
{
*ptemp = (uint16_t)(buf[1] << 8) | buf[2]; /* 得到温度传感器原始值 */
*ptemp = (*ptemp - 46244) / 45.2f + 25; /* 根据原厂提供的计算公式,换算成℃ */
}
return 0;
}
/**
* @brief ST480MC读取磁力计数据(只读 X,Y,Z轴数据),读times次取平均
* @note 本函数耗时 ≈ 15 * times ms
*
* @param pmagx : X轴磁力计原始值指针
* pmagy : Y轴磁力计原始值指针
* pmagz : Z轴磁力计原始值指针
* times : 读取多少次取平均
* @retval 0, 成功
* 其他, 异常
*/
uint8_t st480mc_read_magdata_average(int16_t *pmagx, int16_t *pmagy, int16_t *pmagz, uint8_t times)
{
uint8_t i = 0;
uint8_t error_cnt = 0;
int32_t magx = 0;
int32_t magy = 0;
int32_t magz = 0;
while (i < times) /* 连续读取times次 */
{
if (st480mc_read_magdata(pmagx, pmagy, pmagz) == 0) /* 读取数据是否正常? */
{
magx += *pmagx; /* 累加 */
magy += *pmagy;
magz += *pmagz;
i++;
error_cnt = 0;
}
else
{
error_cnt++; /* 错误计数器 */
delay_ms(10);
if (error_cnt > 100) /* 连续100次出错, 直接返回异常 */
{
return 0XFF;
}
}
}
*pmagx = magx / times; /* 取平均值 */
*pmagy = magy / times; /* 取平均值 */
*pmagz = magz / times; /* 取平均值 */
return 0;
}
st480mc.h
#ifndef __ST480MC_H
#define __ST480MC_H
#include "./SYSTEM/sys/sys.h"
#define ST480MC_ADDR 0X0C /* ST480MC IIC器件地址(A0 = 0时) */
#define ST480MC_RESET 0XF0 /* ST480MC 复位命令 */
#define ST480MC_READ_REG 0X50 /* ST480MC 读寄存器 */
#define ST480MC_WRITE_REG 0X60 /* ST480MC 写寄存器 */
#define ST480MC_READ_DATA 0X4F /* ST480MC 读取全部数据(zxyt) */
#define ST480MC_BURST_MODE 0X1F /* ST480MC 突发模式读取数据(zxyt) */
#define ST480MC_SINGLE_MODE 0X3F /* ST480MC 单次测量读取数据(zxyt) */
#define ST480MC_SENS_XY 667 /* X,Y 轴灵敏度; 单位: LSB/Gauss */
#define ST480MC_SENS_Z 400 /* Z 轴灵敏度 单位: LSB/Gauss */
uint8_t st480mc_init(void); /* 初始化 */
uint8_t st480mc_read_nbytes(uint8_t cmd, uint8_t length, uint8_t *buf); /* 读N字节,可以发送命令 */
uint16_t st480mc_read_register(uint8_t reg); /* 读寄存器 */
uint8_t st480mc_write_register(uint8_t reg, uint16_t data); /* 写寄存器 */
uint8_t st480mc_read_magdata(int16_t *pmagx, int16_t *pmagy, int16_t *pmagz); /* 读取磁力原始数据(单次) */
uint8_t st480mc_read_temperature(float *ptemp); /* 读取温度传感器值(单次,℃) */
uint8_t st480mc_read_magdata_average(int16_t *pmagx, int16_t *pmagy, int16_t *pmagz, uint8_t times); /* 读取磁力原始数据(平均值) */
#endif
main.c
#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./BSP/LED/led.h"
#include "./BSP/LCD/lcd.h"
#include "./USMART/usmart.h"
#include "./BSP/KEY/key.h"
#include "./BSP/24CXX/24cxx.h"
#include "./BSP/ST480MC/st480mc.h"
#include <math.h>
/* 磁力计平面校准参数
* 校准原理请参考(方法2):http://blog.sina.com.cn/s/blog_402c071e0102v8ie.html
*/
int16_t g_magx_offset = 0; /* x轴补偿值 */
int16_t g_magy_offset = 0; /* y轴补偿值 */
/**
* @brief 显示磁力计原始数据
* @param x, y : 坐标
* @param title: 标题
* @param val : 值
* @retval 无
*/
void user_show_mag(uint16_t x, uint16_t y, char * title, int16_t val)
{
char buf[20];
sprintf(buf,"%s%d", title, val); /* 格式化输出 */
lcd_fill(x, y, x + 160, y + 16, WHITE); /* 清除上次数据(最多显示20个字符,20*8=160) */
lcd_show_string(x, y, 160, 16, 16, buf, BLUE); /* 显示字符串 */
}
/**
* @brief 显示方位角
* @param x, y : 坐标
* @param angle: 角度
* @retval 无
*/
void user_show_angle(uint16_t x, uint16_t y, float angle)
{
char buf[20];
sprintf(buf,"Angle:%3.1fC", angle); /* 格式化输出 */
lcd_fill(x, y, x + 160, y + 16, WHITE); /* 清除上次数据(最多显示20个字符,20*8=160) */
lcd_show_string(x, y, 160, 16, 16, buf, BLUE); /* 显示字符串 */
}
/**
* @brief 显示温度
* @param x, y : 坐标
* @param temp : 温度
* @retval 无
*/
void user_show_temprate(uint16_t x, uint16_t y, float temp)
{
char buf[20];
sprintf(buf,"Temp:%2.1fC", temp); /* 格式化输出 */
lcd_fill(x, y, x + 160, y + 16, WHITE); /* 清除上次数据(最多显示20个字符,20*8=160) */
lcd_show_string(x, y, 160, 16, 16, buf, BLUE); /* 显示字符串 */
}
/**
* @brief 罗盘(磁力计)校准函数
* @note 这里我们使用最简单的平面校准方法.
* 进入此函数后,请水平转动开发板至少一周(360°),转动完成后, 按WKUP键退出!
* @param 无
* @retval 无
*/
void compass_calibration(void)
{
int16_t x_min = 0;
int16_t x_max = 0;
int16_t y_min = 0;
int16_t y_max = 0;
int16_t magx, magy, magz;
uint8_t res;
uint8_t key = 0;
lcd_clear(WHITE);
lcd_show_string(10, 90, 240, 16, 16, "Compass Calibration", RED);
lcd_show_string(10, 110, 240, 16, 16, "Pls rotate horiz one cycle!", RED);
lcd_show_string(10, 130, 240, 16, 16, "If done, press WKUP key!", RED);
while (1)
{
key = key_scan(0);
if (key == WKUP_PRES) /* 结束校准 */
{
break;
}
res = st480mc_read_magdata(&magx, &magy, &magz); /* 读取数据 */
if (res == 0)
{
x_max = x_max < magx ? magx : x_max; /* 记录x最大值 */
x_min = x_min > magx ? magx : x_min; /* 记录x最小值 */
y_max = y_max < magy ? magy : y_max; /* 记录y最大值 */
y_min = y_min > magy ? magy : y_min; /* 记录y最小值 */
}
LED0_TOGGLE(); /* LED0闪烁,提示程序运行 */
}
g_magx_offset = (x_max + x_min) / 2; /* X轴偏移量 */
g_magy_offset = (y_max + y_min) / 2; /* Y轴偏移量 */
/* 串口打印水平校准相关参数 */
printf("x_min:%d\r\n", x_min);
printf("x_max:%d\r\n", x_max);
printf("y_min:%d\r\n", y_min);
printf("y_max:%d\r\n", y_max);
printf("g_magx_offset:%d\r\n", g_magx_offset);
printf("g_magy_offset:%d\r\n", g_magy_offset);
}
/**
* @brief 罗盘获取角度
* @note 获取当前罗盘的角度(地磁角度)
* @param 无
* @retval 角度
*/
float compass_get_angle(void)
{
float angle;
int16_t magx, magy, magz;
st480mc_read_magdata_average(&magx, &magy, &magz, 10); /* 读取原始数据, 10次取平均 */
magx = (magx - g_magx_offset) ; /* 根据校准参数, 计算新的输出 */
magy = (magy - g_magy_offset) ; /* 根据校准参数, 计算新的输出 */
/* 根据不同的象限情况, 进行方位角换算 */
if ((magx > 0) && (magy > 0))
{
angle = (atan((double)magy / magx) * 180) / 3.14159f;
}
else if ((magx > 0) && (magy < 0))
{
angle = 360 + (atan((double)magy / magx) * 180) / 3.14159f;
}
else if ((magx == 0) && (magy > 0))
{
angle = 90;
}
else if ((magx == 0) && (magy < 0))
{
angle = 270;
}
else if (magx < 0)
{
angle = 180 + (atan((double)magy / magx) * 180) / 3.14159f;
}
if (angle > 360) angle = 360; /* 限定方位角范围 */
if (angle < 0) angle = 0; /* 限定方位角范围 */
return angle;
}
int main(void)
{
uint8_t t = 0;
uint8_t key;
float angle;
float temperature;
int16_t magx, magy, magz;
HAL_Init(); /* 初始化HAL库 */
sys_stm32_clock_init(336, 8, 2, 7); /* 设置时钟,168Mhz */
delay_init(168); /* 延时初始化 */
usart_init(115200); /* 串口初始化为115200 */
usmart_dev.init(84); /* 初始化USMART */
led_init(); /* 初始化LED */
key_init(); /* 初始化按键 */
lcd_init(); /* 初始化LCD */
while (st480mc_init()) /* ST480MC初始化 */
{
lcd_show_string(30, 110, 200, 16, 16, "ST480MC Error", RED);
delay_ms(200);
lcd_fill(30, 110, 239, 130 + 16, WHITE);
delay_ms(200);
}
RST:
lcd_show_string(30, 50, 200, 16, 16, "STM32", RED);
lcd_show_string(30, 70, 200, 16, 16, "ST480MC TEST", RED);
lcd_show_string(30, 90, 200, 16, 16, "ATOM@ALIENTEK", RED);
lcd_show_string(30, 110, 200, 16, 16, "KEY0 to calibration", RED);
while (1)
{
key = key_scan(0);
if (key == KEY0_PRES) /* KEY0 按下 ,执行校准 */
{
compass_calibration(); /* 校准函数 */
lcd_clear(WHITE); /* 清屏 */
goto RST; /* 校准完后,跳到RST, 重新显示提示信息 */
}
delay_ms(10);
t++;
if (t == 20) /* 0.2秒左右更新一次温度/磁力计原始值 */
{
angle = compass_get_angle(); /* 执行一次约150ms */
user_show_angle(30, 130, angle); /* 显示角度 */
st480mc_read_temperature(&temperature); /* 读取温湿度值 */
user_show_temprate(30, 150, temperature); /* 显示温度 */
st480mc_read_magdata(&magx, &magy, &magz); /* 读取原始数据 */
user_show_mag(30, 170, "MagX:", magx); /* 显示magx */
user_show_mag(30, 190, "MagY:", magy); /* 显示magy */
user_show_mag(30, 210, "MagZ:", magz); /* 显示magz */
t = 0;
LED0_TOGGLE(); /* LED0闪烁 */
}
}
}



五、总结





2万+

被折叠的 条评论
为什么被折叠?



