OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 2790|回复: 0

《STM32F407 探索者开发指南》第四十四章 磁力计实验

[复制链接]

1140

主题

1152

帖子

2

精华

超级版主

Rank: 8Rank: 8

积分
4895
金钱
4895
注册时间
2019-5-8
在线时间
1248 小时
发表于 2023-8-25 14:34:54 | 显示全部楼层 |阅读模式
本帖最后由 正点原子运营 于 2023-8-25 14:34 编辑

第四十四章 磁力计实验
1)实验平台:正点原子探索者STM32F407开发板

2) 章节摘自【正点原子】STM32F407开发指南 V1.1

3)购买链接:https://detail.tmall.com/item.htm?id=609294673401

4)全套实验源码+手册+视频下载地址:http://www.openedv.com/docs/boards/stm32/zdyz_stm32f407_explorerV3.html

5)正点原子官方B站:https://space.bilibili.com/394620890

6)STM32技术交流QQ群:151941872

155537c2odj87vz1z9vj6l.jpg

155537nfqovl2gg9faaol9.png

探索者STM32F407开发板自带了ST480MC传感器。本章,我们将介绍如何使用磁力计ST480MC,将读取到的XYZ原始数据显示到LCD,并将XY数据转换为XY平面的方位角(角度范围0~360°,正北方向为0°)、将读取到的T转换为温度显示到LCD。本章分为如下几个小节:
本章分为如下几个小节:
44.1 ST480MC介绍
44.2 硬件设计
44.3 程序设计
44.4 下载验证

44.1 ST480MC介绍
44.1.1 ST480MC简介
ST480MC是一款由Sendia公司生产的三轴电子磁力计,内部进行信号处理,通过IIC接口进行输出。ST480MC提供的输出信号与它在XYZ方向上测量的磁场成比例,如果需要消除比例因子,需要用户自行校准。ST480MC主要特点如下:
1)测量范围:最大±48高斯
2)高分辨率:0.15μT/LSB(X/Y轴),0.25μT/LSB(Z轴)
3)高灵敏度:X/Y轴灵敏度:667 LSBs/Gauss,Z轴灵敏度:400 LSBs/Gauss
4)工作温度范围:-40~+85℃
5)供电电压:2.2~3.6V
6)16位ADC采样
7)内部带有温度传感器
8)支持中断

ST480MC传感器的检测轴如图44.1.1.1所示:                                 
image001.png
图44.1.1.1 ST480MC检测轴与方向

在ST480MC传感器,通过MUX选择器选择采集的是X、Y、Z的数据,然后通过16位ADC获取X、Y、Z的数据,同样,传感器的温度也是通过ADC采集,采集到的数据通过IIC接口进行输出。ST480MC内部也有供给用户读写的EEPROM区域,在本实验中有读写ST480MC内部EEPROM的接口,但是没有用到,感兴趣的可以自行研究。ST480MC内部结构图如图44.1.1.2所示:     
image003.png
图44.1.1.2 ST480MC内部结构图

图44.1.1.2中SDA和SCLK是连接MCU的IIC接口,ST480MC的8pin WLCSP封装只支持IIC协议,QFN封装支持IIC/SPI协议,这里我们主要介绍使用IIC驱动ST480MC。

每一个IIC设备都有自己的设备地址,ST480MC也不例外,ST480MC的设备地址是包括不可编程部分和可编程部分,可编程部分是根据上图的硬件引脚A0所决定。设备地址最后一位用于设置数据的传输方向,即读操作/写操作,0是写操作,1是读操作,具体格式如下图44.1.1.4所示:     
image005.png
图44.1.1.4 ST480MC设备地址格式图

本实验将A0接地,那么设备地址(不包含R/W位)为0x0C,读设备地址(R/W位:1)为0x19,写设备地址(R/W位:0)为0x18。

44.1.2 ST480MC工作模式简介
接下来我们分别ST480MC三种工作模式:单次测量模式、突发模式、唤醒模式。

1. 单次测量模式
当ST480MC设置为该模式(或第一次启动)时,ST480MC进入空闲状态,等待主机发出命令执行特定采集。在接收到主机的查询命令后,ST480MC进行数据采集,然后返回ST480MC的状态和采集的数据,最后返回睡眠模式,直到下次主机查询。

2. 突发模式
当ST480MC设置为突发模式后,就会不断转换XYZT数据,转换完成后INT引脚为高电平,如果还没有转换完成,INT引脚则为低电平(必须在数据转换完成后才能读取到数据)。

