找回密码
 立即注册
查看: 65|回复: 11

求救大佬,请支援下关于硬件I2C与HMC5883地磁传感器

[复制链接]
  • 打卡等级:初来乍到
  • 打卡总天数:2
  • 最近打卡:2025-06-27 00:27:11
已绑定手机

1

主题

4

回帖

21

积分

新手上路

积分
21
发表于 前天 23:30 | 显示全部楼层 |阅读模式
求救大佬,请支援下关于硬件I2C与HMC5883地磁传感器通讯问题,
目前使用软件I2C能够与HMC5883通讯上,但是换到硬件I2C就不行了。
要么读出的数据全是-1,要么读出的数据就是一个固定值,怎么转动传感器都没任何反应。

目前使用环境是STC8H8K64U, 淘宝买的gy271模块,24Mhz


// 硬件I2C初始化
void I2C_Init(void) {
  P1M1 |= 0x30;  // P1.4(SDA), P1.5(SCL)开漏
  P1M0 |= 0x30;
    P_SW2 = 0x80;

    I2CCFG = 0xe0;                              //使能I2C主机模式
    I2CMSST = 0x00;
        

}
// I2C总线等待
void i2c_Wait(void)
{
    while (!(I2CMSST & 0x40));
    I2CMSST &= ~0x40;

}
//发送START命令
void i2c_Start()
{
    I2CMSCR = 0x01;                             
    i2c_Wait();
}

void i2c_SendData(char dat)
{
    I2CTXD = dat;                               //写数据到数据缓冲区
    I2CMSCR = 0x02;                             //发送SEND命令
    i2c_Wait();
    i2c_SendACK();                       //发送ACK信号
}
//发送读ACK命令
void i2c_RecvACK()
{
    I2CMSCR = 0x03;                             //发送读ACK命令
    i2c_Wait();
}

char i2c_RecvData()
{
    I2CMSCR = 0x04;                             //发送RECV命令
    i2c_Wait();
    return I2CRXD;
}

void i2c_SendACK()
{
    I2CMSST = 0x00;                             //设置ACK信号
    I2CMSCR = 0x05;                             //发送ACK命令
    i2c_Wait();
}

void i2c_SendNAK()
{
    I2CMSST = 0x01;                             //设置NAK信号
    I2CMSCR = 0x05;                             //发送ACK命令
    i2c_Wait();
}

void i2c_Stop()
{
    I2CMSCR = 0x06;                             //发送STOP命令
    i2c_Wait();
}


//单字节写入
void i2c_Single_Write(u8 SlaveAddress, u8 REG_Address, u8 REG_data)
{
    i2c_Start();                                        //起始信号
    i2c_SendData(SlaveAddress);        //发送设备地址+写信号
    i2c_SendData(REG_Address);                //写寄存器地址
    i2c_SendData(REG_data);                //写寄存器数据
    i2c_Stop();                                                //发送停止信号
}

//单字节读取
u8 i2c_Single_Read(u8 SlaveAddress, u8 REG_Address)
{  
        uchar REG_data;
    i2c_Start();                          //起始信号
    i2c_SendData(SlaveAddress);           //发送设备地址+写信号
    i2c_SendData(REG_Address);            //写寄存器地址
                i2c_Stop();
    i2c_Start();                          //起始信号
    i2c_SendData(SlaveAddress + 1);         //发送设备地址+读信号
    REG_data = i2c_RecvData();              //读出寄存器数据
    i2c_SendNAK();                       //发送NAK信号
        i2c_Stop();                           //停止信号
    return REG_data;
}

