This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

LDC1612-Q1: 串口和I2C与LDC1612的通信失败

Part Number: LDC1612-Q1
Other Parts Discussed in Thread: LDC1612

/* ldc1612.h */
#ifndef __LDC1612_H
#define __LDC1612_H

/* LDC1612 I2C地址(7位地址左移1位) */
#define LDC1612_ADDR 0x2A  // 0x2A为器件默认地址

/* 寄存器地址定义 */
#define LDC1612_DEVICE_ID 0x7F              //ID寄存器

#define LDC1612_DATA_MSB_CH0  0x00  		// 通道0数据高16位
#define LDC1612_DATA_LSB_CH0  0x01  		// 通道0数据低16位
#define	LDC1612_DATA_MSB_CH1  0x02          // 通道1数据高16位
#define	LDC1612_DATA_LSB_CH1  0x03          // 通道1数据高16位
#define LDC1612_RCOUNT_CH0    0x08  		// 通道0转换周期设置
#define	LDC1612_RCOUNT_CH1    0x09  		// 通道1稳定时间设置
#define LDC1612_SETTLE_CH0    0x10  		// 通道0稳定时间设置
#define	LDC1612_SETTLE_CH1    0x11  		// 通道1稳定时间设置
#define LDC1612_CLOCK_DIVIDERS  0x14		// 时钟分频器配置
#define LDC1612_STATUS        0x18  		// 状态寄存器
#define LDC1612_ERROR_CONFIG  0x19  		//错误配置寄存器
#define LDC1612_CONFIG        0x1A  		// 主配置寄存器
#define LDC1612_MUX_CONFIG    0x1B  		// 多路复用配置
#define LDC1612_RESET_DEV     0x1C			// 设备复位寄存器
#define LDC1612_DRIVE_CURRENT_CH0  0x1E 	// 通道0驱动电流设置
#define	LDC1612_DRIVE_CURRENT_CH1  0x1F 	// 通道1驱动电流设置


/* 函数声明 */
void LDC1612_Init(void);                      // 初始化LDC1612
uint16_t LDC1612_ReadReg(uint8_t reg);        // 读取寄存器
void LDC1612_WriteReg(uint8_t reg, uint16_t value); // 写入寄存器
float LDC1612_GetFrequency(uint8_t channel);  // 获取频率值
float LDC1612_GetDistance(uint8_t channel);   // 计算距离值

uint16_t LDC1612_GetRegID(void);

#endif





/*LDC1612.c*/
#include "stm32f10x.h"
#include "LDC1612.h"
#include "I2C.h"
#include "Delay.h"
#include "math.h"

/**
  * @brief  初始化LDC1612传感器
  * @note   配置转换参数、时钟分频、工作模式等
  */
void LDC1612_Init(void)
{
    /* 硬件复位 */
    LDC1612_WriteReg(LDC1612_RESET_DEV, 0x8000); // 设置复位位
    Delay_ms(1);  // 短暂等待复位完成(复位位自动清除) 
    
    /* 配置通道0参数 */
    LDC1612_WriteReg(LDC1612_RCOUNT_CH0, 0xFFFF); // 设置最大转换周期(65535),提高分辨率
    LDC1612_WriteReg(LDC1612_SETTLE_CH0, 0x0400); // 设置稳定时间为1024个传感器周期
    
    /* 设置驱动电流(根据线圈参数调整) */
    LDC1612_WriteReg(LDC1612_DRIVE_CURRENT_CH0, 0xA000); // 驱动电流IDRIVE=10mA,线宽越细越需要大电流
    
    /* 时钟配置(假设外部晶振40MHz) */
    LDC1612_WriteReg(LDC1612_CLOCK_DIVIDERS, 0x0003); // CH0分频3,CH1分频3
    
    /* 错误检测配置 */
    LDC1612_WriteReg(LDC1612_ERROR_CONFIG, 0x0003); // 使能振幅错误检测
    
    /* 主工作模式配置 */
    LDC1612_WriteReg(LDC1612_CONFIG, 0x1001); // 连续转换模式,自动调整RP,使能传感器
    
    /* 多路复用器配置 */
    LDC1612_WriteReg(LDC1612_MUX_CONFIG, 0x0208); // 选择通道0单端输入模式
}