3. 唤醒模式
当ST480MC设置为唤醒模式后,在超过设定的阈值后,INT引脚为高电平,如果没有超过设定的阈值,INT引脚为低电平。

44.1.3 ST480MC寄存器简介
在这里简单介绍一下ST480MC比较重要的命令。

l  命令列表
ST480MC只监听一组命令,除复位命令外,所有命令都会生成一个可读取的状态字节,描述如下表所示:
image007.png
表44.1.3.1 ST480MC命令列表

所有模式启动命令(SB/SW/SM)中的参数是半个字节,指定传感器按顺序执行的转换《zyxt》,在后面的程序中,我们只使用了SM命令。如果要测量zyxt数据,就得把对应的位置1,即zyxt为1111。假如说不需要某一项的数据,则将对应的位清零即可。

l  状态字节
状态字节是ST480MC响应主机发出的命令而发送的第一个字节,各位描述如下图所示:
image009.png
图44.1.3.1 ST480MC状态字节

1)3个MODE位:MODE位与我们设置的模式有关。本实验设置为单次测量模式,对应MODE位为001。
2)ERROR位:如果命令被拒绝或在存储器中检测到不可纠正的错误,则该位为1,否则为0。
3)SED位:如果标记非易失性存储中的位错误已被纠正,则该位为1,否则为0。
4)RS位:每当ST480MC复位时,该位为1,否则为0。
5)D[1:0]位:仅在RM(ReadMeasurement)命令有意义,此数据作为ST480MC的响应,响应字节数对应2*D[1:0]+2,因此响应字节数为2、4、6或8。

l  寄存器映射关系
ST480MC有10个16位的非易失性存储寄存器,以及相同数量的易失性存储器,RR和WR命令只影响易失性存储器,无法直接访问非易失性存储器。易失性存储寄存器映射关系如下图所示(Address[1:0]=0x00):
image011.png
图44.1.3.2 ST480MC寄存器映射关系图

44.1.4 ST480MC工作时序简介
在探索者STM32F407开发板的上,A0是接GND的,所以ST480MC的设备地址是0x0C(不含最低位)。在前面我们已经介绍过IIC总线的基本读写,所以我们只介绍ST480MC的数据传输时序(橙色代表主机发送,蓝色代表从机发送)。

下面把实验中到的数据传输时序讲解一下,分别是对ST480MC的写时序和读时序。ST480MC写寄存器时序图如图44.1.4.1所示:
image014.png
图44.1.4.1 ST480MC写时序图

上图展示的主机向ST480MC写操作时序图,主机发送开始信号,主机在IIC总线发送第1个字节的数据为ST480MC的写设备地址0x18,等待ST480MC的应答信号,用于寻找总线上找到ST480MC,等待ST480MC的应答信号在获得ST480MC的应答信号之后,继续发送第2个字节数据,该字节数据是ST480MC的写寄存器命令,再等到ST480MC的应答信号,主机开始发送1个字节的写数据的高八位,然后等待ST480MC的应答信号,继续发送1个字节的写数据的低八位,然后等待ST480MC的应答信号。最后发送1个字节的待写入寄存器地址,等待ST480MC的应答信号。最后发出停止信号,终止数据传输。

说完寄存器写入方式之后,下面看一下图44.1.4.2关于ST480MC的读寄存器时序。
image016.png
图44.1.4.2 ST480MC读寄存器时序图

ST480MC读取寄存器的过程是一个复合的时序,其中包含写时序和读时序。先看第一个通信过程,这里是写时序,起始信号产生后,主机发送ST480MC写设备地址0x18,获取ST480MC的应答信号后,接着发送需要读取命令,等待ST480MC的应答信号;发送读寄存器地址,获取ST480MC的应答信号后,重新产生起始信号;主机发送ST480MC读设备地址0x19,获取从机应答信号后,接着从机返回刚刚在写时序中内存地址的数据,以字节为单位传输在总线上,假如主机获取数据后返回的是应答信号,那么从机会一直传输数据,这里我们读取一个寄存器的地址,所以返回的是状态寄存器State和寄存器的高八位和低八位;当主机发出停止信号,从机就会结束传输。

ST480MC读取数据过程与读取寄存器过程十分相似,少了发送read commond步骤,ST480MC读数据时序图如图44.1.4.3所示:
image018.png
图44.1.4.2 ST480MC读数据时序图