//初始化HMC5883
void HMC5883L_Init(void)
{
          i2c_Single_Write(0x3C,0x00,0x70);
          i2c_Single_Write(0x3C,0x01,0xA0);        
  i2c_Single_Write(0x3C,0x02,0x00);

}
u8 HMC5883L_Update(void)
{
        u16 x,y;
        u8 i,datas[6];
        i2c_Start();
    i2c_SendData(0x3C); // 设备地址 + 写

    i2c_SendData(0x03); // 寄存器地址
i2c_Stop();
    i2c_Start(); // 重新发送起始信号
    i2c_SendData(0x3c+1); // 设备地址 + 读

    for (i = 0; i < 5; i++) {
        datas = i2c_RecvData(); // 读取数据
        if(i==6) {
            i2c_SendNAK(); // 最后一个字节发送NAK
        } else {
            i2c_SendACK(); // 其他字节发送ACK
        }
    }
    i2c_Stop(); // 停止传输
        x = (int16_t)((datas[0] << 8) | datas[1]);        
        y = (int16_t)((datas[4] << 8) | datas[5]);        
        printf("HMC5883_X:%d Y:%d \r\n",(int)x,(int)y);
        if(x==0 && y==0){ return;}
        m_Heading = (u8)((atan2((float)y,(float)x)*180.0/3.14159)+360)%360;
        //I2C_Data_Received();
        return m_Heading;
}


回复

使用道具 举报 送花

  • 打卡等级:常住居民III
  • 打卡总天数:111
  • 最近打卡:2025-06-28 08:54:08

740

主题

1万

回帖

1万

积分

管理员

积分
17147
发表于 昨天 13:39 | 显示全部楼层
截图202506271328085557.jpg


截图202506271329289620.jpg

截图202506271332066177.jpg

截图202506271336358674.jpg

截图202506271339161698.jpg



//<<AICUBE_USER_HEADER_REMARK_BEGIN>>
////////////////////////////////////////
// 在此添加用户文件头说明信息  
// 文件名称: main.c
// 文件描述:
// 文件版本: V1.0
// 修改记录:
//   1. (2025-06-27) 创建文件
////////////////////////////////////////
//<<AICUBE_USER_HEADER_REMARK_END>>

#include "config.h" //默认已包含stdio.h、intrins.h、ai_usb.h等头文件

//<<AICUBE_USER_INCLUDE_BEGIN>>
// 在此添加用户头文件包含  
//<<AICUBE_USER_INCLUDE_END>>

//<<AICUBE_USER_GLOBAL_DEFINE_BEGIN>>
// 在此添加用户全局变量定义、用户宏定义以及函数声明  
//<<AICUBE_USER_GLOBAL_DEFINE_END>>


BOOL fI2CMasterBusy;                    //I2C主模式忙标志


////////////////////////////////////////
// 项目主函数
// 入口参数: 无
// 函数返回: 无
////////////////////////////////////////
void main(void)
{
    //<<AICUBE_USER_MAIN_INITIAL_BEGIN>>
    // 在此添加用户主函数初始化代码  
    //<<AICUBE_USER_MAIN_INITIAL_END>>

    SYS_Init();

    //<<AICUBE_USER_MAIN_CODE_BEGIN>>
    // 在此添加主函数中运行一次的用户代码  
    //<<AICUBE_USER_MAIN_CODE_END>>

    while (1)
    {
        //<<AICUBE_USER_MAIN_LOOP_BEGIN>>
        // 在此添加主函数中用户主循环代码  
        //<<AICUBE_USER_MAIN_LOOP_END>>
    }
}

////////////////////////////////////////
// 系统初始化函数
// 入口参数: 无
// 函数返回: 无
////////////////////////////////////////
void SYS_Init(void)
{
    EnableAccessXFR();                  //使能访问扩展XFR
    IAP_SetTimeBase();                  //设置IAP等待参数,产生1us时基

    P0M0 = 0x00; P0M1 = 0x00;           //初始化P0口为准双向口模式
    P1M0 = 0x00; P1M1 = 0x00;           //初始化P1口为准双向口模式
    P2M0 = 0x00; P2M1 = 0x00;           //初始化P2口为准双向口模式
    P3M0 = 0x00; P3M1 = 0x00;           //初始化P3口为准双向口模式
    P4M0 = 0x00; P4M1 = 0x00;           //初始化P4口为准双向口模式
    P5M0 = 0x00; P5M1 = 0x00;           //初始化P5口为准双向口模式
    P6M0 = 0x00; P6M1 = 0x00;           //初始化P6口为准双向口模式
    P7M0 = 0x00; P7M1 = 0x00;           //初始化P7口为准双向口模式

    CLK_Init();                         //时钟模块初始化
    PORT2_Init();                       //P2口初始化
    I2C_Init();                         //I2C初始化

    //<<AICUBE_USER_INITIAL_CODE_BEGIN>>
    // 在此添加用户初始化代码  
    //<<AICUBE_USER_INITIAL_CODE_END>>

    EnableGlobalInt();                  //使能全局中断
}

