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