ST480MC读取数据的过程也是一个复合的时序,其中包含写时序和读时序。先看第一个通信过程,这里是写时序,起始信号产生后,主机发送ST480MC写设备地址0x18,获取ST480MC的应答信号后,接着发送读寄存器地址,获取ST480MC的应答信号后,重新产生起始信号;主机发送ST480MC读设备地址0x19,获取从机应答信号后,接着从机返回刚刚在写时序中内存地址的数据,以字节为单位传输在总线上,假如主机获取数据后返回的是应答信号,那么从机会一直传输数据,这里我们读取N个寄存器的地址,所以返回的是状态寄存器State和N个寄存器的高八位和低八位;当主机发出停止信号,从机就会结束传输。

44.2 硬件设计
1. 例程功能
开机的时候先检测ST480MC是否存在,在检测到ST480MC之后,可以通过KEY0进入校准模式,KEY_UP结束校准。在初始化完成后(必须进入一次校准,不然数据不是准确的数据),就会不断的读取ST480MC的数据,同时在LCD上面显示相关信息。LED0闪烁用于提示程序正在运行。

2. 硬件资源
1)LED灯
LED0 – PF9
2 ) 独立按键
KEY0 – PE4
KEY_UP – PA0
3)正点原子 2.8/3.5/4.3/7寸TFTLCD模块(仅限MCU屏,16位8080并口驱动)
4)串口1(PA9/PA10连接在板载USB转串口芯片CH340上面)
5)磁力计 ST480MC

3. 原理图
我们主要来看看ST480MC和开发板的连接,如下图所示:     
image019.png
图44.2.3.1 ST480MC连接原理

ST480MC的SCL和SDA分别连接在STM32的PB8和PB9上。本实验通过软件模拟IIC信号建立起与ST480MC的通信,进行数据发送与接收,LCD屏幕进行显示。通过原理图可知本实验使用的ST480MC没有INT引脚,所以源码里面只使用单次测量模式读取数据。

44.3 程序设计
44.3.1 程序流程图
QQ截图20230825143341.png
图44.3.1.1 磁力计实验程序流程图

44.3.2 程序解析
本实验中,我们通过GPIO来模拟IIC,所以不需要在Drivers/STM32F4xx_HAL_Driver分组下添加IIC的HAL库文件支持。实验工程中,我们复制IIC实验工程,重命名为磁力计实验,新增st480mc.c文件存放ST480MC驱动。

1. ST480MC驱动代码
这里我们只讲解核心代码,详细的源码请大家参考光盘本实验对应源码。ST480MC驱动源码包括两个文件:st480mc.c和st480mc.h。

在IIC实验中已经对IIC协议中的需要用到的信号都用函数封装好了,那么现在就要定义符合ST480MC时序的函数。为了使代码功能更加健全,所以在st480mc.h中宏定义了ST480MC的相关寄存器,具体定义如下:
  1. #define ST480MC_ADDR             0X0C     /* ST480MC IIC器件地址(A0 = 0时) */
  2. #define ST480MC_RESET            0XF0     /* ST480MC 复位命令 */
  3. #define ST480MC_READ_REG        0x50     /* ST480MC 读寄存器 */
  4. #define ST480MC_WRITE_REG       0x60     /* ST480MC 写寄存器 */
  5. #define ST480MC_READ_DATA       0x4F     /* ST480MC 读取全部数据(zxyt) */
  6. #define ST480MC_BURST_MODE      0x1F     /* ST480MC 突发模式读取数据(zxyt) */
  7. #define ST480MC_SINGLR_MODE     0x3F     /* ST480MC 单次测量读取数据(zxyt) */
  8. #define ST480MC_SENS_XY         667      /* X,Y 轴灵敏度; 单位:  LSB/Gauss */
  9. #define ST480MC_SENS_Z          400      /* Z 轴灵敏度     单位:  LSB/Gauss */
复制代码
下面先看一下st480mc_read_nbytes函数,实现从ST480MC芯片指定地址读取N字节数据,代码如下:
  1. /**
  2. *@brief        从ST480MC读取N字节数据
  3. *  @note       ST480MC的命令发送,也是用该函数实现(不带参数的命令, 也会有一个状态寄存器需
  4. *                要读取)
  5. *@param        addr    : 寄存器地址/命令
  6. *@param        length  : 读取长度
  7. *@param        buf     : 数据存储buf
  8. *@retval       0, 操作成功
  9. *               其他, 操作失败
  10. */
  11. uint8_t st480mc_read_nbytes(uint8_t addr, uint8_t length, uint8_t *buf)
  12. {
  13.     uint8_t i;
  14.    iic_start();
  15.    iic_send_byte((ST480MC_ADDR << 1) | 0X00);  /* IIC地址最低位是0, 表示写入 */
  16.     if (iic_wait_ack())
  17.     {
  18.        iic_stop();
  19.        return 1;
  20.     }
  21.    iic_send_byte(addr);                          /* 写地址/命令 */
  22.    iic_wait_ack();
  23.    iic_start();
  24.    iic_send_byte((ST480MC_ADDR << 1) | 0x01);  /* IIC地址最低位是1, 表示读取 */
  25.    iic_wait_ack();
  26.     for (i = 0; i < length; i++)                 /* 循环读取 数据 */
  27.     {
  28.        buf = iic_read_byte(1);
  29.     }
  30.    iic_stop();
  31.     return 0;
  32. }
