MMC5603地磁传感器调试日志

本文详细介绍使用nrf52832开发板与MMC5603地磁传感器搭建指南针的过程,涵盖硬件连接、TWI通信配置、传感器数据读取及磁场测量等关键步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

/******************************************************************************/
/*开发平台:Keil uVision5                                                                      */
/*开发语言:C语言                                                                                 */
/*作者:jerseyceo                                                                                  */     
/*实现功能:地磁传感器的应用(指南针)                                            */
/******************************************************************************/
第一步:nrf52832开发板与MMC5603地磁传感器硬件连接

注:MMC5603NJ可以从单个1.62V到3.6 v供应。下面的电路连接图说明了电源连接选项。

          nrf52832                          MMC5603
引脚:    VDD               —>              VDD
引脚:    GND               —>              GND
引脚:    SCL                —>              SCL
引脚:    SDA                —>              SDA

NOTE:焊接引脚连接上电后,使用万用表测量传感器VDD与GND引脚两端电压,检测传感器是否处于正常工作电压范围,以便进行接下来的keil编程调试与开发。

第二步:学习并掌握硬件TWI(I2C)通信原理,使用I2C通信协议配置传感器工作参数。

1.库函数的应用

                                                        图1:TWI应用步骤

1.1.定义TWI驱动程序实例

/* TWI instance ID,ID和外设编号对应,0:TWI0   1:TWI1*/
#define TWI_INSTANCE_ID     0

/* TWI instance. 定义TWI驱动程序实例,使用TWI0:外设0*/
static const nrf_drv_twi_t m_twi = NRF_DRV_TWI_INSTANCE(TWI_INSTANCE_ID);

/* 从机设备地址0x60=0x30<<1(TWI硬件自动完成读写位)*/
#define MMC5603_7BITI2C_ADDRESS        0x30

 

1.2.初始化TWI

初始化实例:err_code = nrf_drv_twi_init(&m_twi, &twi_mmc5603_config, twi_handler, NULL);

1.2.1.初始化TWI函数原型:

ret_code_t nrf_drv_twi_init(nrf_drv_twi_t const *        p_instance,
                                           nrf_drv_twi_config_t const * p_config,
                                           nrf_drv_twi_evt_handler_t    event_handler,
                                           void *                       p_context)

p_instance:指向驱动程序实例结构体

p_config:指向初始化配置结构体

event_handler:事件句柄,如果设置为NULL,则使能TWI阻塞模式

p_context:指向传递给事件句柄的context

 

1.2.2.初始化配置结构体(nrf_drv_twi_config_t) :

const nrf_drv_twi_config_t twi_mmc5603_config = {
       .scl                      = ARDUINO_SCL_PIN,                 //定义TWI SCL 引脚
       .sda                    = ARDUINO_SDA_PIN,                 //定义TWI SDA 引脚
       .frequency           = NRF_DRV_TWI_FREQ_100K,  //TWI 工作频率
       .interrupt_priority = APP_IRQ_PRIORITY_HIGH,    //TWI优先级
       .clear_bus_init     = false };

1.2.3.TWI事件句柄(nrf_drv_twi_evt_handler_t) :(TWI工作于非阻塞模式)

//TWI传输完成标志,false表示传输未完成

static volatile bool m_xfer_done = false;

void twi_handler(nrf_drv_twi_evt_t const * p_event, void * p_context)
{

    //判断TWI事件类型
    switch (p_event->type)
    {

        //传输事件完成
        case NRF_DRV_TWI_EVT_DONE:

           //功能代码
            if (p_event->xfer_desc.type == NRF_DRV_TWI_XFER_RX)
            {
             //此处可对传输事件完成后,打印读取数据日志信息。
            }
            m_xfer_done = true;//置位传输完成标志
            break;


       case  NRF_DRV_TWI_EVT_ADDRESS_NACK:   //< Error event: NACK received after sending the address.
       case  NRF_DRV_TWI_EVT_DATA_NACK :   //< Error event: NACK received after sending a data byte. 
        default:
            break;
    }
}

1.2.4.使能TWI(nrf_drv_twi_enable):初始化完成后,使能TWI才能进行数据传输