////////////////////////////////////////
// 微秒延时函数
// 入口参数: us (设置延时的微秒值)
// 函数返回: 无
////////////////////////////////////////
void delay_us(uint16_t us)
{
    do
    {
        NOP(30);                        //(MAIN_Fosc + 500000) / 1000000 - 10
    } while (--us);
}


////////////////////////////////////////
// 毫秒延时函数
// 入口参数: ms (设置延时的毫秒值)
// 函数返回: 无
////////////////////////////////////////
void delay_ms(uint16_t ms)
{
    uint16_t i;

    do
    {
        i = MAIN_Fosc / 10000;
        while (--i);
    } while (--ms);
}

////////////////////////////////////////
// I2C中断服务程序
// 入口参数: 无
// 函数返回: 无
////////////////////////////////////////
void I2C_ISR(void) interrupt I2C_VECTOR
{
    //<<AICUBE_USER_I2C_ISR_CODE1_BEGIN>>
    // 在此添加中断函数用户代码  
    if (I2C_CheckMasterFlag())          //判断I2C主机中断
    {
        I2C_ClearMasterFlag();          //清除I2C主机中断标志
        fI2CMasterBusy = 0;             //清除主机模式忙标志位
    }
    //<<AICUBE_USER_I2C_ISR_CODE1_END>>
}


////////////////////////////////////////
// 时钟初始化函数
// 入口参数: 无
// 函数返回: 无
void CLK_Init(void)
{
    CLK_SYSCLK_Divider(10);             //切换主时钟前先将系统时钟降频

    HIRC_40M();                         //选择内部预置的频率

    CLK_MCLK_HIRC();                    //选择内部高精度HIRC作为主时钟
    CLK_MCLK2_BYPASS();                 //旁路MCLK2,直接使用MCLK选择

    CLK_SYSCLK_Divider(1);              //设置系统时钟分频系数

    //<<AICUBE_USER_CLOCK_INITIAL_BEGIN>>
    // 在此添加用户初始化代码  
    //<<AICUBE_USER_CLOCK_INITIAL_END>>
}

////////////////////////////////////////
// P2口初始化函数
// 入口参数: 无
// 函数返回: 无
////////////////////////////////////////
void PORT2_Init(void)
{
    SetP2nQuasiMode(BIT_ALL);           //设置P2为准双向口模式

    EnableP2nPullUp(0x30);              //使能P2.5,P2.4内部上拉电阻
    DisableP2nPullUp(0xcf);             //关闭P2.7,P2.6,P2.3,P2.2,P2.1,P2.0内部上拉电阻
    DisableP2nSchmitt(BIT_ALL);         //使能P2施密特触发
    SetP2nSlewRateNormal(BIT_ALL);      //设置P2一般翻转速度
    SetP2nDrivingNormal(BIT_ALL);       //设置P2一般驱动能力
    SetP2nDigitalInput(BIT_ALL);        //使能P2数字信号输入功能

    //<<AICUBE_USER_PORT2_INITIAL_BEGIN>>
    // 在此添加用户初始化代码  
    //<<AICUBE_USER_PORT2_INITIAL_END>>
}

////////////////////////////////////////
// I2C初始化函数
// 入口参数: 无
// 函数返回: 无
////////////////////////////////////////
void I2C_Init(void)
{
    I2C_SwitchP2425();                  //选择I2C数据口: SCL(P2.5), SDA(P2.4)

    I2C_MasterMode();                   //设置I2C为主机模式
    I2C_SetClockDivider(20);            //设置I2C为主机模式时钟

    I2C_SetIntPriority(3);              //设置中断为最高优先级
    I2C_EnableMasterInt();              //设置I2C主机模式中断
    fI2CMasterBusy = 0;                 //清除主机模式忙标志位
    DMA_I2C_EnableACKErrorInt();        //使能I2C接收ACK错误中断

    I2C_Enable();                       //使能I2C功能

    //<<AICUBE_USER_I2C_INITIAL_BEGIN>>
    // 在此添加用户初始化代码  
    //<<AICUBE_USER_I2C_INITIAL_END>>
}