复制代码
该函数首先调用iic_start函数产生起始信号,然后调用iic_send_byte函数发送第1个字节数据设备写地址,等待ST480MC设备返回应答信号;收到应答信号后,继续发送第2个1字节数据内存地址addr;等待接收应答后,发送第3个1字节数据设备读地址;最后ST480MC最先收到的是状态字节(一个字节),随后是读取的数据(数据都是两个字节),返回应答信号,主机调用iic_stop函数产生停止信号终止数据传输,等待ST480MC读取完毕。

接下来看一下st480mc_write_register函数,实现从ST480MC芯片指定地址写入数据,其定义如下:
  1. /**
  2. *@brief        ST480MC写寄存器
  3. *@param        reg    : 寄存器地址
  4. *@param        data  : 写入的值
  5. *@retval       0, 操作成功
  6. *               其他, 操作失败
  7. */
  8. uint8_t st480mc_write_register(uint8_t reg, uint16_t data)
  9. {
  10.    iic_start();
  11.    iic_send_byte((ST480MC_ADDR << 1) | 0X00); /* IIC地址最低位是0, 表示写入 */
  12.     if (iic_wait_ack())
  13.     {
  14.        iic_stop();
  15.        return 1;
  16.     }
  17.    iic_send_byte(ST480MC_WRITE_REG);           /* 发送写寄存器命令 */
  18.    iic_wait_ack();
  19.    iic_send_byte(data >> 8);                    /* 发送高字节数据 */
  20.    iic_wait_ack();
  21.    iic_send_byte(data & 0XFF);                  /* 发送低字节数据 */
  22.    iic_wait_ack();
  23.    iic_send_byte(reg << 2);                      /* 发送寄存器地址(低2位默认是0) */
  24.    iic_wait_ack();
  25.    iic_stop();
  26.     return 0;
  27. }
复制代码
该函数首先调用iic_start函数产生起始信号,然后调用iic_send_byte函数发送第1个字节数据设备写地址,等待ST480MC设备返回应答信号;收到应答信号后,继续发送第2个1字节写入数据内存地址ST480MC_WRITE_REG;等待接收应答后,发送第3个1字节数据写入内存地址的数据data高字节;等待接收应答后,发送第4个1字节数据写入内存地址的数据data低八位;等待接收应答后,发送第5个1字节数据写入内存地址的数据,因为数据地址的低2位为00,所以寄存器左移两位即:reg<<2;ST480MC设备写完数据,返回应答信号,主机调用iic_stop函数产生停止信号终止数据传输,等待ST480MC写入完毕。

介绍完写寄存器函数,那么我们接下来开始介绍st480mc_read_register函数,实现ST480MC内部寄存器的读取,代码如下:
  1. /**
  2. *@brief        ST480MC读寄存器
  3. *@param        reg  : 寄存器地址
  4. *@retval       读取到的值
  5. *               0XFFFF, 则可能表示错误
  6. */
  7. uint16_t st480mc_read_register(uint8_t reg)
  8. {
  9.     uint8_t buf[3];
  10.     uint8_t i;
  11.    iic_start();
  12.    iic_send_byte((ST480MC_ADDR << 1) | 0X00);  /* IIC地址最低位是0, 表示写入 */
  13.    iic_wait_ack();
  14.    iic_send_byte(ST480MC_READ_REG);            /* 发送读寄存器命令 */
  15.    iic_wait_ack();
  16.    iic_send_byte(reg << 2);                      /* 发送寄存器地址(低2位默认是0) */
  17.    iic_wait_ack();
  18.    
  19.    iic_start();
  20.    iic_send_byte((ST480MC_ADDR << 1) | 0x01);  /* IIC地址最低位是1, 表示读取 */
  21.    iic_wait_ack();
  22.     for (i = 0; i < 3; i++)                       /* 循环读取 数据 */
  23.     {
  24.        buf = iic_read_byte(1);
  25.     }
  26.    iic_stop();
  27.    
  28.     if(buf[0] & 0X10)
  29.     {
  30.        return 0XFFFF;
  31.     }
  32.     return ((uint16_t)buf[1] << 8) | buf[2];   /* 返回寄存器数据(16位) */
  33. }