/**
  * @brief  从指定寄存器读取16位数据
  * @param  reg: 寄存器地址
  * @retval 读取到的16位数据
  */
uint16_t LDC1612_ReadReg(uint8_t reg)             //实际上,LDC1612中有两个寄存器,分别读取数据的高12位和低16位,最高的四位可能为0补充;
{                                                 //每一个寄存器的16位,分两次读取,每次读八位(即一个字节)
    uint8_t buf[2];
    I2C_ReadBytes(LDC1612_ADDR, reg, buf, 2);
    return (buf[0] << 8) | buf[1];
}

/**
  * @brief  向指定寄存器写入16位数据
  * @param  reg: 寄存器地址
  * @param  value: 要写入的16位数据
  */
void LDC1612_WriteReg(uint8_t reg, uint16_t value)
{
    uint8_t buf[2];
    buf[0] = value >> 8;    // 高字节
    buf[1] = value & 0xFF;  // 低字节
    I2C_WriteBytes(LDC1612_ADDR, reg, buf, 2);
}

/**
  * @brief  获取通道频率值
  * @param  channel: 通道编号(0或1)
  * @retval 计算得到的实际频率值(Hz)
  */
float LDC1612_GetFrequency(uint8_t channel)
{
    uint32_t data;
    
    /* 读取28位数据(高12位有效) */
    if(channel == 0) {
        data = ((uint32_t)LDC1612_ReadReg(LDC1612_DATA_MSB_CH0) << 16) 
               | LDC1612_ReadReg(LDC1612_DATA_LSB_CH0);
    } else {
        data = ((uint32_t)LDC1612_ReadReg(LDC1612_DATA_MSB_CH1) << 16) 
               | LDC1612_ReadReg(LDC1612_DATA_LSB_CH1);
    }
    
    /* 计算实际频率(公式见LDC1612数据手册) */
    const float f_ref = 40.0e6; // 假设外部参考时钟40MHz
    return (f_ref * 16.0) / (float)(data >> 4); // 右移4位获取有效24位数据
}

/**
  * @brief  计算金属距离(需根据实际LC参数校准)
  * @param  channel: 通道编号
  * @retval 估算距离(毫米)
  */
float LDC1612_GetDistance(uint8_t channel)
{
    /* 获取当前频率 */
    float freq = LDC1612_GetFrequency(channel);
    
    /* 基准参数(需要实际测量校准) */
    const float L = 10.88e-6;   // 线圈电感(假设10.88μH)
    const float C = 330.0e-12; // 谐振电容(假设330pF)
    const float f0 = 1.0 / (2 * 3.1415926 * sqrt(L*C)); // 理论谐振频率
    
    /* 简化距离计算模型(需根据实验数据校准) */
    float delta_f = fabs(freq - f0);
    float distance = 1.0 / (delta_f + 0.1); // 示例公式,实际需要多项式拟合
    
    return distance * 1000.0; // 转换为毫米
}

/**
  * 函    数:获取ID寄存器的值
  * 参    数:无
  * 返 回 值:指定寄存器的值
  */
uint16_t LDC1612_GetRegID(void)
{
	return LDC1612_ReadReg(LDC1612_DEVICE_ID);		
}



/*I2C.h*/
#ifndef __I2C_H
#define __I2C_H

void MyI2C_Init(void);
void I2C_WriteBytes(uint8_t devAddr, uint8_t regAddr, uint8_t *data, uint8_t len);
void I2C_ReadBytes(uint8_t devAddr, uint8_t regAddr, uint8_t *data, uint8_t len);

#endif








/*I2C.c*/
#include "stm32f10x.h"
#include "I2C.h"
#include "stm32f10x_i2c.h"
#include "USART.h"

void MyI2C_Init(void)
{
    // 使能时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);
    
    // 配置I2C引脚
	GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;            	//开漏输出
    GPIO_Init(GPIOB, &GPIO_InitStructure);
    
    // I2C配置
	I2C_InitTypeDef I2C_InitStructure;
    I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;                	//保证标准的I2C模式
    I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;        	//高速模式下的占空比配置,I2C_DutyCycle_2表示低电平占60%、高电平占40%(典型高速模式配置)。
    I2C_InitStructure.I2C_OwnAddress1 = 0x00;  					//主机模式不需要地址,从模式时设置此参数
    I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;                 //接收数据时,会向从设备发送应答
    I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;  //作用:设置I2C地址宽度为7位。
    I2C_InitStructure.I2C_ClockSpeed = 400000; // 400kHz标准模式   
    I2C_Init(I2C1, &I2C_InitStructure);
	
    I2C_Cmd(I2C1, ENABLE);
}