////////////////////////////////////////
// 主机模式等待命令完成
// 入口参数: 无
// 函数返回: 无
////////////////////////////////////////
void I2C_MasterWait(void)
{
    while (fI2CMasterBusy);             //等待忙标志为0
}

////////////////////////////////////////
// 主机模式发送起始信号
// 入口参数: 无
// 函数返回: 无
////////////////////////////////////////
void I2C_MasterStart(void)
{
    I2C_Start();                        //触发主机模式起始命令
    fI2CMasterBusy = 1;                 //设置忙标志
    I2C_MasterWait();                   //等待命令完成
}

////////////////////////////////////////
// 主机模式发送停止信号
// 入口参数: 无
// 函数返回: 无
////////////////////////////////////////
void I2C_MasterStop(void)
{
    I2C_Stop();                         //触发主机模式停止命令
    fI2CMasterBusy = 1;                 //设置忙标志
    I2C_MasterWait();                   //等待命令完成
}

////////////////////////////////////////
// 主机模式发送1字节数据
// 入口参数: dat (待发送的字节数据)
// 函数返回: 0   (接收的应答信号为ACK)
//           1   (接收的应答信号为NAK)
////////////////////////////////////////
BOOL I2C_MasterSendByte(uint8_t dat)
{
    I2C_WriteData(dat);                 //将数据写入I2C数据寄存器
    I2C_SendData();                     //触发主机模式写数据命令
    fI2CMasterBusy = 1;                 //设置忙标志
    I2C_MasterWait();                   //等待命令完成
    I2C_RecvACK();                      //触发主机模式接收应答命令
    fI2CMasterBusy = 1;                 //设置忙标志
    I2C_MasterWait();                   //等待命令完成

    return I2C_MasterReadACK();         //读取并返回应答信号
}

////////////////////////////////////////
// 主机模式接收1字节数据
// 入口参数: ack (待发送的应答信号)
// 函数返回:     (接收的字节数据)
////////////////////////////////////////
uint8_t I2C_MasterReadByte(BOOL ack)
{
    uint8_t dat;

    I2C_RecvData();                     //触发主机模式读数据命令
    fI2CMasterBusy = 1;                 //设置忙标志
    I2C_MasterWait();                   //等待命令完成
    dat = I2C_ReadData();               //读取接收的数据
    if (!ack)
        I2C_MasterSetACK();             //将ACK数据写入寄存器
    else
        I2C_MasterSetNAK();             //将NAK数据写入寄存器
    I2C_SendACK();                      //触发主机模式发送应答命令
    fI2CMasterBusy = 1;                 //设置忙标志
    I2C_MasterWait();                   //等待命令完成

    return dat;                         //返回接收的数据
}


//<<AICUBE_USER_FUNCTION_IMPLEMENT_BEGIN>>
// 在此添加用户函数实现代码  
//<<AICUBE_USER_FUNCTION_IMPLEMENT_END>>



回复 支持 反对

使用道具 举报 送花

  • 打卡等级:初来乍到
  • 打卡总天数:2
  • 最近打卡:2025-06-27 00:27:11
已绑定手机

1

主题

4

回帖

21

积分

新手上路

积分
21
发表于 前天 23:31 | 显示全部楼层
P0M0 = 0; P0M1 = 0;
        P1M0 = 0; P1M1 = 0;
        P2M0 = 0; P2M1 = 0;
        P3M0 = 0; P3M1 = 0;
        P4M0 = 0; P4M1 = 0;
        P5M0 = 0; P5M1 = 0;

// 初始化外设
        uart1_config();
        uart1_SendString("uart1 Inital....\r\n");
        I2C_Init();
        uart1_SendString("I2C Inital....\r\n");
        HMC5883L_Init();
        uart1_SendString("HMC5883L Inital....\r\n");
       
        while (1) {
                heading = HMC5883L_Update();
                printf("heading:%d\r\n",heading);
                delay_ms(500);
        }
主程序就测试了模块,开了串口
回复 支持 反对

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:480
  • 最近打卡:2025-06-28 00:00:59