复制代码
该函数与我们介绍第一个函数相似,首先调用iic_start函数产生起始信号,然后调用iic_send_byte函数发送第1个字节数据设备写地址,等待ST480MC设备返回应答信号;收到应答信号后,继续发送第2个1字节数据内存地址reg<<2(读取地址的低2位也为00);等待接收应答后,发送第3个1字节数据设备读地址;最后ST480MC设备接收完数据(一个状态字节和一个两字节的数据),返回应答信号,主机调用iic_stop函数产生停止信号终止数据传输,等待ST480MC读取完毕。

最后,我们介绍三个读取数据函数,代码如下:
  1. /**
  2. *@brief        ST480MC使用单次模式 读取一次磁力计数据(只读 X,Y,Z轴数据),读取一次至少
  3. *                15ms以上!
  4. *  @note       一次性读取XYZT,需要时间为: Tstby + Tactive + m * Tconvm + Tconvt
  5. *               Tstby    : 从IDLE状态到待机状态时间, 250us
  6. *               Tactive : 待机到激活状态时间, 8us
  7. *               Tconvm   : 单轴转换时间, (2 + 2^DIG_FILT) * 2^OSR * 0.064 ms, 其中
  8. *               DIG_FILT默认为2, OSR默认为0
  9. *               Tconvt   : 温度转换时间, 2^OSR2 * 0.192ms, OSR2默认为0
  10. *               因此, 一次性读取XYZT所需总时间为 = 0.25 + 0.008 + 3 * (2 + 2^2) *
  11. *                                                   2 ^ 0* 0.064+ 2^0 * 0.192 ≈ 1.6ms
  12. *               但是, ST480MC有个IDLE TO DATA READY的时间 : Tconv, 这个一般需要
  13. *                 16ms, 所以, 一次转换到数据读取, 基本要18ms左右
  14. *
  15. *@param        pmagx   : X轴磁力计原始值指针
  16. *               pmagy   : Y轴磁力计原始值指针
  17. *               pmagz   : Z轴磁力计原始值指针
  18. *@retval       0, 成功
  19. *               其他, 异常
  20. */
  21. uint8_t st480mc_read_magdata(int16_t *pmagx, int16_t *pmagy, int16_t *pmagz)
  22. {
  23.     uint8_t buf[7];
  24.     /*发送单次测量命令(不测量温度) */
  25.    st480mc_read_nbytes(ST480MC_SINGLR_MODE & 0XFE, 1, buf);
  26.    delay_ms(15);        /* 延时15ms,基本能争取读取数据 */
  27.     /* 发送读取数据命令(不读取温度) */
  28.    st480mc_read_nbytes(ST480MC_READ_DATA & 0XFE, 7, buf);
  29.     if(buf[0] & 0X10)   /* 读取数据异常 */
  30.     {
  31.        return buf[0];   /* 发生错误了 */
  32.     }
  33.     else
  34.     {
  35.        *pmagx = (short int)(buf[1] << 8) | buf[2];     /* 组合数据 */
  36.        *pmagy = (short int)(buf[3] << 8) | buf[4];     /* 组合数据 */
  37.        *pmagz = (short int)(buf[5] << 8) | buf[6];     /* 组合数据 */
  38.     }
  39.     return 0;
  40. }
  41. /**
  42. *@brief       ST480MC使用单次模式 读取一次温度传感器, 读取一次至少15ms以上!
  43. *  @note       一次性读取XYZT,需要时间为: Tstby + Tactive + m * Tconvm + Tconvt
  44. *               Tstby   : 从IDLE状态到待机状态时间, 250us
  45. *               Tactive : 待机到激活状态时间, 8us
  46. *               Tconvm  : 单轴转换时间, (2 + 2^DIG_FILT) * 2^OSR * 0.064 ms, 其中
  47. *               DIG_FILT默认为2, OSR默认为0
  48. *               Tconvt  : 温度转换时间, 2^OSR2 * 0.192ms, OSR2默认为0
  49. *               因此, 一次性读取XYZT所需总时间为 = 0.25 + 0.008 + 3 * (2 + 2^2) *
  50. *                                                    2^0 * 0.064 + 2^0 * 0.192 ≈1.6ms
  51. *               但是, ST480MC有个IDLE TO DATA READY的时间: Tconv, 这个一般需要16ms,
  52. *               所以,一次转换到数据读取, 基本要18ms左右
  53. *
  54. *@param        ptemp   : 温度值(℃)指针
  55. *@retval       0, 成功
  56. *               其他, 异常
  57. */
  58. uint8_t st480mc_read_temperature(float *ptemp)
  59. {
  60.     uint8_t buf[9];
  61.    st480mc_read_nbytes(ST480MC_SINGLR_MODE, 1, buf); /* 发送单次测量命令(含温度) */
  62.    delay_ms(15);        /* 延时15ms,基本能争取读取数据 */
  63.    st480mc_read_nbytes(ST480MC_READ_DATA, 9, buf);   /* 发送读取数据命令(含温度) */
  64.     if(buf[0] & 0X10)   /* 读取数据异常 */
  65.     {
  66.        return buf[0];   /* 发生错误了 */
  67.     }
  68.     else
  69.     {
  70.        *ptemp = (uint16_t)(buf[1] << 8) | buf[2];    /* 得到温度传感器原始值 */
  71.        *ptemp = (*ptemp - 46244) / 45.2f + 25;  /* 根据原厂提供的计算公式,换算成℃ */
  72.     }
  73.     return 0;
  74. }
  75. /**
  76. *@brief        ST480MC读取磁力计数据(只读 X,Y,Z轴数据),读times次取平均
  77. *  @note         本函数耗时 ≈ 15 * times ms
  78. *
  79. *@param        pmagx   : X轴磁力计原始值指针
  80. *               pmagy    : Y轴磁力计原始值指针
  81. *               pmagz    : Z轴磁力计原始值指针
  82. *               times    : 读取多少次取平均
  83. *@retval       0, 成功
  84. *               其他, 异常
  85. */
  86. uint8_t st480mc_read_magdata_average(int16_t *pmagx, int16_t *pmagy,
  87.                                              int16_t *pmagz, uint8_t times)
  88. {
  89.     uint8_t i = 0;
  90.     uint8_t error_cnt = 0;
  91.     int32_t magx = 0;
  92.     int32_t magy = 0;
  93.     int32_t magz = 0;
  94.     while(i < times)    /* 连续读取times次 */
  95.     {
  96.        if(st480mc_read_magdata(pmagx, pmagy, pmagz) == 0)   /* 读取数据是否正常? */
  97.        {
  98.            magx += *pmagx;            /* 累加 */
  99.            magy += *pmagy;
  100.            magz += *pmagz;
  101.            
  102.            i++;
  103.            error_cnt = 0;
  104.        }else
  105.        {
  106.            error_cnt++;               /* 错误计数器 */
  107.            delay_ms(10);
  108.            if(error_cnt > 100)       /* 连续100次出错, 直接返回异常 */
  109.            {
  110.                 return 0XFF;
  111.            }
  112.        }
  113.     }
  114.    
  115.     *pmagx = magx / times;   /* 取平均值 */
  116.     *pmagy = magy / times;   /* 取平均值 */
  117.     *pmagz = magz / times;   /* 取平均值 */
  118.    
  119.     return 0;
  120. }