__STATIC_INLINE
void nrf_drv_twi_enable(nrf_drv_twi_t const * p_instance)
{
    if (NRF_DRV_TWI_USE_TWIM)
    {
        nrfx_twim_enable(&p_instance->u.twim);
    }
    else if (NRF_DRV_TWI_USE_TWI)
    {
        nrfx_twi_enable(&p_instance->u.twi);
    }
}

1.2.5.数据传输

数据发送:向TWI从机发送数据

实例:err_code_tx = nrf_drv_twi_tx(&m_twi, i2c_add, &reg_add, 1, false);

ret_code_t  nrf_drv_twi_tx(

                          nrf_drv_twi_t const * p_instance,//指向TWI驱动程序实例结构体
                          uint8_t               address,//指定的从机地址(7位LSB)
                          uint8_t const *       p_data,//指向传输数据缓存(传感器内部寄存器/写入寄存器的命令)
                          uint8_t               length,//发送的字节数
                          bool                  no_stop)//如果置位,成功传输后不会生成停止条件(下次传输中可重复启动)

数据接收:从TWI从机读取数据

实例:ret_code_t err_code_rx = nrf_drv_twi_rx(&m_twi, i2c_add, data, sizeof(data));

ret_code_t nrf_drv_twi_rx(

                          nrf_drv_twi_t const * p_instance,//指向TWI驱动程序实例结构体
                          uint8_t               address,//指定的从机地址(7位LSB)
                          uint8_t *             p_data,//指向接收数据缓存
                          uint8_t               length)//读取的字节数

2.MMC5603地磁传感器原始数据读取(TWI0非阻塞)程序调试

2.1.MMC5603 I2C 时序

       MMC5603 数据传输的时序如下图所示,首先产生起始条件,紧跟着发送7位地址 + 读

写位(0=写,1=读),之后发送传输的数据,最后产生停止条件。

                                              图2:MMC5603 数据传输时序

*_*:根据 MMC5603 数据传输时序图,对照MMC5603的寄存器表,编写“写MMC5603寄存器”

和“读MMC5603寄存器”的函数。在编写读/写函数时,需要注意的是:

(1)因为时序要求写操作后产生停止条件,因此调用nrf_drv_twi_tx()函数时,参数“no_stop”必

须为false,即产生停止条件。

(2)配置TWI的工作模式是非阻塞模式,是异步的,所以调用nrf_drv_twi_tx()函数后,要等待

TWI传输完成,代码中通过查询m_xfer_done标志判断TWI传输是否完成。

2.2.写MMC5603寄存器函数

/*@brief I2C write register */
int MMC5603_Write_Reg(unsigned char i2c_add, unsigned char reg_add, unsigned char cmd)
{
    /* i2c_add is the 7-bit i2c address of the sensor
     * reg_add is the register address to write
     * cmd is the value that need to be written to the register
     * I2C operating successfully, return 1, otherwise return 0;
     */
    m_xfer_done = false;//TWI传输完成标志位设置为 false,表示传输未完成
   
    ret_code_t err_code_tx; /*指示TWI硬件驱动API调用过程的成功或失败*/
    uint8_t reg[2]={reg_add,cmd};/*等待写入的寄存器地址,要写入的命名*/ 
    err_code_tx = nrf_drv_twi_tx(&m_twi, i2c_add, reg, sizeof(reg), false);/*写入数据*/
    APP_ERROR_CHECK(err_code_tx);/*检查写入过程成功或失败*/


   Delay_Ms(5);//硬件I2C实现数据传输需要处理时间,设置延时,避免数据传输冲突
   while (m_xfer_done == false);/*一直等待TWI总线传输完成,直到m_xfer_done置为true,跳出函数,结束写操作*/

 return 1;
}

NOTE:写MMC5603寄存器函数功能检查(示波器法)

步骤:用示波器线分别连接好SCL和GND及SDA和GND,运行写函数,观察数据传输时序波形图,如下图:

引脚连接图

传输时序:起始条件+从机地址/写位+应答位+发送的寄存器地址数据0x28+应答位+写入寄存器的数据0x24+应答位+停止条件

解析:示波器所示的传输时序符合条件,可判断写寄存器函数功能正常。

2.3.单字节读MMC5603寄存器函数