/**
  * 函    数:I2C等待事件
  * 参    数:I2CX,I2C事件(如起始事件)
  * 返 回 值:无
  */
void I2C_WaitEvent(I2C_TypeDef* I2Cx, uint32_t I2C_EVENT)
{
	uint32_t Timeout;
	Timeout = 10000;									//给定超时计数时间
	while (I2C_CheckEvent(I2Cx, I2C_EVENT) != SUCCESS)	//循环等待指定事件(如:起始事件标志一直没有生成则进入循环,在循环的限定时间内还没有生成,则显示错误信号,否则起始标志生成)
	{
		Timeout --;										//等待时,计数值自减
		if (Timeout == 0)								//自减到0后,等待超时
		{
			/*超时的错误处理代码,可以添加到此处*/
			
			break;										//跳出等待,不等了
		}
	}
}


/**
  * 函    数:I2C发送数据
  * 参    数:devAddr:从机地址;
  *           regAddr:从机寄存器地址;
  *           data:数据指针
  *           len:数据个数
  * 返 回 值:无
  */
void I2C_WriteBytes(uint8_t devAddr, uint8_t regAddr, uint8_t *data, uint8_t len)
{    
    I2C_GenerateSTART(I2C1, ENABLE);                            //生成起始标志,使能后为主机,通知从设备开始通信
    I2C_WaitEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT);           //等待起始标志生成,即EV5
    
    I2C_Send7bitAddress(I2C1, devAddr, I2C_Direction_Transmitter);  //发送从机地址
	I2C_WaitEvent(I2C1,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED); //等待从机地址发送成功事件  
    
    I2C_SendData(I2C1, regAddr);                                    //发送寄存器地址
	I2C_WaitEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTING);          //等待从机寄存器地址发送成功事件
    
    for(uint8_t i = 0; i < len; i++)                                //循环发送数据
	{                              
        I2C_SendData(I2C1, data[i]);
		I2C_WaitEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED);      //等待数据发送成功事件
    }
    
    I2C_GenerateSTOP(I2C1, ENABLE);                                  //发送终止事件标志
}


/**
  * 函    数:I2C接收数据
  * 参    数:devAddr:从机地址;
  *           regAddr:从机寄存器地址;
  *           data:数据指针
  *           len:数据个数
  * 返 回 值:无
  */
void I2C_ReadBytes(uint8_t devAddr, uint8_t regAddr, uint8_t *data, uint8_t len)
{
    
    // 先写入寄存器地址
    I2C_GenerateSTART(I2C1, ENABLE);                                 //起始标志
    I2C_WaitEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT);
    
    I2C_Send7bitAddress(I2C1, devAddr, I2C_Direction_Transmitter);   //从机地址
    I2C_WaitEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED);
    
    I2C_SendData(I2C1, regAddr);                                     //寄存器地址
    I2C_WaitEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED);
    
    // 重新启动以读取数据
    I2C_GenerateSTART(I2C1, ENABLE);                                 //在以上寄存器中重新开始读取数据,重新生成起始标志
    I2C_WaitEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT);
    
    I2C_Send7bitAddress(I2C1, devAddr, I2C_Direction_Receiver);      //发送从机地址
    I2C_WaitEvent(I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED);
    
    for(uint8_t i = 0; i < len; i++)                                 //从写入的数据中,四次循环读取一个32位数据
	{   
        if(i == len - 1) 
		{
            I2C_AcknowledgeConfig(I2C1, DISABLE);                    //读最后一个数据前,先将应答失能,防止错误
			I2C_GenerateSTOP(I2C1, ENABLE);                          //读最后一个数据前,提前申请停止事件;
		}
		I2C_WaitEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED);         //等待数据接收应答
        data[i] = I2C_ReceiveData(I2C1);
    }
        
    I2C_AcknowledgeConfig(I2C1, ENABLE);                            //恢复应答
}