复制代码
st480mc_read_magdata函数:首先设置为单次模式,然后读取一次磁力计的x,y,z轴原始数据,iic数据有7位:status,x高八位,x低八位,y高八位,y低八位,z高八位,z低八位,。
st480mc_read_temperature函数:在单次模式下,读取一次转换后的温度数据。
st480mc_read_magdata_average函数:调用st480mc_read_magdata函数,读取多次磁力计的x,y,z轴原始数据,取平均值。

我们在main函数主要调用st480mc_read_temperature函数和st480mc_read_magdata_average函数实现读取磁力计的数据。

2. main.c代码
我们读取到的x,y,z轴数据是原始数据,此时的数据还不能直接用作方位角的计算,因为此时的磁力计可能受到其他一些磁性材料的影响,导致圆心坐标偏移,所以我们要对磁力计进行校准,将漂移值移除,使圆心回到原点。

常用校准方法有:平面校准法、立体八字校准法、十面校准法。本实验采用平面校准法,针对XY轴进行校准,将开发板在XY平面内旋转一周,等价于将地球磁场矢量绕着点O(x0,y0)(即圆心)垂直于XY平面的法线旋转,这样就可以得到圆心的位置x_offeset = (x_max + x_min) / 2,y_offeset = (y_max + y_min) / 2。立体八字校准法与十面校准法我们不做介绍,感兴趣的可以自己去了解一下。

