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,想寻求专业人员的帮助,帮我检测一下程序配置是否正确,会的大佬,帮帮我,求求了