已绑定手机

80

主题

5214

回帖

9305

积分

超级版主

DebugLab

积分
9305
发表于 昨天 09:41 | 显示全部楼层
检查IO模式、IO切换、SCL频率,使用逻辑分析仪测量,对比软件I2C和硬件I2C的信号有什么不同
没用过HMC5883,几个I2C程序供参考:
https://www.stcaimcu.com/thread-4612-1-1.html
https://www.stcaimcu.com/thread-4613-1-1.html
https://www.stcaimcu.com/thread-4698-1-1.html
DebugLab
回复 支持 反对

使用道具 举报 送花

  • 打卡等级:常住居民III
  • 打卡总天数:111
  • 最近打卡:2025-06-28 08:54:08

740

主题

1万

回帖

1万

积分

管理员

积分
17147
发表于 昨天 10:09 | 显示全部楼层
硬件 I2C 的程序,可如下自动产生


截图202506271009255129.jpg



https://www.stcaimcu.com/forum.p ... 8574&pid=171767

回复 支持 反对

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:578
  • 最近打卡:2025-06-28 14:10:41

116

主题

2038

回帖

5889

积分

论坛元老

积分
5889
发表于 昨天 10:37 | 显示全部楼层
硬件iic可以修改速率吗?我觉得可以尝试把速率降低一下试试

截图202506271340199092.jpg

点评

可以  发表于 昨天 13:41
回复 支持 反对

使用道具 举报 送花

  • 打卡等级:初来乍到
  • 打卡总天数:2
  • 最近打卡:2025-06-27 00:27:11
已绑定手机

1

主题

4

回帖

21

积分

新手上路

积分
21
发表于 昨天 10:40 来自手机 | 显示全部楼层
神农鼎 发表于 2025-6-27 10:09
硬件 I2C 的程序,可如下自动产生



嗐,就是想着以后方便移植才没整那个自动配置软件。毕竟他之前的单片机都配置不了
回复 支持 反对

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:471
  • 最近打卡:2025-06-27 10:50:20

33

主题

2437

回帖

5186

积分

论坛元老

积分
5186
发表于 昨天 10:50 | 显示全部楼层
alanz*** 发表于 2025-6-27 10:40
嗐,就是想着以后方便移植才没整那个自动配置软件。毕竟他之前的单片机都配置不了 ...

你可以看看生成的程序,和你自己的程序的区别,,,没准就解决问题了
参考例程并不是对技术参 考手册的补充,而是对技术参 考手册的解释。
技术参 考手册不应该需要参考例程作为补充,而是解释成了参考例程的样子
回复 支持 反对

使用道具 举报 送花

  • 打卡等级:初来乍到
  • 打卡总天数:2
  • 最近打卡:2025-06-27 00:27:11
已绑定手机

1

主题

4

回帖

21

积分

新手上路

积分
21
发表于 昨天 11:40 来自手机 | 显示全部楼层
_奶咖君_ 发表于 2025-6-27 10:50
你可以看看生成的程序,和你自己的程序的区别,,,没准就解决问题了


嗐。烦的这是在这,
这个硬件i2c就是看了isp软件的示范程序,对这stc8h的手册搞的。
结果就是不行。
deepseek都开了4个对话了,它说stc的寄存器配置贼鸡儿麻烦,让我去用库

给我整无语也是。
硬件spi,adc,pwm这些基本按照历程研究下手册问问deepseek都搞定了。
唯独给卡这i2c了。
回复 支持 反对

使用道具 举报 送花

  • 打卡等级:常住居民III
  • 打卡总天数:111
  • 最近打卡:2025-06-28 08:54:08

740

主题

1万

回帖

1万

积分

管理员

积分
17147
发表于 昨天 13:24 | 显示全部楼层

有 楼主用的 STC8H8K64U 系列


截图202506271323468581.jpg
回复 支持 反对

使用道具 举报 送花

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

本版积分规则

QQ|手机版|小黑屋|深圳国芯人工智能有限公司 ( 粤ICP备2022108929号-2 )

GMT+8, 2025-6-28 23:45 , Processed in 0.212949 second(s), 99 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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