我们以XY平面为例,假设圆心O点发生了偏移,如图44.3.2.2.1所示:     
image023.png
图44.3.2.2.1 圆心偏移示意图

我们校准后,使得圆心O点回到零点(0,0),如图44.3.2.2.2所示:     
image025.png
图44.3.2.2.2 圆心在零点示意图

最后,我们打开main.c,这里就不全部贴出来了,仅介绍三个重要函数:
  1. /**
  2. *@brief       罗盘(磁力计)校准函数
  3. *  @note       这里我们使用最简单的平面校准方法.
  4. *               进入此函数后,请水平转动开发板至少一周(360°),转动完成后, 按WKUP键退出!
  5. *@param        无
  6. *@retval       无
  7. */
  8. voidcompass_calibration(void)
  9. {
  10.     int16_t x_min = 0;
  11.     int16_t x_max = 0;
  12.     int16_t y_min = 0;
  13.     int16_t y_max = 0;
  14.     int16_t magx, magy, magz;
  15.     uint8_t res;
  16.     uint8_t key = 0;
  17.    
  18.    lcd_clear(WHITE);
  19.    lcd_show_string(10, 90, 240, 16, 16, "CompassCalibration", RED);
  20.    lcd_show_string(10, 110, 240, 16, 16, "Pls rotatehoriz one cycle!", RED);
  21.    lcd_show_string(10, 130, 240, 16, 16, "If done,press WKUP key!", RED);
  22.    
  23.     while (1)
  24.     {
  25.        key = key_scan(0);
  26.       
  27.        if (key == WKUP_PRES)    /* 结束校准 */
  28.        {
  29.            break;
  30.        }
  31.       
  32.        res =st480mc_read_magdata(&magx, &magy, &magz);    /* 读取数据 */
  33.       
  34.        if (res == 0)
  35.        {
  36.            x_max = x_max < magx ? magx : x_max;              /* 记录x最大值 */
  37.            x_min = x_min > magx ? magx : x_min;              /* 记录x最小值 */
  38.            y_max = y_max < magy ? magy : y_max;              /* 记录y最大值 */
  39.            y_min = y_min > magy ? magy : y_min;              /* 记录y最小值 */
  40.        }
  41.       
  42.        LED0_TOGGLE();      /* LED0闪烁,提示程序运行 */
  43.     }
  44.    
  45.    g_magx_offset = (x_max + x_min) / 2;    /* X轴偏移量 */
  46.    g_magy_offset = (y_max + y_min) / 2;    /* Y轴偏移量 */
  47.    
  48.     /* 串口打印水平校准相关参数 */
  49.    printf("x_min:%d\r\n", x_min);
  50.    printf("x_max:%d\r\n", x_max);
  51.    printf("y_min:%d\r\n", y_min);
  52.    printf("y_max:%d\r\n", y_max);
  53.    
  54.    printf("g_magx_offset:%d\r\n", g_magx_offset);
  55.    printf("g_magy_offset:%d\r\n", g_magy_offset);
  56. }
  57. /**
  58. *@brief       罗盘获取角度
  59. *  @note      获取当前罗盘的角度(地磁角度)
  60. *@param       无
  61. *@retval      角度
  62. */
  63. floatcompass_get_angle(void)
  64. {
  65.     float angle;
  66.     int16_t magx, magy, magz;
  67.     /* 读取原始数据, 10次取平均 */
  68.    st480mc_read_magdata_average(&magx, &magy, &magz, 10);
  69.    
  70.    magx = (magx - g_magx_offset) ;     /* 根据校准参数, 计算新的输出 */
  71.    magy = (magy - g_magy_offset) ;     /* 根据校准参数, 计算新的输出 */
  72.     /* 根据不同的象限情况, 进行方位角换算 */
  73.     if ((magx > 0) && (magy > 0))
  74.     {
  75.        angle = (atan((double)magy / magx) * 180) / 3.14159f;
  76.     }
  77.     else if ((magx > 0) && (magy < 0))
  78.     {
  79.        angle = 360 + (atan((double)magy / magx) * 180) / 3.14159f;
  80.     }
  81.     else if ((magx == 0) && (magy > 0))
  82.     {
  83.        angle = 90;
  84.     }
  85.     else if ((magx == 0) && (magy < 0))
  86.     {
  87.        angle = 270;
  88.     }
  89.     else if (magx < 0)
  90.     {
  91.        angle = 180 + (atan((double)magy / magx) * 180) / 3.14159f;
  92.     }
  93.    
  94.     if (angle > 360) angle = 360;   /* 限定方位角范围 */
  95.     if (angle < 0) angle = 0;        /* 限定方位角范围 */
  96.     return angle;
  97. }
  98. int main(void)
  99. {
  100.     uint8_t t = 0;
  101.     uint8_t key;
  102.     float angle;
  103.     float temperature;
  104.     int16_t magx, magy, magz;
  105.    HAL_Init();                              /* 初始化HAL库 */
  106.    sys_stm32_clock_init(336, 8, 2, 7); /* 设置时钟,168Mhz */
  107.    delay_init(168);                       /* 延时初始化 */
  108.    usart_init(115200);                    /* 串口初始化为115200 */
  109.    usmart_dev.init(84);                  /* 初始化USMART */
  110.    led_init();                             /* 初始化LED */
  111.    key_init();                             /* 初始化按键 */
  112.    lcd_init();                             /* 初始化LCD */
  113.     while (st480mc_init())                /* ST480MC初始化 */
  114.     {
  115.        lcd_show_string(30, 110, 200, 16, 16, "ST480MCError", RED);
  116.        delay_ms(200);
  117.        lcd_fill(30, 110, 239, 130 + 16, WHITE);
  118.        delay_ms(200);
  119.     }
  120. RST:
  121.    lcd_show_string(30, 50, 200, 16, 16, "STM32", RED);
  122.    lcd_show_string(30, 70, 200, 16, 16, "ST480MCTEST", RED);
  123.    lcd_show_string(30, 90, 200, 16, 16, "ATOM@ALIENTEK", RED);
  124.    lcd_show_string(30, 110, 200, 16, 16, "KEY0 tocalibration", RED);
  125.     while (1)
  126.     {
  127.        key = key_scan(0);
  128.       
  129.        if (key == KEY0_PRES)        /* KEY0 按下 ,执行校准 */
  130.        {
  131.            compass_calibration();   /* 校准函数 */
  132.            lcd_clear(WHITE);         /* 清屏 */
  133.            goto RST;                   /* 校准完后,跳到RST, 重新显示提示信息 */
  134.        }
  135.       
  136.        delay_ms(10);
  137.        t++;
  138.       
  139.        if (t == 20)    /* 0.2秒左右更新一次温度/磁力计原始值 */
  140.        {        
  141.            angle =compass_get_angle();                   /* 执行一次约150ms */
  142.            user_show_angle(30, 130, angle);               /* 显示角度 */
  143.       
  144.            st480mc_read_temperature(&temperature);      /* 读取温湿度值 */
  145.            user_show_temprate(30, 150, temperature);    /* 显示温度 */
  146.            
  147.            st480mc_read_magdata(&magx, &magy, &magz);   /* 读取原始数据 */
  148.            user_show_mag(30, 170, "MagX:", magx);       /* 显示magx */
  149.            user_show_mag(30, 190, "MagY:", magy);       /* 显示magy */
  150.            user_show_mag(30, 210, "MagZ:", magz);       /* 显示magz */
  151.            
  152.            t = 0;
  153.            LED0_TOGGLE();  /* LED0闪烁 */
  154.        }
  155.     }
  156. }
