求救大佬,请支援下关于硬件I2C与HMC5883地磁传感器
求救大佬,请支援下关于硬件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;
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 << 8) | datas);
y = (int16_t)((datas << 8) | datas);
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;
}
//<<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>>
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);
}
主程序就测试了模块,开了串口 检查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
硬件 I2C 的程序,可如下自动产生
https://www.stcaimcu.com/forum.php?mod=redirect&goto=findpost&ptid=18574&pid=171767
硬件iic可以修改速率吗?我觉得可以尝试把速率降低一下试试
神农鼎 发表于 2025-6-27 10:09
硬件 I2C 的程序,可如下自动产生
嗐,就是想着以后方便移植才没整那个自动配置软件。毕竟他之前的单片机都配置不了 alanzhuzhu 发表于 2025-6-27 10:40
嗐,就是想着以后方便移植才没整那个自动配置软件。毕竟他之前的单片机都配置不了 ...
你可以看看生成的程序,和你自己的程序的区别,,,没准就解决问题了 _奶咖君_ 发表于 2025-6-27 10:50
你可以看看生成的程序,和你自己的程序的区别,,,没准就解决问题了
{:4_167:}
嗐。烦的这是在这,
这个硬件i2c就是看了isp软件的示范程序,对这stc8h的手册搞的。
结果就是不行。
deepseek都开了4个对话了,它说stc的寄存器配置贼鸡儿麻烦,让我去用库
{:4_167:}{:4_167:}{:4_167:}
给我整无语也是。
硬件spi,adc,pwm这些基本按照历程研究下手册问问deepseek都搞定了。
唯独给卡这i2c了。
有 楼主用的 STC8H8K64U 系列
页:
[1]
2