/*@brief I2C read register.*/
int MMC5603_Read_Reg(unsigned char i2c_add, unsigned char reg_add, unsigned char *data)
{
    /* i2c_add is the 7-bit i2c address of the sensor
     * reg_add is the register address to read
     * data is the first address of the buffer that need to store the register value
     * I2C operating successfully, return 1, otherwise return 0;    
     */

//先写寄存器,确定要读取的(只读)从机寄存器,此处不需要(无法)向目标寄存器写命令;

//再读寄存器,从slave目标(只读)寄存器读取数据到指定数据缓存区;

/* 步骤一: 写寄存器

    m_xfer_done = false;//TWI传输完成标志位设置为 false,表示传输未完成
    
    ret_code_t err_code_tx; /*指示TWI硬件驱动API调用过程中发送数据接口函数调用的成功或失败*/
    err_code_tx = nrf_drv_twi_tx(&m_twi, i2c_add, &reg_add, 1, false);//发送要读取的目标寄存器地址,长度为1字节
    APP_ERROR_CHECK(err_code_tx);/*检查写入过程的成功或失败*/
    
    while (m_xfer_done == false);/*一直等待TWI总线传输完成,直到m_xfer_done置为true,跳出函数,结束写操作*/

 

/* 步骤一: 读寄存器
    m_xfer_done = false;//TWI传输完成标志位设置为 false,表示传输未完
    /* 从指定地址读取一个字节*/
    ret_code_t err_code_rx = nrf_drv_twi_rx(&m_twi, i2c_add, dataBuffer, sizeof(dataBuffer));//将从指定寄存器读取的数据(一个字节)存入指针dataBuffer指向的缓存区。
    APP_ERROR_CHECK(err_code_rx);/*检查读取过程的成功或失败*/

    while (m_xfer_done == false);/*一直等待TWI总线传输完成,直到m_xfer_done置为true,跳出函数,结束读操作*/

    Delay_Ms(5);//硬件I2C实现数据传输需要处理时间,设置延时,避免数据传输冲突

    return 1;
}

2.4.多字节读MMC5603寄存器函数

int MMC5603_MultiRead_Reg(unsigned char i2c_add, unsigned char reg_add, int bytesNumber, unsigned char *dataBuffer)
{
    /* i2c_add is the 7-bit i2c address of the sensor
     * reg_add is the first register address to read
     * num is the number of the register to read    
     * data is the first address of the buffer that need to store the register value
     * I2C operating successfully, return 1, otherwise return 0;    
     */

//先写寄存器,确定要读取的(只读)从机寄存器,此处不需要(无法)向目标寄存器写命令;

//再读寄存器,从slave目标(只读)寄存器读取数据到指定数据缓存区;

/* 步骤一: 写寄存器

    m_xfer_done = false;//TWI传输完成标志位设置为 false,表示传输未完成
    
    ret_code_t err_code_tx; /*指示TWI硬件驱动API调用过程中发送数据接口函数调用的成功或失败*/
    err_code_tx = nrf_drv_twi_tx(&m_twi, i2c_add, &reg_add, 1, false);//发送要读取的目标寄存器地址,长度为1字节,将no_stop置为true,表示连续写多个寄存器,写完一个寄存器后停止。
    APP_ERROR_CHECK(err_code_tx);/*检查写入过程的成功或失败*/
    
    while (m_xfer_done == false);/*一直等待TWI总线传输完成,直到m_xfer_done置为true,跳出函数,结束写操作*/

 

/* 步骤一: 读寄存器
    m_xfer_done = false;//TWI传输完成标志位设置为 false,表示传输未完成
    /* 从指定地址读取一个字节*/
    ret_code_t err_code_rx = nrf_drv_twi_rx(&m_twi, i2c_add, dataBuffer, sizeof(dataBuffer));//将从指定寄存器读取的数据(多个字节)存入指针data指向的缓存区。读取时寄存器地址自增,读取的数据依次不断改变,存入指针data指向的缓存区。
    APP_ERROR_CHECK(err_code_rx);/*检查读取过程的成功或失败*/

    while (m_xfer_done == false);/*一直等待TWI总线传输完成,直到m_xfer_done置为true,跳出函数,结束读操作*/

    Delay_Ms(5);//硬件I2C实现数据传输需要处理时间,设置延时,避免数据传输冲突 

    return 1;
}