复制代码
下面分别介绍一下这三个函数。

compass_calibration,该函数用于校准磁力计ST480MC,校准方法采用的是我们前面介绍的平面校准法,校准完成后的偏移量offeset存到全局变量g_magx_offset和g_magy_offset。

compass_get_angle,该函数用于计算XY平面的方位角angle。首先,获取x,y,z的原始数据,然后x和y加上校准完成后的偏移量,最后再计算XY平面的方位角angle,atan求出来的角度范围-90°~+90°,所以想要得到0°~360°,我们进行方位角计算式,就要根据x,y判断象限,不同象限的情况下进行不同的计算。

main函数,则比较简单,初始化相关外设,在循环中不断获取磁力计数据,并在LCD上显示数据;我们可以通过按键KEY0进入校准模式,KEY_UP退出校准模式。

44.4 下载验证
在代码编译成功后,我们通过下载代码到开发板上,可以看到LED0不停闪烁,提示程序已经在运行了。LCD显示磁力计的MagX,MagY,MagZ原始数据和计算后的Angle,Temp数据,如图44.4.1所示:     
image027.png
图44.4.1 程序运行效果图

按下KEY0,进入校准模式,如图44.4.2所示:     
image029.png
图44.4.2 校准模式效果图

退出校准模式后,可以与手机指南针的航向角进行对比,如果发现误差较大,则需要再次进行校准。
正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2025-2-24 06:58

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表