OpenEdv-开源电子网

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

《STM32H7R7开发指南 V1.1 》第四十六章 QMC6308磁力计实验

[复制链接]

1334

主题

1350

帖子

2

精华

超级版主

Rank: 8Rank: 8

积分
5699
金钱
5699
注册时间
2019-5-8
在线时间
1536 小时
发表于 昨天 09:36 | 显示全部楼层 |阅读模式
第四十六章 QMC6308磁力计实验

1)实验平台:正点原子STM32H7R7开发板

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

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

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

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

6)正点原子STM32开发板技术交流群:756580169


2.jpg

3.png

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


46.1 QMC6308介绍

46.1.1 QMC6308简介
QMC6308是一款QST(上海矽睿)的一款三轴地磁传感器,内部进行信号处理,通过IIC接口进行输出。QMC6308提供的输出信号与它在XYZ方向上测量的磁场成比例,如果需要消除比例因子,需要用户自行校准。QMC6308主要特点如下:
1)测量范围:最大±30高斯
2)高分辨率:0.15μT/LSB(X/Y轴),0.25μT/LSB(Z轴)
3)高灵敏度:三个轴正交:90°±1,灵敏度:1000 LSBs/Gauss
4)工作温度范围:-40~+85℃
5)供电电压:1.65~1.95V
6)16位ADC采样
7)内部带有温度传感器
8)支持中断
QMC6308传感器的检测轴如图46.1.1.1所示:


第四十六章 QMC6308磁力计实验552.png
图46.1.1.1 QMC6308检测轴与方向

在QMC6308传感器,通过MUX选择器选择采集的是X、Y、Z的数据,然后通过16位ADC获取X、Y、Z的数据,同样,传感器的温度也是通过ADC采集,采集到的数据通过IIC接口进行输出。QMC6308内部结构图如图46.1.1.2所示:

第四十六章 QMC6308磁力计实验697.png
图46.1.1.2 QMC6308内部结构图

图46.1.1.2中SDA和SCL是连接MCU的IIC接口,QMC6308的WLCSP封装只支持IIC协议,这里我们主要介绍使用IIC驱动QMC6308。
每一个IIC设备都有自己的设备地址,QMC6308也不例外,QMC6308的设备地址是包括不可编程部分和可编程部分。
本实验将A1接地,那么设备地址为0x2C。


46.1.2 QMC6308工作模式简介
接下来我们分别QMC6308三种工作模式:单次测量模式、连续模式、挂起模式。
1. 单次测量模式
在单次测量模式(Mode bits=10)时,整个芯片只运行一次,在完成一次测量后进入暂时挂起模式。噪声性能也可以通过OSR2设置来控制。
2. 连续模式
在连续模式(mode位= 11)期间,磁传感器连续进行测量,并将测量数据放入数据输出寄存器。字段范围寄存器位于控制寄存器(OBH)中,数据输出速率与OSR2设置有关,它们应该在连续模式下为应用程序正确设置。
3. 挂起模式
挂起模式是磁力计在POR和软复位时的默认状态。在这种模式下,只有少数功能块被激活,从而使功耗尽可能低。在这种状态下,寄存器值由低功耗LDO保持,I2C接口处于活动状态,允许所有寄存器读写。在悬浮状态下没有磁力计测量。

46.1.3 QMC6308工作时序简介
在STM32H7R7开发板的上,A1是接GND的,所以QMC6308的设备地址是0x2C。在前面我们已经介绍过IIC总线的基本读写,所以我们只介绍QMC6308的数据传输时序。
下面把实验中到的数据传输时序讲解一下,分别是对QMC6308的写时序和读时序。QMC6308写寄存器时序图如图46.1.4.1所示:


第四十六章 QMC6308磁力计实验1427.png
图46.1.4.1 QMC6308写时序图

I2C写序列以主机生成的起始条件开始,后面跟着7位从机地址和一个写位(R/W-0)。从机发送一个确认位(ACK-0)并释放总线。主机发送一个字节的寄存器地址。从机再次确认传输,并等待8位数据写入指定的寄存器地址。从机确认数据字节后,主机产生停止信号并终止写入协议。
说完寄存器写入方式之后,下面看一下图46.1.4.2关于QMC6308的读寄存器时序。


第四十六章 QMC6308磁力计实验1628.png
图46.1.4.2 QMC6308读时序图