3.建立KEIL工程调试程序

3.1.检查产品ID,判断master与slave是否正常通信,检测单字节读取寄存器函数功能是否正常

/*********************************************************************************
* decription: Product ID check 产品ID检查
*********************************************************************************/
int MMC5603_CheckID(void)
{
    unsigned char pro_id = 0;

    /* Read register 0x39, check product ID */
    MMC5603_Read_Reg(MMC5603_7BITI2C_ADDRESS, MMC5603_REG_PRODUCTID1, &pro_id);    
    if(pro_id != MMC5603_PRODUCT_ID)
        return -1;
    
    return 1;
}

3.2.自动校准寄存器配置:采用片上自检信号对传感器进行自诊断(自我校准),检测多字节读取寄存器函数功能是否正常,

流程:校准过程排除环境中的磁场对地磁场的干扰,当地的磁偏角对磁场测量也有影响

1)读出存储在寄存器27H/28H/29H处的自检信号

2)计算自检信号阈值,80%的数据从上述寄存器中读出。

3)将阈值写入寄存器1EH、1FH和20H。

4)写入[01000001](TM_M和auto_st_en high)到内部控制寄存器0(1BH)进行自检。

5)读出设备上状态寄存器1 (18H) 的Sat_sensor位的值。

6) Sat_sensor=0,自检合格

/*********************************************************************************
* decription: Auto self-test registers configuration自动校准寄存器配置
*********************************************************************************/
void MMC5603_Auto_SelfTest_Configuration(void)
{
    int i;
    uint8_t reg_value[3];
    int16_t st_thr_data[3]={0};
    int16_t st_thr_new[3]={0};
    
    int16_t st_thd[3]={0};
    uint8_t st_thd_reg[3];        

    /* Read trim data from reg 0x27-0x29 ,Read_only:reg_value[3]={0x5C,0x78,0x6E}  */
    MMC5603_MultiRead_Reg(MMC5603_7BITI2C_ADDRESS,MMC5603_REG_ST_X_VAL,3,reg_value);
    for(i=0;i<3;i++)
    {
        st_thr_data[i] = (int16_t)(reg_value[i]-128)*32; 
        if(st_thr_data[i]<0)
            st_thr_data[i] = -st_thr_data[i];
        st_thr_new[i] = st_thr_data[i]-st_thr_data[i]/5;
        
        st_thd[i] = st_thr_new[i]/8;
        if(st_thd[i]>255)
            st_thd_reg[i] = 0xFF;
        else
            st_thd_reg[i] = (uint8_t)st_thd[i];
    }
    /* Write threshold into the reg 0x1E-0x20 */
    MMC5603_Write_Reg(MMC5603_7BITI2C_ADDRESS, MMC5603_REG_X_THD, st_thd_reg[0]);
    MMC5603_Write_Reg(MMC5603_7BITI2C_ADDRESS, MMC5603_REG_Y_THD, st_thd_reg[1]);
    MMC5603_Write_Reg(MMC5603_7BITI2C_ADDRESS, MMC5603_REG_Z_THD, st_thd_reg[2]);
    
    return;
}

/*********************************************************************************
* decription: Auto self-test 自动校准
*********************************************************************************/
int MMC5603_Auto_SelfTest(void)
{
    uint8_t reg_status = 0;
    
    /* Write 0x40 to register 0x1B, set Auto_st_en bit high */
    MMC5603_Write_Reg(MMC5603_7BITI2C_ADDRESS, MMC5603_REG_CTRL0, MMC5603_CMD_AUTO_ST_EN);
    
    /* Delay 15ms to finish the selftest process */
    Delay_Ms(15);
    
    /* Read register 0x18, check Sat_sensor bit */
    MMC5603_Read_Reg(MMC5603_7BITI2C_ADDRESS, MMC5603_REG_STATUS1, &reg_status);    
    if((reg_status&MMC5603_SAT_SENSOR))//读取自检信号指示位Sat_sensor,自检合格后保持低位,读取状态寄存器1的值按位与0x20,Sat_sensor位为0,则按位与结果为0,即自检合格,返回值为1。
    {
        return -1;
    }
    
    return 1;
}

3.3.地磁传感器数据采集主函数

