TI中文支持网
TI专业的中文技术问题搜集分享网站

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

Part Number:LDC1612-Q1Other 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_CH00x08  		// 通道0转换周期设置
#define	LDC1612_RCOUNT_CH10x09  		// 通道1稳定时间设置
#define LDC1612_SETTLE_CH00x10  		// 通道0稳定时间设置
#define	LDC1612_SETTLE_CH10x11  		// 通道1稳定时间设置
#define LDC1612_CLOCK_DIVIDERS  0x14		// 时钟分频器配置
#define LDC1612_STATUS0x18  		// 状态寄存器
#define LDC1612_ERROR_CONFIG  0x19  		//错误配置寄存器
#define LDC1612_CONFIG0x1A  		// 主配置寄存器
#define LDC1612_MUX_CONFIG0x1B  		// 多路复用配置
#define LDC1612_RESET_DEV0x1C			// 设备复位寄存器
#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; // 假设外部参考时钟40MHzreturn (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);//等待起始标志生成,即EV5I2C_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,想寻求专业人员的帮助,帮我检测一下程序配置是否正确,会的大佬,帮帮我,求求了

Eirwen:

已经收到了您的案例,调查需要些时间,感谢您的耐心等待。

,

Daniel:

您好

我认为您需要在初始化时进行的一项修改是最后在 LDC1612_MUX_CONFIG 之后写入 LDC1612_CONFIG。 除此之外、您是否已验证您使用的距离公式与您在工作台上观察到的内容相关? 如果通信失败、您是否探测了 I2C 线路?

赞(0)
未经允许不得转载:TI中文支持网 » LDC1612-Q1: 串口和I2C与LDC1612的通信失败
分享到: 更多 (0)

© 2026 TI中文支持网   网站地图 鲁ICP备2022002796号-1