读取寄存器的过程是一个复合的时序,其中包含写时序和读时序。启动条件必须在两个时序之间产生。先看第一个通信过程,这里是写时序,起始信号产生后,主机发送QMC6308写设备地址,获取从机的应答信号后,然后主机释放总线并等待数据字节被释放在从机读出。在每个数据字节之后,主机必须生成一个确认位(ACK-0)来启用进一步的数据传输。来自主服务器的NACK将停止从服务器传输数据。从机释放总线,以便主机可以产生一个停止条件和终止传输。

46.2 硬件设计

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

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

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


第四十六章 QMC6308磁力计实验2211.png
图46.2.3.1 QMC6308连接原理

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

46.3 程序设计

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

1. QMC6308驱动代码
这里我们只讲解核心代码,详细的源码请大家参考光盘本实验对应源码。QMC6308驱动源码包括两个文件:qmc6308.c和qmc6308.h。
在IIC实验中已经对IIC协议中的需要用到的信号都用函数封装好了,那么现在就要定义符合QMC6308时序的函数。为了使代码功能更加健全,所以在qmc6308.h中宏定义了QMC6308的相关寄存器,具体定义如下:

  1. #define QMC6308_ADDR                0X2C    /* QMC6308 IIC器件地址 */
  2. #define QMC6308_CHIP_ID_REG         0x00    /* 芯片ID寄存器 */
  3. #define QMC6308_DEVICE_ID           0x80    /* ID号 */

  4. /* QMC6308寄存器地址 */
  5. #define QMC6308_DATA_X_L_REG        0x01    /* x轴低8位数据输出寄存器 */
  6. #define QMC6308_DATA_X_H_REG        0x02    /* x轴高8位数据输出寄存器 */
  7. #define QMC6308_DATA_Y_L_REG        0x03    /* y轴低8位数据输出寄存器 */
  8. #define QMC6308_DATA_Y_H_REG        0x04    /* y轴高8位数据输出寄存器 */
  9. #define QMC6308_DATA_Z_L_REG        0x05    /* z轴低8位数据输出寄存器 */
  10. #define QMC6308_DATA_Z_H_REG        0x06    /* z轴高8位数据输出寄存器 */
  11. #define QMC6308_STATUS_REG          0x09    /* 状态寄存器 */
  12. #define QMC6308_CTL_REG1            0x0A    /* 控制寄存器1 */
  13. #define QMC6308_CTL_REG2            0x0B    /* 控制寄存器2 */
  14. #define QMC6308_CTL_REG3            0x0D    /* 控制寄存器3 */
  15. #define QMC6308_CTL_REG4            0x0F    /* 控制寄存器4 */