int main(void)
{
    APP_ERROR_CHECK(NRF_LOG_INIT(NULL));
    NRF_LOG_DEFAULT_BACKENDS_INIT();

    NRF_LOG_INFO("\r\nTWI magsensor example started.");//利用J-LINK RTT打印调试LOG,打印数据放在RAM中
    NRF_LOG_FLUSH();//使用FLUSH函数,将存放在RAM中的LOG调试数据利用J-LINK RTT 窗口打印出来
    
      /* TWI initialization初始化. */
      twi_init();
      
    
      /* Enable the sensor使能传感器. */
      /*(1)检查产品ID:读取产品ID寄存器地址的数据,MMC5603_PRODUCT_ID=0x10*/
      /*(2)自动校准寄存器参数配置:读取自动校准设置值寄存器数据,计算校准信号阈值,然后将阈值写入自动校准阈值寄存器
      /*(3)连续测量工作模式设置:1)带宽设置; 2)ODR速率设置; 3)使能置位/复位; 根据ODR计算测量时间; 4)使能连续测量模式*/ 

      //                                              00:6.6ms          50Hz

MMC5603_Enable();    
    
    while(1)
    {        
        /*磁场矢量缓存区,单位是高斯 */
        float mag_raw_data[3] = {0.0};
        
        /* Get the MMC5603 data, unit is gauss */
        /*情况一:读取数据寄存器数据并转换为磁场,磁场=(寄存器数据-偏移量)/灵敏度*/
        /*情况二:自动切换Auto_SR和SETonly之间的工作模式*/
        /*(1)SETonly模式:如果X轴或Y轴输出超过10高斯,则切换到置位模式开始测量*/
        /*(2)Auto_SR模式:如果X轴和Y轴的输出都小于8高斯,则切换到连续模式;*/
        /*不符合(1)(2)情况则进行定期校准再开始测量(如果校准不成功则进行置位,再开始测量)*/ 

        MMC5603_GetData(mag_raw_data);    

        /*将数据寄存器转换的16bit磁场量转换为8bit的X/Y/Z三轴磁场数据*/

        magnetic_field_x = (uint8_t)mag_raw_data[0];    //unit is gauss
        magnetic_field_y = (uint8_t)mag_raw_data[1];    //unit is gauss
        magnetic_field_z = (uint8_t)mag_raw_data[2];    //unit is gauss
        
        /* 采样间隔20ms,采样率50Hz ,1s=1000ms=1000000ns*/
        Delay_Ms(20);

      //循环打印磁场数据

      char Buffer_X[100]={0};
      char Buffer_Y[100]={0};
      char Buffer_Z[100]={0};
      sprintf(Buffer_X,"%0.3f ",mag_raw_data[0]);    
      sprintf(Buffer_Y,"%0.3f ",mag_raw_data[1]);    
      sprintf(Buffer_Z,"%0.3f ",mag_raw_data[2]);                
      NRF_LOG_INFO("x1: %s gauss.",Buffer_X);
      NRF_LOG_INFO("y1: %s gauss.",Buffer_Y);
      NRF_LOG_INFO("z1: %s gauss.",Buffer_Z);
      NRF_LOG_FLUSH();    
           

       /*Shenzhen Guangdong
        Latitude纬度: 22° 32' 43.9" N
        Longitude经度: 114° 4' 5.9" E
        MA TSO LUNG
        Magnetic Declination地磁偏角: -3° 1' 
        Declination is NEGATIVE (WEST)
        Inclination: 34° 6' 
        Magnetic field strength磁场强度: 45442.3 nT=45.4423μT=0.454423Gauss*/    

     //方向角计算:方向角是X轴和Y轴读数的反正切,输出方向角与手机指南针功能对比,测量结果良好       
     float Curent_Angle=(atan2((double)mag_raw_data[1],(double)mag_raw_data[0]) * (180 / 3.14159265) + 180+地磁偏角);
     char TEST_OK[]={"OK"};
     char Buffer_Angle[100]={0};
     sprintf(Buffer_Angle,"%0.2f ",Curent_Angle);
     NRF_LOG_INFO("Buffer_Angle: %s ",Buffer_Angle);
     NRF_LOG_INFO("%s",TEST_OK);
     NRF_LOG_FLUSH();            
    }


}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值