/*USART.h*/
#ifndef __SERIAL_H
#define __SERIAL_H

#include <stdio.h>

void USART1_Init(void);
void USART1_SendByte(uint8_t Byte);
void USART1_SendArray(uint8_t *Array, uint16_t Length);
void USART1_SendString(char *String);
void USART1_SendNumber(uint32_t Number, uint8_t Length);
void USART1_Printf(char *format, ...);

uint8_t USART1_GetRxFlag(void);
uint8_t USART1_GetRxData(void);

#endif



/*USART.c*/
#include "stm32f10x.h"                  // Device header
#include <stdio.h>
#include <stdarg.h>

uint8_t USART1_RxData;		//定义串口接收的数据变量
uint8_t USART1_RxFlag;		//定义串口接收的标志位变量

/**
  * 函    数:串口初始化
  * 参    数:无
  * 返 回 值:无
  */
void USART1_Init(void)
{
	/*开启时钟*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);	//开启USART1的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);	//开启GPIOA的时钟
	
	/*GPIO初始化*/
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);					//将PA9引脚初始化为复用推挽输出
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);					//将PA10引脚初始化为上拉输入
	
	/*USART初始化*/
	USART_InitTypeDef USART_InitStructure;					//定义结构体变量
	USART_InitStructure.USART_BaudRate = 115200;				//波特率
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;	//硬件流控制,不需要
	USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;	//模式,发送模式和接收模式均选择
	USART_InitStructure.USART_Parity = USART_Parity_No;		//奇偶校验,不需要
	USART_InitStructure.USART_StopBits = USART_StopBits_1;	//停止位,选择1位
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;		//字长,选择8位
	USART_Init(USART1, &USART_InitStructure);				//将结构体变量交给USART_Init,配置USART1
	
	/*中断输出配置*/
	USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);			//开启串口接收数据的中断
	
	/*NVIC中断分组*/
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);			//配置NVIC为%E5%88%86组2
	
	/*NVIC配置*/
	NVIC_InitTypeDef NVIC_InitStructure;					//定义结构体变量
	NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;		//选择配置NVIC的USART1线
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			//指定NVIC线路使能
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;		//指定NVIC线路的抢占优先级为1
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;		//指定NVIC线路的响应优先级为1
	NVIC_Init(&NVIC_InitStructure);							//将结构体变量交给NVIC_Init,配置NVIC外设
	
	/*USART使能*/
	USART_Cmd(USART1, ENABLE);								//使能USART1,串口开始运行
}

/**
  * 函    数:串口发送一个字节(从单片机给电脑)
  * 参    数:Byte 要发送的一个字节
  * 返 回 值:无
  */
void USART1_SendByte(uint8_t Byte)
{
	USART_SendData(USART1, Byte);		//将字节数据写入数据寄存器,写入后USART自动生成时序波形
	while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);	//等待发送完成
	/*下次写入数据寄存器会自动清除发送完成标志位,故此循环后,无需清除标志位*/
}

/**
  * 函    数:串口发送一个数组
  * 参    数:Array 要发送数组的首地址
  * 参    数:Length 要发送数组的长度
  * 返 回 值:无
  */
void USART1_SendArray(uint8_t *Array, uint16_t Length)
{
	uint16_t i;
	for (i = 0; i < Length; i ++)		//遍历数组
	{
		USART1_SendByte(Array[i]);		//依次调用Serial_SendByte发送每个字节数据
	}
}

/**
  * 函    数:串口发送一个字符串
  * 参    数:String 要发送字符串的首地址
  * 返 回 值:无
  */
void USART1_SendString(char *String)
{
	uint8_t i;
	for (i = 0; String[i] != '\0'; i ++)//遍历字符数组(字符串),遇到字符串结束标志位后停止
	{
		USART1_SendByte(String[i]);		//依次调用Serial_SendByte发送每个字节数据
	}
}

/**
  * 函    数:次方函数(内部使用)
  * 返 回 值:返回值等于X的Y次方
  */
uint32_t USART1_Pow(uint32_t X, uint32_t Y)
{
	uint32_t Result = 1;	//设置结果初值为1
	while (Y --)			//执行Y次
	{
		Result *= X;		//将X累乘到结果
	}
	return Result;
}