复制代码
下面先看一下qmc6308_read_nbytes函数,实现从QMC6308芯片指定地址读取N字节数据,代码如下:
  1. /**
  2. * @brief       从QMC6308读取N字节数据
  3. * [url=home.php?mod=space&uid=60778]@note[/url]        QMC6308的命令发送, 也是用该函数实现(不带参数的命令, 也会有一个状态寄存器
  4. *              需要读取)
  5. * [url=home.php?mod=space&uid=271674]@param[/url]       addr  : 寄存器地址/命令
  6. * @param       buf   : 数据存储buf
  7. * @param       length: 读取长度
  8. * @retval      0, 操作成功
  9. *              其他, 操作失败
  10. */
  11. uint8_t qmc6308_read_nbytes(uint8_t addr, uint8_t *buf, uint8_t length)
  12. {
  13.     uint8_t i;

  14.     iic_start();
  15.     iic_send_byte((QMC6308_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((QMC6308_ADDR << 1) | 0x01);  /* IIC地址最低位是1, 表示读取 */
  25.     iic_wait_ack();

  26.     for (i = 0; i < length; i++)
  27.     {
  28.         buf[i] = iic_read_byte((i == (length - 1)) ? 0 : 1);
  29.     }
  30.     iic_stop();

  31.     return 0;
  32. }
复制代码
该函数首先调用iic_start函数产生起始信号,然后调用iic_send_byte函数发送第1个字节数据设备写地址,等待QMC6308设备返回应答信号;收到应答信号后,继续发送第2个1字节数据内存地址addr;等待接收应答后,发送第3个1字节数据设备读地址;最后QMC6308最先收到的是状态字节(一个字节),随后是读取的数据(数据都是两个字节),返回应答信号,主机调用iic_stop函数产生停止信号终止数据传输,等待QMC6308读取完毕。
接下来看一下qmc6308_write_register函数,实现从QMC6308芯片指定地址写入数据,其定义如下:

  1. /**
  2. * @brief       QMC6308写寄存器
  3. * @param       reg  : 寄存器地址
  4. * @param       data : 写入的值
  5. * @retval      0, 操作成功
  6. *              其他, 操作失败
  7. */
  8. uint8_t qmc6308_write_register(uint8_t reg, uint8_t data)
  9. {
  10.     iic_start();
  11.     iic_send_byte((QMC6308_ADDR << 1) | 0X00);  /* IIC地址最低位是0, 表示写入 */
  12.     if (iic_wait_ack())
  13.     {
  14.         iic_stop();
  15.         return 1;
  16.     }
  17.     iic_send_byte(reg);                         /* 发送写寄存器命令 */
  18.     if (iic_wait_ack() != 0)
  19.     {
  20.         iic_stop();
  21.         return 1;
  22.     }
  23.    
  24.     iic_send_byte(data);                        /* 发送数据 */
  25.     if (iic_wait_ack() != 0)
  26.     {
  27.         iic_stop();
  28.         return 1;
  29.     }
  30.     iic_stop();

  31.     return 0;
  32. }
复制代码
该函数首先调用iic_start函数产生起始信号,然后调用iic_send_byte函数发送第1个字节数据设备写地址,等待QMC6308设备返回应答信号;收到应答信号后,继续发送第2个1字节写入数据内存地址reg;等待接收应答后,发送第3个1字节数据写入内存地址的数据data高字节;QMC6308设备写完数据,返回应答信号,主机调用iic_stop函数产生停止信号终止数据传输,等待QMC6308写入完毕。
介绍完写寄存器函数,那么我们接下来开始介绍qmc6308_read_register函数,实现QMC6308内部寄存器的读取,代码如下:

  1. /**
  2. * @brief       QMC6308读寄存器
  3. * @param       reg  : 寄存器地址
  4. * @retval      读取到的值
  5. *              0XFFFF, 则可能表示错误
  6. */
  7. uint16_t qmc6308_read_register(uint8_t reg)
  8. {
  9.     uint8_t temp = 0;

  10.     iic_start();
  11.     iic_send_byte((QMC6308_ADDR << 1) | 0X00);  /* IIC地址最低位是0, 表示写入 */
  12.     iic_wait_ack();

  13.     iic_send_byte(reg);                         /* 发送读寄存器命令 */
  14.     iic_wait_ack();

  15.     iic_start();
  16.     iic_send_byte((QMC6308_ADDR << 1) | 0x01);  /* IIC地址最低位是1, 表示读取 */
  17.     if (iic_wait_ack() != 0)
  18.     {
  19.         iic_stop();
  20.         return 1;
  21.     }
  22.     temp = iic_read_byte(0);
  23.     iic_stop();
  24.     return temp;
  25. }
复制代码
该函数与我们介绍第一个函数相似,首先调用iic_start函数产生起始信号,然后调用iic_send_byte函数发送第1个字节数据设备写地址,等待QMC6308设备返回应答信号;收到应答信号后,继续发送第2个1字节数据内存地址reg<<1(读取地址的低1位也为0);等待接收应答后,发送第3个1字节数据设备读地址;最后QMC6308设备接收完数据(一个状态字节和一个两字节的数据),返回应答信号,主机调用iic_stop函数产生停止信号终止数据传输,等待QMC6308读取完毕。
最后,我们介绍三个读取数据函数,代码如下:

  1. /**
  2. * @brief       QMC6308读取磁力计数据(只读 X,Y,Z轴数据)
  3. * @param       pmagx    : X轴磁力计原始值指针
  4. * @param       pmagy    : Y轴磁力计原始值指针
  5. * @param       pmagz    : Z轴磁力计原始值指针
  6. * @retval      0, 成功
  7. *              其他, 异常
  8. */
  9. uint8_t qmc6308_read_magdata(float *pmagx, float *pmagy, float *pmagz)
  10. {
  11.     int res = 0;
  12.     unsigned char mag_data[6];
  13.     short hw_d[3] = {0};
  14.     short raw_c[3];
  15.     int t = 0;
  16.     unsigned char rdy = 0;
  17.    
  18.     /* Check status register for data availability */
  19.     while(!(rdy & 0x01) && (t < 5))
  20.     {
  21.         qmc6308_read_nbytes(QMC6308_STATUS_REG, &rdy, 1);
  22.         t++;
  23.     }
  24. res = qmc6308_read_nbytes(QMC6308_DATA_X_L_REG, mag_data, 6);   
  25. /* 读取XYZ轴数据 */
  26.     if(res != 0)
  27.     {
  28.         return 1;                                   /* 读取失败直接返回 */
  29.     }
  30.     /* 组合数据 */
  31.     hw_d[0] = (short)(((mag_data[1]) << 8) | mag_data[0]);
  32.     hw_d[1] = (short)(((mag_data[3]) << 8) | mag_data[2]);
  33.     hw_d[2] = (short)(((mag_data[5]) << 8) | mag_data[4]);
  34.    
  35.     /* Unit:mG  1G = 100uT = 1000mG */
  36.     raw_c[AXIS_X] = (int)(c_map.sign[AXIS_X] * hw_d[c_map.map[AXIS_X]]);
  37.     raw_c[AXIS_Y] = (int)(c_map.sign[AXIS_Y] * hw_d[c_map.map[AXIS_Y]]);
  38.     raw_c[AXIS_Z] = (int)(c_map.sign[AXIS_Z] * hw_d[c_map.map[AXIS_Z]]);
  39.    
  40.     *pmagx = (float)raw_c[AXIS_X] / 10.0f;
  41.     *pmagy = (float)raw_c[AXIS_Y] / 10.0f;
  42.     *pmagz = (float)raw_c[AXIS_Z] / 10.0f;
  43.     return res;
  44.    
  45. }

  46. /**
  47. * @brief       QMC6308读取磁力计数据(只读 X,Y,Z轴数据),读times次取平均
  48. * @note        本函数耗时 ≈ 15 * times ms
  49. *
  50. * @param       pmagx    : X轴磁力计原始值指针
  51. * @param       pmagy    : Y轴磁力计原始值指针
  52. * @param       pmagz    : Z轴磁力计原始值指针
  53. * @param       times    : 读取多少次取平均
  54. * @retval      0, 成功
  55. *              其他, 异常
  56. */
  57. uint8_t qmc6308_read_magdata_average(float *pmagx, float *pmagy, float *pmagz, uint8_t times)
  58. {
  59.     uint8_t i = 0;
  60.     uint8_t error_cnt = 0;
  61.     int32_t magx = 0;
  62.     int32_t magy = 0;
  63.     int32_t magz = 0;

  64.     while (i < times)                                   /* 连续读取times次 */
  65.     {
  66.         if (qmc6308_read_magdata(pmagx,pmagy,pmagz)==0) /* 读取数据是否正常? */
  67.         {
  68.             magx += *pmagx;                             /* 累加 */
  69.             magy += *pmagy;
  70.             magz += *pmagz;
  71.             
  72.             i++;
  73.             error_cnt = 0;
  74.         }
  75.         else
  76.         {
  77.             error_cnt++;                                /* 错误计数器 */
  78.             delay_ms(10);
  79.             if (error_cnt > 100)         /* 连续100次出错, 直接返回异常 */
  80.             {
  81.                 return 0XFF;
  82.             }
  83.         }
  84.     }
  85.     *pmagx = magx / times;                              /* 取平均值 */
  86.     *pmagy = magy / times;                              /* 取平均值 */
  87.     *pmagz = magz / times;                              /* 取平均值 */
  88.    
  89.     return 0;
  90. }
复制代码
qmc6308_read_magdata函数:首先设置为单次模式,然后读取一次磁力计的x,y,z轴原始数据,iic数据有7位:status,x高八位,x低八位,y高八位,y低八位,z高八位,z低八位。
qmc6308_read_magdata_average函数:调用qmc6308_read_magdata函数,读取多次磁力计的x,y,z轴原始数据,取平均值。
我们在main函数主要调用qmc6308_read_magdata函数实现读取磁力计的数据。

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点发生了偏移,如图46.3.1.2.1所示:


第四十六章 QMC6308磁力计实验9904.png
图46.3.1.2.1 圆心偏移示意图

我们校准后,使得圆心O点回到零点(0,0),如图46.3.1.2.2所示:

第四十六章 QMC6308磁力计实验9964.png
图46.3.1.2.2 圆心在零点示意图

最后,我们打开main.c,这里就不全部贴出来了,仅介绍三个重要函数:
  1. /**
  2. * @brief    罗盘(磁力计)校准函数
  3. * @note     这里我们使用最简单的平面校准方法.
  4. *           进入此函数后,请水平转动开发板至少一周(360°),转动完成后, 按WKUP键退出!
  5. * @param    无
  6. * @retval   无
  7. */
  8. void compass_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, "Compass Calibration", RED);
  20.     lcd_show_string(10, 110, 240, 16, 16, "Pls rotate horiz 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 = qmc6308_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. float compass_get_angle(void)
  64. {
  65.     float angle;
  66.     int16_t magx, magy, magz;

  67.     /* 读取原始数据, 10次取平均 */
  68.     qmc6308_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 magx, magy, magz;
  104.    
  105.     sys_mpu_config();                   /* 配置MPU */
  106.     sys_cache_enable();                 /* 使能Cache */
  107.     HAL_Init();                         /* 初始化HAL库 */
  108.     sys_stm32_clock_init(300, 6, 2);    /* 配置时钟,600MHz */
  109.     delay_init(600);                    /* 初始化延时 */
  110.     usart_init(500000);                 /* 初始化串口 */
  111.     led_init();                         /* 初始化LED */
  112.     key_init();                         /* 初始化按键 */
  113.     hyperram_init();                    /* 初始化HyperRAM */
  114.     lcd_init();                         /* 初始化LCD */
  115.    
  116.     while (qmc6308_init())              /* QMC6308初始化 */
  117.     {
  118.         lcd_show_string(30, 110, 200, 16, 16, "QMC6308 Error", RED);
  119.         delay_ms(200);
  120.         lcd_fill(30, 110, 239, 130 + 16, WHITE);
  121.         delay_ms(200);
  122.     }
  123.     RST:
  124.     lcd_show_string(30, 50, 200, 16, 16, "STM32", RED);
  125.     lcd_show_string(30, 70, 200, 16, 16, "QMC6308 TEST", RED);
  126.     lcd_show_string(30, 90, 200, 16, 16, "ATOM@ALIENTEK", RED);
  127.     lcd_show_string(30, 110, 200, 16, 16, "KEY0 to calibration", RED);

  128.     while (1)
  129.     {
  130.         key = key_scan(0);
  131.         
  132.         if (key == KEY0_PRES)            /* KEY0 按下 ,执行校准 */
  133.         {
  134.             compass_calibration();       /* 校准函数 */
  135.             lcd_clear(WHITE);            /* 清屏 */
  136.             goto RST;                    /* 校准完后,跳到RST, 重新显示提示信息 */
  137.         }
  138.         
  139.         delay_ms(10);

  140.         t++;
  141.         
  142.         if (t == 10)                     /* 0.1秒左右更新一次磁力计原始值 */
  143.         {        
  144.             angle = compass_get_angle();                /* 执行一次约150ms */
  145.             user_show_angle(30, 130, 360 - angle);      /* 显示角度 */
  146.             qmc6308_read_magdata(&magx, &magy, &magz);  /* 读取原始数据 */
  147.             user_show_mag(30, 170, "MagX:", magx);      /* 显示magx */
  148.             user_show_mag(30, 190, "MagY:", magy);      /* 显示magy */
  149.             user_show_mag(30, 210, "MagZ:", magz);      /* 显示magz */
  150.             
  151.             t = 0;
  152.             LED0_TOGGLE();                              /* LED0闪烁 */
  153.         }
  154.     }
  155. }
复制代码
下面分别介绍一下这三个函数。
compass_calibration,该函数用于校准磁力计QMC6308,校准方法采用的是我们前面介绍的平面校准法,校准完成后的偏移量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进入校准模式。


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

第四十六章 QMC6308磁力计实验15254.png
图46.4.1 程序运行效果图

按下KEY0,进入校准模式,如图46.4.2所示:

第四十六章 QMC6308磁力计实验15298.png
图46.4.2 校准模式效果图

退出校准模式后,可以与手机指南针的航向角进行对比,如果发现误差较大,则需要再次进行校准。
回复

使用道具 举报

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

本版积分规则



关闭

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

正点原子公众号

如发现本坛存在违规或侵权内容, 请点击这里发送邮件举报 (或致电020-38271790)。请提供侵权说明和联系方式。我们将及时审核依法处理,感谢配合。

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

GMT+8, 2026-5-22 13:33

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

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