/**
  * 函    数:串口发送数字
  * 参    数:Number 要发送的数字,范围:0~4294967295
  * 参    数:Length 要发送数字的长度,范围:0~10
  * 返 回 值:无
  */
void USART1_SendNumber(uint32_t Number, uint8_t Length)
{
	uint8_t i;
	for (i = 0; i < Length; i ++)		//根据数字长度遍历数字的每一位
	{
		USART1_SendByte(Number / USART1_Pow(10, Length - i - 1) % 10 + '0');	//依次调用Serial_SendByte发送每位数字
	}
}

/**
  * 函    数:使用printf需要重定向的底层函数
  * 参    数:保持原始格式即可,无需变动
  * 返 回 值:保持原始格式即可,无需变动
  */
int fputc(int ch, FILE *f)
{
	USART1_SendByte(ch);			//将printf的底层重定向到自己的发送字节函数
	return ch;
}

/**
  * 函    数:自己封装的prinf函数
  * 参    数:format 格式化字符串
  * 参    数:... 可变的参数列表
  * 返 回 值:无
  */
void USART1_Printf(char *format, ...)
{
	char String[100];				//定义字符数组
	va_list arg;					//定义可变参数列表数据类型的变量arg
	va_start(arg, format);			//从format开始,接收参数列表到arg变量
	vsprintf(String, format, arg);	//使用vsprintf打印格式化字符串和参数列表到字符数组中
	va_end(arg);					//结束变量arg
	USART1_SendString(String);		//串口发送字符数组(字符串)
}

/**
  * 函    数:获取串口接收标志位
  * 参    数:无
  * 返 回 值:串口接收标志位,范围:0~1,接收到数据后,标志位置1,读取后标志位自动清零
  */
uint8_t USART1_GetRxFlag(void)
{
	if (USART1_RxFlag == 1)			//如果标志位为1
	{
		USART1_RxFlag = 0;
		return 1;					//则返回1,并自动清零标志位
	}
	return 0;						//如果标志位为0,则返回0
}

/**
  * 函    数:获取串口接收的数据
  * 参    数:无
  * 返 回 值:接收的数据,范围:0~255
  */
uint8_t USART1_GetRxData(void)
{
	return USART1_RxData;			//返回接收的数据变量
}



/**
  * 函    数:USART1中断函数
  * 参    数:无
  * 返 回 值:无
  * 注意事项:此函数为中断函数,无需调用,中断触发后自动执行
  *           函数名为预留的指定名称,可以从启动文件复制
  *           请确保函数名正确,不能有任何差异,否则中断函数将不能进入
  */
void USART1_IRQHandler(void)
{
	if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET)		//判断是否是USART1的接收事件触发的中断
	{
		USART1_RxData = USART_ReceiveData(USART1);				//读取数据寄存器,存放在接收的数据变量
		USART1_RxFlag = 1;										//置接收标志位变量为1
		USART_ClearITPendingBit(USART1, USART_IT_RXNE);			//清除USART1的RXNE标志位
																//读取数据寄存器会自动清除此标志位
																//如果已经读取了数据寄存器,也可以不执行此代码
	}
}






/*main.c*/

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "I2C.h"
#include "LDC1612.h"
#include "USART.h"
#include <stdio.h>


int main(void)
{
	MyI2C_Init();
	USART1_Init();
	LDC1612_Init();
	
	while(1)
	{
//        // 读取通道0数据(28位)
//        uint16_t msb = LDC1612_ReadReg(LDC1612_DATA_MSB_CH0);
//        uint16_t lsb = LDC1612_ReadReg(LDC1612_DATA_LSB_CH0);
//        uint32_t freqCount = ((msb & 0x0FFF) << 16) | lsb;
//        
//        // 计算距离并通过串口发送
//        float distance = LDC1612_GetDistance(freqCount);    //注意:distance为28位数据
//        USART1_Printf("distance: %.2f",distance);

		uint16_t ID = LDC1612_GetRegID();
        USART1_Printf("ID: %d\r\n",ID);
		
        Delay_ms(50); // 50ms采样间隔
	}
}
	


使用STM32F103C8T6+LDC1612+LC振荡器,写的程序和制作的硬件,通过串口读出的LDC1612的ID值为2B2B,想寻求专业人员的帮助,帮我检测一下程序配置是否正确,会的大佬,帮帮我,求求了