From 5cdd7ca58c1545dec1cabb94bfd0846b4d759ca8 Mon Sep 17 00:00:00 2001 From: yelvlab Date: Sun, 17 Aug 2025 02:58:32 +0800 Subject: [PATCH] release dev branch --- .vscode/settings.json | 12 +- .vscode/tasks.json | 22 +- CMakeLists.txt | 6 + CMakePresets.json | 1 + I2C_IMPROVEMENTS.md | 139 +++++ Inc/board_config.h | 39 ++ Inc/command.h | 106 ++++ Inc/i2c.h | 127 +++++ Inc/ldc1612.h | 121 +++++ Inc/led.h | 1 + Inc/sensor_example.h | 28 + Inc/soft_i2c.h | 52 ++ Inc/systick.h | 61 +-- Inc/tmp112.h | 155 ++++++ Inc/uart.h | 10 +- Inc/uart_ring_buffer.h | 119 ++++ LDC1612_COIL_TEST_GUIDE.md | 281 ++++++++++ LDC1612_CONFIG_SEQUENCE.md | 222 ++++++++ LDC1612_Configuration_Reference.md | 149 +++++ LDC1612_USAGE_EXAMPLE.md | 178 ++++++ Src/command.c | 846 +++++++++++++++++++++++++++++ Src/gd32e23x_it.c | 14 +- Src/i2c.c | 677 +++++++++++++++++++++++ Src/ldc1612.c | 236 ++++++++ Src/led.c | 47 +- Src/main.c | 46 +- Src/sensor_example.c | 224 ++++++++ Src/soft_i2c.c | 234 ++++++++ Src/systick.c | 167 +++--- Src/tmp112.c | 323 +++++++++++ Src/uart.c | 108 ++-- Src/uart_ring_buffer.c | 104 ++++ cmake/project_config.cmake | 4 +- i2c_wait.c | 542 ++++++++++++++++++ 34 files changed, 5220 insertions(+), 181 deletions(-) create mode 100644 I2C_IMPROVEMENTS.md create mode 100644 Inc/command.h create mode 100644 Inc/i2c.h create mode 100644 Inc/ldc1612.h create mode 100644 Inc/sensor_example.h create mode 100644 Inc/soft_i2c.h create mode 100644 Inc/tmp112.h create mode 100644 Inc/uart_ring_buffer.h create mode 100644 LDC1612_COIL_TEST_GUIDE.md create mode 100644 LDC1612_CONFIG_SEQUENCE.md create mode 100644 LDC1612_Configuration_Reference.md create mode 100644 LDC1612_USAGE_EXAMPLE.md create mode 100644 Src/command.c create mode 100644 Src/i2c.c create mode 100644 Src/ldc1612.c create mode 100644 Src/sensor_example.c create mode 100644 Src/soft_i2c.c create mode 100644 Src/tmp112.c create mode 100644 Src/uart_ring_buffer.c create mode 100644 i2c_wait.c diff --git a/.vscode/settings.json b/.vscode/settings.json index e550c39..4107651 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -4,6 +4,10 @@ "Git Bash": { "path": "C:\\Program Files\\Git\\bin\\bash.exe", "icon": "terminal-bash" + }, + "Git-Bash": { + "path": "D:\\Git\\bin\\bash.exe", + "icon": "terminal-bash" } }, "terminal.integrated.defaultProfile.windows": "Git-Bash", @@ -15,6 +19,12 @@ "vcpkg.storageLocation": "C:\\Dev\\Tools\\vcpkg", "files.associations": { "*.h": "c", - "*.c": "c" + "*.c": "c", + "array": "c", + "string": "c", + "string_view": "c", + "ranges": "c", + "span": "c" }, + "cortex-debug.variableUseNaturalFormat": true, } \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 2d36195..3c31cd0 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -11,7 +11,11 @@ "Build", "Flash MCU" ], - "dependsOrder": "sequence" + "dependsOrder": "sequence", + "icon": { + "id": "insert", + "tooltip": "Build and Flash" + } }, { "label": "Flash MCU", @@ -31,6 +35,10 @@ }, "presentation": { "clear": true + }, + "icon": { + "id": "gather", + "tooltip": "Flash MCU" } }, { @@ -51,6 +59,10 @@ }, "presentation": { "clear": true + }, + "icon": { + "id": "discard", + "tooltip": "Reset MCU" } }, { @@ -71,6 +83,10 @@ }, "presentation": { "clear": true + }, + "icon": { + "id": "clear-all", + "tooltip": "Erase MCU" } }, { @@ -119,6 +135,10 @@ }, "presentation": { "clear": true + }, + "icon": { + "id": "code", + "tooltip": "Build" } } ] diff --git a/CMakeLists.txt b/CMakeLists.txt index 9c57be0..3283057 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,6 +27,12 @@ set(TARGET_SRC # Add new source files here Src/uart.c Src/led.c + Src/uart_ring_buffer.c + Src/command.c + Src/i2c.c + Src/ldc1612.c + # Src/tmp112.c + # Src/sensor_example.c ) # 设置输出目录 diff --git a/CMakePresets.json b/CMakePresets.json index 1965b92..d8926a8 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -12,6 +12,7 @@ "type": "FILEPATH", "value": "${sourceDir}/cmake/arm-none-eabi-gcc.cmake" } + ,"CMAKE_EXPORT_COMPILE_COMMANDS": "ON" }, "architecture": { "value": "unspecified", diff --git a/I2C_IMPROVEMENTS.md b/I2C_IMPROVEMENTS.md new file mode 100644 index 0000000..644f73d --- /dev/null +++ b/I2C_IMPROVEMENTS.md @@ -0,0 +1,139 @@ +# I2C驱动改进总结 + +## 🔧 主要改进内容 + +### 1. **状态机重构** +- **原问题**: 状态机逻辑混乱,使用复杂的read_cycle变量 +- **改进方案**: + - 使用清晰的`i2c_state_t`枚举定义状态 + - 分离写入和读取的状态流程 + - 每个状态职责单一,逻辑清晰 + +```c +typedef enum { + I2C_STATE_IDLE = 0, /* 空闲状态 */ + I2C_STATE_START, /* 生成起始条件 */ + I2C_STATE_SEND_ADDRESS, /* 发送从设备地址 */ + I2C_STATE_CLEAR_ADDRESS, /* 清除地址标志 */ + I2C_STATE_TRANSMIT_REG, /* 发送寄存器地址 */ + I2C_STATE_TRANSMIT_DATA, /* 发送数据 */ + I2C_STATE_RESTART, /* 生成重启条件 */ + I2C_STATE_RECEIVE_DATA, /* 接收数据 */ + I2C_STATE_STOP, /* 生成停止条件 */ + I2C_STATE_ERROR /* 错误状态 */ +} i2c_state_t; +``` + +### 2. **错误处理改进** +- **原问题**: 函数总是返回成功,无法区分错误类型 +- **改进方案**: + - 定义详细的状态码枚举 + - 添加参数验证 + - 实现重试机制 + +```c +typedef enum { + I2C_STATUS_SUCCESS = 0, /* 操作成功 */ + I2C_STATUS_TIMEOUT, /* 超时 */ + I2C_STATUS_NACK, /* 无应答 */ + I2C_STATUS_BUS_BUSY, /* 总线忙 */ + I2C_STATUS_ERROR, /* 一般错误 */ + I2C_STATUS_INVALID_PARAM /* 无效参数 */ +} i2c_status_t; +``` + +### 3. **超时处理优化** +- **原问题**: 超时后无限循环重试 +- **改进方案**: + - 限制最大重试次数 (`I2C_MAX_RETRY = 3`) + - 超时后进入错误状态 + - 重试前添加延时 + +### 4. **总线重置完善** +- **原问题**: 总线重置不完整,可能无法恢复卡死状态 +- **改进方案**: + - 实现标准的9时钟脉冲恢复 + - 生成正确的停止条件 + - 重新配置GPIO和I2C外设 + +```c +/* 生成9个时钟脉冲释放卡死的从设备 */ +for (i = 0; i < I2C_RECOVERY_CLOCKS; i++) { + gpio_bit_reset(I2C_SCL_PORT, I2C_SCL_PIN); + delay_us(I2C_DELAY_US); + gpio_bit_set(I2C_SCL_PORT, I2C_SCL_PIN); + delay_us(I2C_DELAY_US); +} +``` + +### 5. **配置问题修复** +- **原问题**: 硬编码从设备地址0xA0 +- **改进方案**: 主机地址设为0x00,从设备地址作为参数传入 + +### 6. **代码结构优化** +- **原问题**: 状态机中有大量重复代码 +- **改进方案**: + - 统一的超时检查模式 + - 清晰的状态转换逻辑 + - 一致的错误处理流程 + +## 📋 新增功能 + +### 1. **状态字符串函数** +```c +const char* i2c_get_status_string(i2c_status_t status); +``` +用于调试时获取状态描述字符串。 + +### 2. **参数验证** +```c +if (data == NULL || slave_addr > 0x7F) { + return I2C_STATUS_INVALID_PARAM; +} +``` + +### 3. **调试信息** +使用`DEBUG_VERBOSE`宏控制调试输出。 + +## 🔍 状态机流程 + +### 写入流程: +``` +START → SEND_ADDRESS → CLEAR_ADDRESS → TRANSMIT_REG → +TRANSMIT_DATA → STOP → SUCCESS +``` + +### 读取流程: +``` +写阶段: START → SEND_ADDRESS → CLEAR_ADDRESS → TRANSMIT_REG → RESTART +读阶段: START → SEND_ADDRESS → CLEAR_ADDRESS → RECEIVE_DATA → STOP → SUCCESS +``` + +## 🚀 使用示例 + +```c +// 写入16位数据 +uint8_t write_data[2] = {0x12, 0x34}; +i2c_status_t status = i2c_write_16bits(0x48, 0x01, write_data); +if (status != I2C_STATUS_SUCCESS) { + printf("Write failed: %s\r\n", i2c_get_status_string(status)); +} + +// 读取16位数据 +uint8_t read_data[2]; +status = i2c_read_16bits(0x48, 0x01, read_data); +if (status == I2C_STATUS_SUCCESS) { + printf("Read data: 0x%02X%02X\r\n", read_data[0], read_data[1]); +} else { + printf("Read failed: %s\r\n", i2c_get_status_string(status)); +} +``` + +## 📝 注意事项 + +1. **编译选项**: 确保包含``以支持bool类型 +2. **调试输出**: 定义`DEBUG_VERBOSE`宏启用调试信息 +3. **延时函数**: 确保`delay_us()`函数可用 +4. **兼容性**: 保留了原有的函数接口以保持向后兼容 + +这些改进大大提高了I2C驱动的可靠性、可维护性和调试能力。 diff --git a/Inc/board_config.h b/Inc/board_config.h index bfd5394..4802acf 100644 --- a/Inc/board_config.h +++ b/Inc/board_config.h @@ -1,6 +1,33 @@ #ifndef BOARD_CONFIG_H #define BOARD_CONFIG_H +/* >>>>>>>>>>>>>>>>>>>>[RS485 PHY DEFINE]<<<<<<<<<<<<<<<<<<<< */ + +// #define RS485_MAX13487 // RS485 PHY : MAX13487 (AutoDir) +#undef RS485_MAX13487 // RS485 PHY : SP3487 (no AutoDir) + +/* >>>>>>>>>>>>>>>>>>>>[IIC TYPE DEFINE]<<<<<<<<<<<<<<<<<<<< */ + +// #define SOFTWARE_IIC // IIC Type : Software IIC +#undef SOFTWARE_IIC // IIC Type : Hardware IIC + +/* >>>>>>>>>>>>>>>>>>>>[DEBUG ASSERTIONS DEFINE]<<<<<<<<<<<<<<<<<<<< */ + +// #define DEBUG_VERBOSE // Debug Assertions Status : Debug Verbose Information +#undef DEBUG_VERBOSE // Debug Assertions Status : No Debug Verbose Information + +/******************************************************************************/ + +#define RCU_GPIO_I2C RCU_GPIOF +#define RCU_I2C RCU_I2C0 +#define I2C_SCL_PORT GPIOF +#define I2C_SCL_PIN GPIO_PIN_1 +#define I2C_SDA_PORT GPIOF +#define I2C_SDA_PIN GPIO_PIN_0 +#define I2C_GPIO_AF GPIO_AF_1 + +#define I2C_DEBUG_UART USART0 + /******************************************************************************/ #define LED_PORT GPIOA @@ -9,4 +36,16 @@ /******************************************************************************/ +#define RS485_RCU RCU_USART0 +#define RS485_GPIO_RCU RCU_GPIOA +#define RS485_GPIO_PORT GPIOA +#define RS485_TX_PIN GPIO_PIN_2 +#define RS485_RX_PIN GPIO_PIN_3 +#define RS485_PHY USART0 +#define RS485_BAUDRATE 115200U +#define RS485_EN_PIN GPIO_PIN_1 +#define RS485_IRQ USART0_IRQn + +/******************************************************************************/ + #endif //BOARD_CONFIG_H diff --git a/Inc/command.h b/Inc/command.h new file mode 100644 index 0000000..7ce45d0 --- /dev/null +++ b/Inc/command.h @@ -0,0 +1,106 @@ +/** + * @file command.h + * @brief 串口命令解析与处理模块接口声明。 + * @details 提供基于环形缓冲区的串口协议解析、命令处理及状态管理功能, + * 支持格式为 D5 03 LEN [cmd] CRC 的命令帧解析与响应。 + */ +#ifndef COMMAND_H +#define COMMAND_H + +#include +#include + +/** + * @defgroup Command 命令处理模块 + * @brief 串口命令解析与处理 + * @{ + */ + +/** @brief 传感器周期上报使能标志 */ +extern volatile bool g_sensor_report_enabled; + +/** + * @section Command_Protocol 协议格式 + * 接收命令帧格式: + * @code + * [0] HEADER = 0xD5 // 包头标识 + * [1] BOARD_TYPE = 0x03 // 板卡类型标识 + * [2] LEN = 数据区字节数 // 有效载荷长度 + * [3..(3+LEN-1)] 数据 // 命令数据 + * [last] CRC // 校验码(从索引1累加到len-2的低8位) + * @endcode + * + * 响应帧格式: + * @code + * [0] HEADER = 0xB5 // 响应包头 + * [1] TYPE // 响应类型(0xF0=成功,0xF1..=错误类型) + * [2] LEN // 响应数据长度 + * [3..(3+LEN-1)] 数据 // 响应数据 + * [last] CRC // 校验码 + * @endcode + * + * @section Command_Usage 使用说明 + * 1) 初始化环形缓冲区: + * @code{.c} + * uart_ring_buffer_init(); + * @endcode + * + * 2) 在主循环中调用命令处理: + * @code{.c} + * while(1) { + * command_process(); // 处理接收到的命令 + * // 其他业务逻辑 + * } + * @endcode + * + * 3) 查询传感器上报状态: + * @code{.c} + * if(get_sensor_report_enabled()) { + * // 执行传感器数据上报 + * } + * @endcode + */ + +/** + * @brief 获取传感器周期上报使能状态。 + * @return bool 上报状态。 + * @retval true 传感器周期上报已启用。 + * @retval false 传感器周期上报已禁用。 + * @ingroup Command + */ +bool get_sensor_report_enabled(void); + +/** + * @brief 设置传感器周期上报使能状态。 + * @param enabled 上报使能标志。 + * @arg true 启用传感器周期上报。 + * @arg false 禁用传感器周期上报。 + * @note 推荐通过此函数修改状态,便于后续功能扩展。 + * @ingroup Command + */ +void set_sensor_report_status(bool enabled); + +/** + * @brief 处理串口环形缓冲区中的命令数据。 + * @details 基于状态机的非阻塞协议解析器,处理完整的命令帧并自动响应。 + * 每次调用处理缓冲区中所有可用数据,遇到错误时自动重置状态机。 + * @note 使用静态变量维护解析状态,函数不可重入。 + * @warning 依赖环形缓冲区正确实现,建议在主循环中周期调用。 + * @ingroup Command + */ +void command_process(void); + +/** + * @brief 解析并处理完整的命令帧。 + * @param cmd 指向完整命令帧的缓冲区(从包头0xD5开始)。 + * @param len 命令帧总长度(字节)。 + * @note 内部函数,由 command_process() 调用,一般不直接使用。 + * @ingroup Command + */ +void handle_command(const uint8_t *cmd, uint8_t len); + +/** @} */ // end of Command group + +void eddy_current_report(void); + +#endif // COMMAND_H diff --git a/Inc/i2c.h b/Inc/i2c.h new file mode 100644 index 0000000..e810478 --- /dev/null +++ b/Inc/i2c.h @@ -0,0 +1,127 @@ +// +// Created by dell on 24-12-20. +// + +#ifndef I2C_H +#define I2C_H + +#include "gd32e23x_it.h" +#include "gd32e23x.h" +#include "systick.h" +#include +#include +#include +#include +#include + +#include "board_config.h" + +/******************************************************************************/ + +#define I2C_SPEED 100000U /* 100kHz */ +#define I2C_TIME_OUT 5000U /* 5000 loops timeout */ +#define I2C_MAX_RETRY 3U /* Maximum retry attempts */ +#define I2C_DELAY_10US 10U /* Delay in microseconds for bus reset */ +#define I2C_RECOVERY_CLOCKS 9U /* Clock pulses for bus recovery */ +#define I2C_MASTER_ADDRESS 0x00U /* Master address (not used) */ + +/* Legacy compatibility */ +#define I2C_OK 1 +#define I2C_FAIL 0 +#define I2C_END 1 + +/******************************************************************************/ + +/* I2C result enumeration */ +typedef enum { + I2C_RESULT_SUCCESS = 0, /* Operation successful */ + I2C_RESULT_TIMEOUT, /* Timeout occurred */ + I2C_RESULT_NACK, /* No acknowledge received */ + I2C_RESULT_BUS_BUSY, /* Bus is busy */ + I2C_RESULT_ERROR, /* General error */ + I2C_RESULT_INVALID_PARAM, /* Invalid parameter */ + I2C_RECOVERY_OK, + I2C_RECOVERY_SDA_STUCK_LOW, + I2C_RECOVERY_SCL_STUCK_LOW +} i2c_result_t; + +/* I2C state machine enumeration */ +typedef enum { + I2C_STATE_IDLE = 0, /* Idle state */ + I2C_STATE_START, /* Generate start condition */ + I2C_STATE_SEND_ADDRESS, /* Send slave address */ + I2C_STATE_CLEAR_ADDRESS, /* Clear address flag */ + I2C_STATE_TRANSMIT_REG, /* Transmit register address */ + I2C_STATE_TRANSMIT_DATA, /* Transmit data */ + I2C_STATE_RESTART, /* Generate restart condition */ + I2C_STATE_RECEIVE_DATA, /* Receive data */ + I2C_STATE_STOP, /* Generate stop condition */ + I2C_STATE_ERROR, /* Error state */ + I2C_STATE_END +} i2c_state_t; + +/******************************************************************************/ + + +/* Function declarations */ +/*! + \brief configure the I2C interface + \param[in] none + \param[out] none + \retval i2c_result_t +*/ +i2c_result_t i2c_config(void); + +/*! + \brief reset I2C bus with proper recovery + \param[in] none + \param[out] none + \retval i2c_result_t +*/ +i2c_result_t i2c_bus_reset(void); + +/*! + \brief scan I2C bus for devices + \param[in] none + \param[out] none + \retval none +*/ +void i2c_scan(void); + +/*! + \brief write 16-bit data to I2C device + \param[in] slave_addr: 7-bit slave address + \param[in] reg_addr: register address + \param[in] data: pointer to 2-byte data array + \param[out] none + \retval i2c_result_t +*/ +i2c_result_t i2c_write_16bits(uint8_t slave_addr, uint8_t reg_addr, uint8_t data[2]); + +/*! + \brief read 16-bit data from I2C device + \param[in] slave_addr: 7-bit slave address + \param[in] reg_addr: register address + \param[out] data: pointer to 2-byte data buffer + \retval i2c_result_t +*/ +i2c_result_t i2c_read_16bits(uint8_t slave_addr, uint8_t reg_addr, uint8_t *data); + +/*! + \brief read 16-bit data from I2C device + \param[in] slave_addr: 7-bit slave address + \param[in] reg_addr: register address + \param[out] data: pointer to 2-byte data buffer + \retval i2c_result_t +*/ +i2c_result_t i2c_read_16bits(uint8_t slave_addr, uint8_t reg_addr, uint8_t *data); + +/*! + \brief get status string for debugging + \param[in] status: i2c_result_t value + \param[out] none + \retval const char* status string +*/ +const char* i2c_get_status_string(i2c_result_t status); + +#endif //I2C_H diff --git a/Inc/ldc1612.h b/Inc/ldc1612.h new file mode 100644 index 0000000..5803a33 --- /dev/null +++ b/Inc/ldc1612.h @@ -0,0 +1,121 @@ +// +// Created by dell on 24-12-3. +// + +#ifndef LDC1612_H +#define LDC1612_H + +#include "gd32e23x_it.h" +#include "gd32e23x.h" +#include "systick.h" +#include +#include +#include +#include +#include +#include "board_config.h" +#include "soft_i2c.h" +#include "i2c.h" + +/***************************************************************************/ + +/* IIC Interface Selection */ +#ifdef SOFTWARE_IIC + #define LDC1612_IIC_WRITE_16BITS(addr, reg, data) soft_i2c_write_16bits(addr, reg, data) + #define LDC1612_IIC_READ_16BITS(addr, reg, data) soft_i2c_read_16bits(addr, reg, data) + #define LDC1612_IIC_TYPE_STR "Software IIC" +#else + #define LDC1612_IIC_WRITE_16BITS(addr, reg, data) i2c_write_16bits(addr, reg, data) + #define LDC1612_IIC_READ_16BITS(addr, reg, data) i2c_read_16bits(addr, reg, data) + #define LDC1612_IIC_TYPE_STR "Hardware IIC" +#endif + +/***************************************************************************/ + +#define LDC1612_ADDR 0x2B + +/*Register Rddr*/ +/***************************************************************************/ + +#define CONVERTION_RESULT_REG_START 0X00 +#define SET_CONVERSION_TIME_REG_START 0X08 +#define SET_CONVERSION_OFFSET_REG_START 0X0C +#define SET_LC_STABILIZE_REG_START 0X10 +#define SET_FREQ_REG_START 0X14 + +#define SENSOR_STATUS_REG 0X18 +#define ERROR_CONFIG_REG 0X19 +#define SENSOR_CONFIG_REG 0X1A +#define MUL_CONFIG_REG 0X1B +#define SENSOR_RESET_REG 0X1C +#define SET_DRIVER_CURRENT_REG 0X1E + +#define READ_MANUFACTURER_ID 0X7E +#define READ_DEVICE_ID 0X7F + +/******************************************************************************/ + +#define CHANNEL_0 0 +#define CHANNEL_1 1 + +/*************************MUX_CONFIG********************************************/ +#define LDC1612_MUX_CONFIG 0x0200 + +/***********************SENSOR_CONFIG********************************************/ + +#define LDC1612_SENSOR_CONFIG_CH0 0x1601 // + +/****************************CONVERSION_TIME************************************/ +#define LDC1612_CONVERSION_TIME_CH0 0x1000 // 0x1000=4096个时钟周期 +#define LC_STABILIZE_TIME_CH0 0x0020 // 0x0020=32个时钟周期 + +/**************************DRIVE_CURRENT****************************************/ +#define LDC1612_DRIVE_CURRENT 0x9000 //A000 + +/**************************SENSOR_CONFIG***************************************/ +#define LDC1612_SLEEP_MODE 0x2801 +/**************************OTHER_CONFIG*****************************************/ +#define LDC1612_ERROR_CONFIG 0x0000 +#define SET_CONVERSION_OFFSET_CH0 0x0000 +#define LDC1612_RESET_DEV 0x8000 //[15:0] 0b1000 0000 0000 0000 + +/******************************************************************************/ + +#define COIL_RP_KOM 7.2 +#define COIL_L_UH 33 +#define COIL_C_PF 150 +#define COIL_Q_FACTOR 35.97 +#define COIL_FREQ_HZ 2262000 + +/******************************************************************************/ + +typedef enum { + LDC1612_STATUS_SUCCESS = 0, + LDC1612_STATUS_ERROR, + LDC1612_STATUS_TIMEOUT, + LDC1612_STATUS_INVALID_PARAM, + LDC1612_STATUS_NO_COIL, + LDC1612_STATUS_UNDER_RANGE, + LDC1612_STATUS_OVER_RANGE +} ldc1612_status_t; + +/******************************************************************************/ +ldc1612_status_t ldc1612_init(void); + +ldc1612_status_t ldc1612_reset_sensor(void); + +ldc1612_status_t ldc1612_config_single_channel(uint8_t channel); + +uint16_t ldc1612_get_manufacturer_id(void); + +uint16_t ldc1612_get_deveice_id(void); + +uint32_t ldc1612_get_raw_channel_result(uint8_t channel); + +uint32_t ldc1612_parse_raw_result(uint32_t raw_result); + +uint16_t ldc1612_get_sensor_status(void); + +bool ldc1612_is_data_ready(uint8_t channel); + +#endif //LDC1612_H diff --git a/Inc/led.h b/Inc/led.h index 6177e10..7376a5e 100644 --- a/Inc/led.h +++ b/Inc/led.h @@ -8,5 +8,6 @@ void led_init(void); void led_on(void); void led_off(void); void led_toggle(void); +void led_heart_beat(void); #endif // LED_H diff --git a/Inc/sensor_example.h b/Inc/sensor_example.h new file mode 100644 index 0000000..cf9749f --- /dev/null +++ b/Inc/sensor_example.h @@ -0,0 +1,28 @@ +// +// Sensor Usage Example Header +// 传感器使用示例头文件 +// + +#ifndef SENSOR_EXAMPLE_H +#define SENSOR_EXAMPLE_H + +#include "gd32e23x.h" +#include "board_config.h" + +/*! + \brief 传感器初始化示例 + \param[in] none + \param[out] none + \retval none +*/ +void sensors_init_example(void); + +/*! + \brief 传感器读取示例 + \param[in] none + \param[out] none + \retval none +*/ +void sensors_read_example(void); + +#endif // SENSOR_EXAMPLE_H diff --git a/Inc/soft_i2c.h b/Inc/soft_i2c.h new file mode 100644 index 0000000..aab2bda --- /dev/null +++ b/Inc/soft_i2c.h @@ -0,0 +1,52 @@ +// +// Created by dell on 24-12-28. +// + +#ifndef SOFT_I2C_H +#define SOFT_I2C_H + +#include "gd32e23x_it.h" +#include "gd32e23x.h" +#include "systick.h" + +#include "board_config.h" + +/******************************************************************************/ + +#define I2C_SCL_HIGH() gpio_bit_set(I2C_SCL_PORT, I2C_SCL_PIN) +#define I2C_SCL_LOW() gpio_bit_reset(I2C_SCL_PORT, I2C_SCL_PIN) +#define I2C_SDA_HIGH() gpio_bit_set(I2C_SDA_PORT, I2C_SDA_PIN) +#define I2C_SDA_LOW() gpio_bit_reset(I2C_SDA_PORT, I2C_SDA_PIN) +#define I2C_SDA_READ() gpio_input_bit_get(I2C_SDA_PORT, I2C_SDA_PIN) + +/******************************************************************************/ + +#define SOFT_I2C_OK 1 +#define SOFT_I2C_FAIL 0 +#define SOFT_I2C_END 1 + +/******************************************************************************/ + +void soft_i2c_delay(void); + +void soft_i2c_config(void); + +void soft_i2c_start(void); + +void soft_i2c_stop(void); + +void soft_i2c_send_ack(void); + +void soft_i2c_send_nack(void); + +uint8_t soft_i2c_wait_ack(void); + +void soft_i2c_send_byte(uint8_t data); + +uint8_t soft_i2c_receive_byte(uint8_t ack); + +uint8_t soft_i2c_write_16bits(uint8_t slave_addr, uint8_t reg_addr, uint8_t data[2]); + +uint8_t soft_i2c_read_16bits(uint8_t slave_addr, uint8_t reg_addr, uint8_t *data); + +#endif //SOFT_I2C_H diff --git a/Inc/systick.h b/Inc/systick.h index 219afa3..202ecf6 100644 --- a/Inc/systick.h +++ b/Inc/systick.h @@ -1,47 +1,36 @@ -/*! - \file systick.h - \brief the header file of systick - - \version 2025-02-10, V2.4.0, demo for GD32E23x -*/ - -/* - Copyright (c) 2025, GigaDevice Semiconductor Inc. - - Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - - 1. Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - 2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - 3. Neither the name of the copyright holder nor the names of its contributors - may be used to endorse or promote products derived from this software without - specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, -INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT -NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY -OF SUCH DAMAGE. -*/ - +/** +* ************************************************************************ + * + * @file systick.h + * @author GD32 + * @brief + * + * ************************************************************************ + * @copyright Copyright (c) 2024 GD32 + * ************************************************************************ + */ #ifndef SYS_TICK_H #define SYS_TICK_H #include +/* function declarations */ /* configure systick */ void systick_config(void); + +/* delay a time in 10 microseconds */ +void delay_10us(uint32_t count); + /* delay a time in milliseconds */ void delay_ms(uint32_t count); -/* delay decrement */ + +/* decrement delay counters */ void delay_decrement(void); -#endif /* SYS_TICK_H */ +// /* delay function that doesn't interfere with SysTick interrupt */ +// void delay_ms_safe(uint32_t count); + +// /* delay a time in microseconds (safe version) */ +// void delay_us_safe(uint32_t count); + +#endif /* SYS_TICK_H */ \ No newline at end of file diff --git a/Inc/tmp112.h b/Inc/tmp112.h new file mode 100644 index 0000000..8b8b638 --- /dev/null +++ b/Inc/tmp112.h @@ -0,0 +1,155 @@ +// +// Created by dell on 24-12-20. +// TMP112A Temperature Sensor Driver Header +// + +#ifndef TMP112_H +#define TMP112_H + +#include "gd32e23x_it.h" +#include "gd32e23x.h" +#include "systick.h" +#include +#include +#include +#include +#include +#include "board_config.h" +#include "i2c.h" + +/******************************************************************************/ +/* TMP112A I2C Address */ +#define TMP112A_ADDR (0x48) // 7-bit address (ADD0=GND) + +/* Register Addresses */ +/******************************************************************************/ +#define TMP112A_TEMP_REG 0x00 // 温度寄存器 +#define TMP112A_CONFIG_REG 0x01 // 配置寄存器 +#define TMP112A_TLOW_REG 0x02 // 低温阈值寄存器 +#define TMP112A_THIGH_REG 0x03 // 高温阈值寄存器 + +/* Configuration Register Bits */ +/******************************************************************************/ +#define TMP112A_CONFIG_OS (1 << 15) // One-shot +#define TMP112A_CONFIG_R1 (1 << 14) // Converter resolution bit 1 +#define TMP112A_CONFIG_R0 (1 << 13) // Converter resolution bit 0 +#define TMP112A_CONFIG_F1 (1 << 12) // Fault queue bit 1 +#define TMP112A_CONFIG_F0 (1 << 11) // Fault queue bit 0 +#define TMP112A_CONFIG_POL (1 << 10) // Polarity +#define TMP112A_CONFIG_TM (1 << 9) // Thermostat mode +#define TMP112A_CONFIG_SD (1 << 8) // Shutdown +#define TMP112A_CONFIG_CR1 (1 << 7) // Conversion rate bit 1 +#define TMP112A_CONFIG_CR0 (1 << 6) // Conversion rate bit 0 +#define TMP112A_CONFIG_AL (1 << 5) // Alert +#define TMP112A_CONFIG_EM (1 << 4) // Extended mode + +/* Resolution Settings */ +/******************************************************************************/ +#define TMP112A_RESOLUTION_9BIT 0x0000 // 9-bit (0.5°C) +#define TMP112A_RESOLUTION_10BIT 0x2000 // 10-bit (0.25°C) +#define TMP112A_RESOLUTION_11BIT 0x4000 // 11-bit (0.125°C) +#define TMP112A_RESOLUTION_12BIT 0x6000 // 12-bit (0.0625°C) + +/* Conversion Rate Settings */ +/******************************************************************************/ +#define TMP112A_RATE_0_25HZ 0x0000 // 0.25 Hz (4s) +#define TMP112A_RATE_1HZ 0x0040 // 1 Hz (1s) +#define TMP112A_RATE_4HZ 0x0080 // 4 Hz (250ms) +#define TMP112A_RATE_8HZ 0x00C0 // 8 Hz (125ms) + +/* Default Configuration */ +/******************************************************************************/ +#define TMP112A_CONFIG_DEFAULT (TMP112A_RESOLUTION_12BIT | TMP112A_RATE_4HZ) + +/* Temperature Conversion Constants */ +/******************************************************************************/ +#define TMP112A_TEMP_RESOLUTION 0.0625f // 12-bit resolution (°C/LSB) +#define TMP112A_TEMP_MIN -55.0f // 最低温度 (°C) +#define TMP112A_TEMP_MAX 125.0f // 最高温度 (°C) + +/* Status Definitions */ +/******************************************************************************/ +typedef enum { + TMP112A_STATUS_SUCCESS = 0, + TMP112A_STATUS_ERROR, + TMP112A_STATUS_TIMEOUT, + TMP112A_STATUS_INVALID_PARAM, + TMP112A_STATUS_OUT_OF_RANGE +} tmp112a_status_t; + +typedef struct { + uint16_t raw_data; + float temperature_c; + float temperature_f; + bool alert_flag; +} tmp112a_result_t; + +/******************************************************************************/ +/* Function Declarations */ + +/*! + \brief 初始化TMP112A传感器 + \param[in] none + \param[out] none + \retval tmp112a_status_t +*/ +tmp112a_status_t tmp112a_init(void); + +/*! + \brief 配置TMP112A传感器 + \param[in] config: 配置值 + \param[out] none + \retval tmp112a_status_t +*/ +tmp112a_status_t tmp112a_config(uint16_t config); + +/*! + \brief 读取温度 + \param[in] none + \param[out] result: 结果结构体指针 + \retval tmp112a_status_t +*/ +tmp112a_status_t tmp112a_read_temperature(tmp112a_result_t *result); + +/*! + \brief 设置温度阈值 + \param[in] low_temp: 低温阈值 (°C) + \param[in] high_temp: 高温阈值 (°C) + \param[out] none + \retval tmp112a_status_t +*/ +tmp112a_status_t tmp112a_set_thresholds(float low_temp, float high_temp); + +/*! + \brief 进入关机模式 + \param[in] none + \param[out] none + \retval tmp112a_status_t +*/ +tmp112a_status_t tmp112a_shutdown(void); + +/*! + \brief 退出关机模式 + \param[in] none + \param[out] none + \retval tmp112a_status_t +*/ +tmp112a_status_t tmp112a_wakeup(void); + +/*! + \brief 单次转换 + \param[in] none + \param[out] result: 结果结构体指针 + \retval tmp112a_status_t +*/ +tmp112a_status_t tmp112a_one_shot(tmp112a_result_t *result); + +/*! + \brief 获取状态字符串 + \param[in] status: 状态码 + \param[out] none + \retval const char* 状态字符串 +*/ +const char* tmp112a_get_status_string(tmp112a_status_t status); + +#endif //TMP112_H diff --git a/Inc/uart.h b/Inc/uart.h index 01e2537..b7edabc 100644 --- a/Inc/uart.h +++ b/Inc/uart.h @@ -3,14 +3,6 @@ #include "gd32e23x.h" -typedef enum { - UART_PRINTF_USART0 = 0, - UART_PRINTF_USART1 = 1, - UART_PRINTF_BOTH = 2 -} uart_printf_port_t; - -void uart0_init(uint32_t baudrate); -void uart1_init(uint32_t baudrate); -void uart_set_printf_port(uart_printf_port_t port); +void rs485_init(void); #endif // UART_H diff --git a/Inc/uart_ring_buffer.h b/Inc/uart_ring_buffer.h new file mode 100644 index 0000000..effeb35 --- /dev/null +++ b/Inc/uart_ring_buffer.h @@ -0,0 +1,119 @@ +/** + * @file uart_ring_buffer.h + * @brief 简单高效的环形接收缓冲区(字节队列)接口声明。 + * @details 提供字节写入/读取、可读长度查询、清空与丢弃统计等 API, + * 适用于中断接收(写)与主循环解析(读)的典型嵌入式串口场景。 + */ +#ifndef UART_RING_BUFFER_H +#define UART_RING_BUFFER_H + +#include +#include + +/** + * @def UART_RX_BUFFER_SIZE + * @brief 接收环形缓冲区容量(单位:字节)。 + * @note 采用“预留一格”区分空/满策略,最大可用容量为 UART_RX_BUFFER_SIZE-1。 + */ +#define UART_RX_BUFFER_SIZE 64 + +/** + * @defgroup RingBuffer 环形缓冲区 + * @brief 字节环形缓冲区(接收端) + * @{ + */ + +/** + * @section RingBuffer_Usage 使用说明 + * 典型用法:中断接收(写入环形缓冲)、主循环解析(读取环形缓冲)。 + * + * 1) 初始化 + * @code{.c} + * uart_ring_buffer_init(); + * @endcode + * + * 2) 使能串口接收非空中断(RBNE)并开启中断(以 USART0 为例) + * @code{.c} + * usart_interrupt_enable(USART0, USART_INT_RBNE); + * nvic_irq_enable(USART0_IRQn, 2, 0); // 根据工程需要设置优先级 + * @endcode + * + * 3) 在中断服务函数中写入环形缓冲(参考你当前工程的写法) + * @code{.c} + * void USART0_IRQHandler(void) { + * if (RESET != usart_interrupt_flag_get(USART0, USART_INT_FLAG_RBNE)) { + * uint8_t data = usart_data_receive(USART0); + * (void)uart_ring_buffer_put(data); // 缓冲满时丢弃并计数 + * } + * } + * @endcode + * + * 4) 在主循环中读取处理 + * @code{.c} + * while (uart_ring_buffer_available() > 0) { + * int b = uart_ring_buffer_get(); + * if (b >= 0) { + * // 处理字节 b + * } + * } + * @endcode + * + * @note 缓冲区满时新字节会被丢弃,可用 uart_ring_buffer_drop_count() 查看累计丢弃数。 + * @note 采用“预留一格”区分空/满,最大可用容量为 UART_RX_BUFFER_SIZE-1。 + */ + +/** + * @brief 初始化环形缓冲区。 + * @details 复位读/写索引与丢弃计数,准备接收数据。 + * @note 若在中断环境使用,初始化前建议关闭相关接收中断以避免并发竞争。 + * @ingroup RingBuffer + */ +void uart_ring_buffer_init(void); + +/** + * @brief 获取当前可读的字节数。 + * @details 返回范围为 [0, UART_RX_BUFFER_SIZE-1]。 + * @return 可读字节数(uint8_t)。 + * @note 预留一个空槽区分“空/满”,因此满时返回 UART_RX_BUFFER_SIZE-1。 + * @ingroup RingBuffer + */ +uint8_t uart_ring_buffer_available(void); + +/** + * @brief 从环形缓冲区读取一个字节。 + * @details 若缓冲区非空,返回队头字节并推进读指针;若为空,返回 -1。 + * @return 读取到的字节(0..255),或 -1 表示缓冲区为空。 + * @retval -1 缓冲区为空,无数据可读。 + * @ingroup RingBuffer + */ +int uart_ring_buffer_get(void); + +/** + * @brief 向环形缓冲区写入一个字节。 + * @param data 待写入的字节。 + * @return 是否写入成功。 + * @retval true 写入成功。 + * @retval false 写入失败(缓冲区已满,数据被丢弃并计数)。 + * @note 如需改为“覆盖写入”策略,可在满时先推进读指针再写入。 + * @ingroup RingBuffer + */ +bool uart_ring_buffer_put(uint8_t data); + +/** + * @brief 清空环形缓冲区。 + * @details 复位读/写索引与丢弃计数,相当于逻辑上丢弃所有已接收数据,不擦除数据区内容。 + * @ingroup RingBuffer + */ +void uart_ring_buffer_clear(void); + +/** + * @brief 获取因缓冲区满而被丢弃的字节累计数量。 + * @details 该计数在 init/clear 时清零。 + * @return 丢弃的累计字节数。 + * @ingroup RingBuffer + */ +uint32_t uart_ring_buffer_drop_count(void); + +/** @} */ + +#endif // UART_RING_BUFFER_H diff --git a/LDC1612_COIL_TEST_GUIDE.md b/LDC1612_COIL_TEST_GUIDE.md new file mode 100644 index 0000000..3f8d46e --- /dev/null +++ b/LDC1612_COIL_TEST_GUIDE.md @@ -0,0 +1,281 @@ +# LDC1612 线圈性能测试指南 + +## 概述 +这份文档详细说明如何使用M1-M9命令来测试和分析LDC1612线圈的性能特性。 + +## 测试命令总览 + +| 命令 | 功能 | 响应字节数 | 主要用途 | +|------|------|-----------|----------| +| M1 | 强制读取传感器数据 | 4 | 获取原始传感器数据 | +| M2 | 通信测试 | 4 | 验证通信链路 | +| M3 | 高电流驱动测试 | 6 | 测试提高驱动电流的效果 | +| M4 | 寄存器诊断 | 8 | 检查配置寄存器状态 | +| M5 | 最高电流启动测试 | 8 | 极限参数启动尝试 | +| M6 | 芯片功能验证 | 12 | 验证LDC1612芯片正常工作 | +| M7 | 保守参数测试 | 10 | 使用保守配置测试 | +| M8 | 极端参数测试 | 6 | 使用极端配置强制启动 | +| M9 | 多频率特性测试 | 8 | 对比不同频率配置效果 | + +--- + +## 详细命令分析 + +### M1 命令 - 强制读取传感器数据 +**命令**: `D5 03 02 4D 31 83` +**响应**: `B5 F0 04 [4字节数据] CRC` + +**响应数据解析**: +``` +[0-3]: 32位原始传感器数据(大端序) + - 高4位:错误标志(通常忽略) + - 低28位:有效传感器数据 +``` + +**使用场景**: 在您已经有工作配置的情况下,直接获取传感器数据 + +--- + +### M2 命令 - 通信测试 +**命令**: `D5 03 02 4D 32 84` +**响应**: `B5 F0 04 DD CC BB AA [CRC]` + +**分析要点**: 如果响应不是固定的`DD CC BB AA`,说明通信有问题 + +--- + +### M3 命令 - 高电流驱动测试 +**命令**: `D5 03 02 4D 33 85` +**响应**: `B5 F0 06 [6字节数据] CRC` + +**响应数据解析**: +``` +[0-1]: 传感器状态寄存器(大端序) + bit[7]: DRDY_1 - 通道1数据就绪 + bit[6]: DRDY_0 - 通道0数据就绪 + bit[5]: UNREAD_CONV - 未读转换结果 + bit[4]: ERR_ZC - 零计数错误 + bit[3]: ERR_AE - 幅度错误(重点关注) + bit[2]: ERR_WD - 看门狗超时 + bit[1]: ERR_OR - 过量程错误 + bit[0]: ERR_UR - 欠量程错误 +[2]: 数据就绪标志 (0x01=就绪, 0x00=未就绪) +[3]: 0xA0 - 高电流测试标记 +[4]: 幅度错误专用标志 (0xAE=有幅度错误, 0x00=无) +[5]: 0x33 - M3命令标记 +``` + +**分析要点**: +- 如果[0-1]从0x0008变为其他值,说明高电流有效果 +- 如果[2]变为0x01,说明数据开始就绪 +- 如果[4]变为0x00,说明幅度错误消失 + +--- + +### M4 命令 - 寄存器诊断 +**命令**: `D5 03 02 4D 34 86` +**响应**: `B5 F0 08 [8字节数据] CRC` + +**响应数据解析**: +``` +[0-1]: 状态寄存器 (0x18) - 当前传感器状态 +[2-3]: 传感器配置寄存器 (0x1A) - 传感器工作模式 + 期望值: 0x1601 (活动模式,单通道) +[4-5]: 驱动电流寄存器 (0x1E) - 当前驱动电流设置 + 常见值: 0x9000(默认), 0xA000(高), 0xF800(最高) +[6]: I2C读取状态 (0x4F='O'=成功, 0xEE=失败) +[7]: 0x44 - M4命令标记 +``` + +**分析要点**: +- [2-3]应该是0x1601,如果不是说明配置异常 +- [4-5]显示实际的驱动电流设置 +- [6]必须是0x4F,否则I2C通信有问题 + +--- + +### M5 命令 - 最高电流启动测试 +**命令**: `D5 03 02 4D 35 87` +**响应**: `B5 F0 08 [8字节数据] CRC` + +**响应数据解析**: +``` +[0-1]: 传感器状态寄存器(启动后状态) +[2]: 数据就绪标志 (0x01=就绪, 0x00=未就绪) +[3-4]: 实际驱动电流设置值(应该是0xF800) +[5]: 幅度错误专用标志 (0xAE=仍有错误, 0x00=错误消失) +[6]: 0x55 - M5命令标记 +[7]: 0xF8 - 最高电流标记 +``` + +**分析要点**: +- 这是最激进的启动尝试 +- 如果[5]仍是0xAE,说明线圈物理特性不兼容 +- [3-4]验证电流设置是否生效 + +--- + +### M6 命令 - 芯片功能验证 +**命令**: `D5 03 02 4D 36 88` +**响应**: `B5 F0 0C [12字节数据] CRC` + +**响应数据解析**: +``` +[0-1]: 写入测试值 (0x9000) +[2-3]: 读取回的值 +[4-5]: 制造商ID (应该是0x5449="TI") +[6-7]: 设备ID (应该是0x3055) +[8-9]: 当前状态寄存器 +[10]: ID读取状态 (0x4F=成功, 0xEE=失败) +[11]: 0x66 - M6命令标记 +``` + +**分析要点**: +- [4-5]必须是0x5449,确认是正品TI芯片 +- [6-7]应该是0x3055,确认是LDC1612型号 +- [10]必须是0x4F,确认芯片通信正常 + +--- + +### M7 命令 - 保守参数测试 +**命令**: `D5 03 02 4D 37 89` +**响应**: `B5 F0 0A [10字节数据] CRC` + +**响应数据解析**: +``` +[0-1]: 状态寄存器 +[2]: 数据就绪标志 +[3-4]: 实际频率分频器设置 (0x2000=较低频率) +[5]: 幅度错误标志 (0xAE=有错误, 0x00=无) +[6]: 欠量程错误标志 (0x01=有, 0x00=无) +[7]: 过量程错误标志 (0x02=有, 0x00=无) +[8]: 0x77 - M7命令标记 +[9]: 0x20 - 低频标记 +``` + +**分析要点**: +- 使用保守配置(低频率、长稳定时间) +- [6][7]指示电感值范围问题 + +--- + +### M8 命令 - 极端参数测试 +**命令**: `D5 03 02 4D 38 8A` +**响应**: `B5 F0 06 [6字节数据] CRC` + +**响应数据解析**: +``` +[0-1]: 传感器状态寄存器 +[2]: 数据就绪标志 +[3]: 幅度错误标志 (0xAE=仍有错误, 0x00=错误消失) +[4]: 0x88 - M8命令标记 +[5]: 0xEE - 极端测试标记 +``` + +**分析要点**: +- 使用极低频率(0x4000)和最大电流(0xFF00) +- 如果[3]仍是0xAE,说明线圈根本无法工作 + +--- + +### M9 命令 - 多频率特性测试 +**命令**: `D5 03 02 4D 39 8B` +**响应**: `B5 F0 08 [8字节数据] CRC` + +**响应数据解析**: +``` +[0-1]: 高频测试状态 +[2]: 高频就绪标志 (0x01=就绪, 0x00=未就绪) +[3-4]: 低频测试状态 +[5]: 低频就绪标志 (0x01=就绪, 0x00=未就绪) +[6]: 0x99 - M9命令标记 +[7]: 0xAA - 多频测试标记 +``` + +**分析要点**: +- 对比高频低电流 vs 低频高电流的效果 +- 如果某个配置工作,说明找到了合适的参数范围 + +--- + +## 线圈诊断流程 + +### 步骤1: 基础验证 +1. 执行M2确认通信正常 +2. 执行M6确认芯片功能正常 + +### 步骤2: 状态诊断 +1. 执行M4查看当前配置状态 +2. 如果状态寄存器是0x0008,说明有幅度错误 + +### 步骤3: 参数优化测试 +1. 执行M3测试高电流是否有改善 +2. 执行M5测试最高电流极限启动 +3. 执行M7测试保守参数配置 +4. 执行M8测试极端参数配置 + +### 步骤4: 特性分析 +1. 执行M9进行多频率对比测试 +2. 分析哪种配置最接近成功 + +### 步骤5: 数据读取 +1. 如果找到工作配置,执行M1获取数据 +2. 如果所有测试都失败,确认线圈不兼容 + +--- + +## 常见问题诊断 + +### 所有命令都显示幅度错误 (0x0008) +**可能原因**: +- 线圈电感值超出范围 (< 1μH 或 > 18mH) +- 线圈Q值过低 (< 5) +- 线圈物理损坏(开路或短路) +- 线圈周围有金属干扰 + +**解决方案**: +1. 用万用表测量线圈直流电阻 +2. 用LCR表测量线圈电感值和Q值 +3. 更换符合规格的线圈 + +### M6显示错误的设备ID +**可能原因**: +- 使用了非正品芯片 +- I2C地址配置错误 +- 芯片损坏 + +### 部分命令有效果但数据不稳定 +**可能原因**: +- 线圈参数在边界范围 +- 外部干扰 +- 电源噪声 + +**解决方案**: +1. 优化PCB布局 +2. 改善电源滤波 +3. 调整线圈参数 + +--- + +## 推荐线圈规格 + +``` +电感值: 10μH - 1mH +Q值: > 15 (在工作频率下) +线径: 0.1-0.5mm 漆包线 +绕制: 单层紧密绕制,避免交叉 +工作频率: 1-10MHz +直流电阻: 1-50欧姆 +``` + +--- + +## 总结 + +通过这套完整的测试命令,您可以: +1. 快速诊断线圈兼容性问题 +2. 找到最优的工作参数 +3. 验证硬件和软件功能 +4. 分析线圈的频率特性 + +如果M8和M9都显示幅度错误,强烈建议更换线圈或调整线圈参数。 diff --git a/LDC1612_CONFIG_SEQUENCE.md b/LDC1612_CONFIG_SEQUENCE.md new file mode 100644 index 0000000..92d3cd2 --- /dev/null +++ b/LDC1612_CONFIG_SEQUENCE.md @@ -0,0 +1,222 @@ +# LDC1612 正确配置顺序说明 + +## 📋 **配置顺序重要性** + +LDC1612的配置顺序**非常关键**,错误的顺序可能导致: +- 传感器无法正常工作 +- 数据不准确 +- 功耗异常 +- 系统不稳定 + +## 🔄 **完整初始化配置流程** + +### **阶段一:硬件复位和验证** + +```c +ldc1612_status_t ldc1612_complete_init(void) { + /* 1. 硬件复位 - 必须第一步 */ + ldc1612_reset_sensor(); + delay_ms(100); // 重要:等待复位完成 + + /* 2. 验证设备身份 */ + uint16_t manufacturer_id = ldc1612_get_manufacturer_id(); + uint16_t device_id = ldc1612_get_deveice_id(); + + if (manufacturer_id != 0x5449 || device_id != 0x3055) { + return LDC1612_STATUS_ERROR; + } + + /* 3. 进入配置阶段 */ + return ldc1612_config_single_channel(CHANNEL_0); +} +``` + +### **阶段二:通道配置(严格顺序)** + +#### **Step 1: 进入睡眠模式** +```c +// 配置前必须让传感器进入睡眠模式 +ldc1612_write_register(SENSOR_CONFIG_REG, LDC1612_SLEEP_MODE); +delay_ms(10); +``` +**原因**:确保传感器停止工作,避免配置冲突 + +#### **Step 2: 频率分频配置** +```c +ldc1612_set_freq_divide(channel); +delay_ms(5); +``` +**原因**: +- 这是最基础的配置,影响所有后续参数 +- 必须根据LC谐振频率正确计算 +- 公式:`f_sensor = 1/(2π√(LC))` + +#### **Step 3: LC稳定时间** +```c +ldc1612_set_LC_stabilize_time(channel, LC_STABILIZE_TIME_CH0); +``` +**原因**: +- 影响测量精度 +- 时间太短可能导致不稳定 +- 时间太长影响响应速度 + +#### **Step 4: 转换时间** +```c +ldc1612_set_conversion_time(channel, LDC1612_CONVERSION_TIME_CH0); +``` +**原因**: +- 影响测量精度和速度的平衡 +- 值越大精度越高但速度越慢 + +#### **Step 5: 转换偏移** +```c +ldc1612_set_conversion_offset(channel, 0x0000); +``` +**原因**: +- 通常设为0,除非有特殊校准需求 + +#### **Step 6: 驱动电流** +```c +ldc1612_set_drive_current(channel, LDC1612_DRIVE_CURRENT); +``` +**原因**: +- 影响传感器灵敏度和功耗 +- 电流越大灵敏度越高但功耗也越大 + +#### **Step 7: 多路复用器配置** +```c +ldc1612_set_mux_config(LDC1612_MUX_CONFIG); +``` +**原因**: +- 设置通道选择和滤波器带宽 +- 影响噪声抑制和响应速度 + +#### **Step 8: 错误配置** +```c +ldc1612_set_error_config(LDC1612_ERROR_CONFIG); +``` +**原因**: +- 配置错误输出行为 +- 通常设为0(所有错误输出启用) + +#### **Step 9: 启动传感器** +```c +ldc1612_write_register(SENSOR_CONFIG_REG, LDC1612_SENSOR_CONFIG); +delay_ms(50); +``` +**原因**: +- **必须最后一步** +- 启动传感器开始正常工作 +- 需要等待稳定时间 + +## ⚠️ **常见配置错误** + +### **1. 配置顺序错误** +```c +// ❌ 错误:先启动传感器再配置 +ldc1612_set_sensor_config(LDC1612_SENSOR_CONFIG); +ldc1612_set_freq_divide(CHANNEL_0); // 太晚了! + +// ✅ 正确:先配置后启动 +ldc1612_set_freq_divide(CHANNEL_0); +ldc1612_set_sensor_config(LDC1612_SENSOR_CONFIG); +``` + +### **2. 缺少延时** +```c +// ❌ 错误:没有等待复位完成 +ldc1612_reset_sensor(); +ldc1612_get_manufacturer_id(); // 可能读取失败 + +// ✅ 正确:适当延时 +ldc1612_reset_sensor(); +delay_ms(100); +ldc1612_get_manufacturer_id(); +``` + +### **3. 频率分频计算错误** +```c +// ❌ 错误:使用固定值 +#define FREQ_DIV_VALUE 0x1002 + +// ✅ 正确:根据LC参数计算 +sensor_freq = 1 / (2 * PI * sqrt(L * C)); +fin_div = (uint16_t)(sensor_freq / 8.75 + 1); +``` + +## 📊 **配置参数说明** + +| 参数 | 地址 | 当前值 | 说明 | +|------|------|--------|------| +| 频率分频 | 0x14 | 0x1002 | 根据LC计算得出 | +| LC稳定时间 | 0x10 | 0x001E | 30个时钟周期 | +| 转换时间 | 0x08 | 0x0546 | 1350个参考时钟 | +| 驱动电流 | 0x1E | 0x9000 | 高驱动电流 | +| 多路复用 | 0x1B | 0x020C | 单通道,3.3MHz带宽 | +| 传感器配置 | 0x1A | 0x1601 | 启用传感器,连续模式 | + +## 🔍 **验证配置是否正确** + +```c +void verify_ldc1612_config(void) { + uint16_t status = ldc1612_get_sensor_status(); + + printf("Status: 0x%04X\n", status); + + // 检查数据就绪 + if (ldc1612_is_data_ready(CHANNEL_0)) { + printf("✓ Channel 0 data ready\n"); + } + + // 检查错误 + if (status & 0xFF00) { + printf("✗ Error detected: 0x%04X\n", status); + } else { + printf("✓ No errors detected\n"); + } +} +``` + +## 📚 **TI官方建议的最佳实践** + +1. **总是先复位**:每次配置前都要复位传感器 +2. **验证设备ID**:确保通信正常 +3. **睡眠模式配置**:配置期间保持睡眠状态 +4. **频率优先**:频率分频必须最先配置 +5. **启动最后**:传感器配置必须最后设置 +6. **适当延时**:每个关键步骤后都要延时 +7. **状态检查**:配置完成后检查状态寄存器 + +## 🎯 **推荐使用方式** + +```c +// 推荐的初始化流程 +int main(void) { + // 系统初始化 + system_init(); + + // IIC初始化 + #ifdef SOFTWARE_IIC + soft_i2c_config(); + #else + i2c_config(); + #endif + + // LDC1612完整初始化 + if (ldc1612_complete_init() != LDC1612_STATUS_SUCCESS) { + printf("LDC1612 initialization failed!\n"); + while(1); + } + + printf("LDC1612 initialization success!\n"); + + // 主循环 + while (1) { + if (ldc1612_is_data_ready(CHANNEL_0)) { + uint32_t data = ldc1612_get_raw_channel_result(CHANNEL_0); + // 处理数据 + } + delay_ms(100); + } +} +``` diff --git a/LDC1612_Configuration_Reference.md b/LDC1612_Configuration_Reference.md new file mode 100644 index 0000000..25dc18d --- /dev/null +++ b/LDC1612_Configuration_Reference.md @@ -0,0 +1,149 @@ +# LDC1612 Configuration Reference Guide + +## 概述 +本文档为德州仪器 LDC1612 电感数字转换器提供全面的配置指导。LDC1612 是一款高分辨率、多通道的电感式传感应用芯片。 + +--- + +## 总测量周期配置 + +### 寄存器信息 +| 参数 | 通道0地址 | 通道1地址 | 数据位宽 | 寄存器名称 | +|------|-----------|-----------|----------|------------| +| 转换时间 | 0x08 | 0x09 | 16位 | CONVERSION_TIME_CHx | +| LC稳定时间 | 0x10 | 0x11 | 16位 | LC_STABILIZE_TIME_CHx | + +### 功能说明 +- **转换时间**: 决定ADC转换精度和数据更新率 +- **LC稳定时间**: LC振荡器稳定所需时间,影响测量精度 +- **约束条件**: LC稳定时间 < 转换时间 + +### 取值范围 +**转换时间寄存器**: +- 最小值: 0x0100 (256个时钟周期) +- 最大值: 0x1FFF (8191个时钟周期) +- 推荐范围: 0x0200 - 0x1800 + +**LC稳定时间寄存器**: +- 最小值: 0x0004 (4个时钟周期) +- 最大值: 0xFFFF (65535个时钟周期) +- 推荐范围: 0x0010 - 0x0100 + +### 计算公式 +``` +转换时间 = 转换设置值 × (1 / f_REF_CLK) +LC稳定时间 = 稳定设置值 × (1 / f_REF_CLK) +总测量时间 = LC稳定时间 + 转换时间 +数据更新率 = f_REF_CLK / (稳定设置值 + 转换设置值) + +其中: f_REF_CLK = 40MHz (LDC1612内部参考时钟) +``` + +### 常用配置档位 + +| 档位 | 转换时间设置 | LC稳定时间设置 | 总测量时间 | 数据更新率 | 适用场景 | +|------|--------------|----------------|------------|------------|----------| +| 超高速 | 0x0200 (512周期) | 0x0010 (16周期) | 13.2μs | ~75.8kSPS | 振动检测 | +| 高速 | 0x0400 (1024周期) | 0x0010 (16周期) | 26.0μs | ~38.5kSPS | 快速响应 | +| 平衡 | 0x0800 (2048周期) | 0x0020 (32周期) | 52.0μs | ~19.2kSPS | 一般应用 | +| 高精度 | 0x1000 (4096周期) | 0x0020 (32周期) | 103.2μs | ~9.7kSPS | 精密测量 | +| 超高精度 | 0x1800 (6144周期) | 0x0040 (64周期) | 155.2μs | ~6.5kSPS | 实验室级 | + +### 典型应用场景配置 + +#### 1. 振动监测/快速运动跟踪 +- **转换时间**: 0x0200 (512周期) +- **LC稳定时间**: 0x0010 (16周期) +- **性能**: ~75.8kSPS,适中精度 + +#### 2. 一般工业传感/位置检测 +- **转换时间**: 0x0800 (2048周期) +- **LC稳定时间**: 0x0020 (32周期) +- **性能**: ~19.2kSPS,平衡性能 + +#### 3. 精密位移测量/材料检测 +- **转换时间**: 0x1000 (4096周期) +- **LC稳定时间**: 0x0020 (32周期) +- **性能**: ~9.7kSPS,高精度 + +#### 4. 实验室级测量/恶劣环境 +- **转换时间**: 0x1800 (6144周期) +- **LC稳定时间**: 0x0040 (64周期) +- **性能**: ~6.5kSPS,最高稳定性 + +### 选择策略 +1. **确定应用需求**: 响应速度、精度要求、环境条件 +2. **线圈特性考虑**: Q因子越高需要更长LC稳定时间 +3. **环境因素**: 温度变化大或电磁干扰强需增加时间参数 +4. **调试优化**: 从保守设置开始,逐步优化提高响应速度 + +--- + +## 驱动电流配置 + +### 寄存器信息 +- **寄存器地址**: 0x1E (通道0), 0x1F (通道1) +- **数据位宽**: 16位 +- **功能**: 控制LC振荡器的驱动电流强度 + +### 取值范围 +- **最小值**: 0x0000 (最小驱动电流) +- **最大值**: 0xFFFF (最大驱动电流) +- **常用范围**: 0x8000 - 0xC000 +- **推荐起始值**: 0x9000 + +### 驱动电流档位 + +| 设置值 | 相对强度 | 适用场景 | 特点 | +|--------|----------|----------|------| +| 0x8000 | 低 | 小线圈、近距离检测 | 低功耗,灵敏度适中 | +| 0x9000 | 中等 | 一般应用 | 平衡性能,通用设置 | +| 0xA000 | 较高 | 大线圈、远距离检测 | 高灵敏度,功耗较高 | +| 0xC000 | 高 | 极端环境、最大灵敏度 | 最高性能,最大功耗 | + +--- + +## 传感器配置寄存器 + +### 寄存器信息 +- **寄存器地址**: 0x1A +- **数据位宽**: 16位 +- **当前设置**: 0x1A23 + +### 关键位域功能 + +| 位域 | 功能 | 当前值 | 说明 | +|------|------|--------|------| +| 位15-14 | 活动通道选择 | 00 | 通道0活动 | +| 位13 | 睡眠模式控制 | 1 | 正常工作模式 | +| 位11 | 激活序列选择 | 1 | 完整激活序列 | +| 位10 | 自动幅度调整 | 0 | 启用自动幅度调整 | +| 位9 | 时钟源选择 | 1 | 外部时钟源 | +| 位1-0 | 去毛刺滤波 | 11 | 10MHz滤波(高EMI环境) | + +### 常用配置组合 + +| 配置值 | 应用场景 | 特点 | +|--------|----------|------| +| 0x1601 | 手动模式+内部时钟 | 精确控制,稳定环境 | +| 0x1821 | 自动模式+内部时钟 | 智能调整,一般环境 | +| 0x1A21 | 自动模式+外部时钟 | 高性能,低噪声 | +| 0x1A23 | 自动模式+外部时钟+增强滤波 | 高EMI环境 | + +--- + +## 注意事项 + +- 本文档将扩展更多LDC1612寄存器配置内容 +- 所有时序计算基于40MHz内部参考时钟 +- 实际性能可能因线圈特性和环境条件而异 +- 请务必根据具体应用需求验证设置 + +--- + +## 文档历史 + +- **版本1.0** (2025-08-17): 初始转换时间配置文档 +- **版本1.1** (2025-08-17): 添加LC稳定时间配置和时序参数协调说明 +- **版本1.2** (2025-08-17): 添加驱动电流配置和传感器配置寄存器说明 +- **未来更新**: 将添加频率分频器、错误配置等更多寄存器配置说明 diff --git a/LDC1612_USAGE_EXAMPLE.md b/LDC1612_USAGE_EXAMPLE.md new file mode 100644 index 0000000..b8cfb78 --- /dev/null +++ b/LDC1612_USAGE_EXAMPLE.md @@ -0,0 +1,178 @@ +# LDC1612 驱动使用指南 + +## 📌 概述 + +LDC1612是TI公司的涡流传感器芯片,本驱动支持单通道(Channel 0)和双通道工作模式,并提供了硬件IIC和软件IIC的灵活切换。 + +## 🔧 硬件IIC与软件IIC切换 + +### 方法一:通过 board_config.h 配置(推荐) + +在 `Inc/board_config.h` 文件中修改以下配置: + +```c +/* 使用软件IIC */ +#define SOFTWARE_IIC // 启用软件IIC +// #undef SOFTWARE_IIC // 注释掉这行 + +/* 使用硬件IIC */ +// #define SOFTWARE_IIC // 注释掉这行 +#undef SOFTWARE_IIC // 使用硬件IIC +``` + +### 方法二:通过编译时定义 + +在CMakeLists.txt中添加: + +```cmake +# 使用软件IIC +target_compile_definitions(${PROJECT_NAME} PRIVATE SOFTWARE_IIC) + +# 使用硬件IIC(移除上面的定义即可) +``` + +## 📋 LDC1612 初始化序列 + +### 推荐的初始化步骤 + +```c + + + // 2. 检查传感器ID + uint16_t manufacturer_id = ldc1612_get_manufacturer_id(); + uint16_t device_id = ldc1612_get_deveice_id(); + + printf("Manufacturer ID: 0x%04X (Expected: 0x5449)\n", manufacturer_id); + printf("Device ID: 0x%04X (Expected: 0x3055)\n", device_id); + + if (manufacturer_id != 0x5449 || device_id != 0x3055) { + printf("ERROR: LDC1612 not detected!\n"); + return; + } + + // 3. 复位传感器 + ldc1612_reset_sensor(); + delay_ms(100); + + // 4. 配置单通道模式 + ldc1612_single_ch0_config(); + + // 5. 显示当前IIC类型 + printf("Current IIC Type: %s\n", ldc1612_get_iic_type()); + + printf("LDC1612 initialization completed\n"); +} +``` + +## 📊 数据读取示例 + +```c +void ldc1612_read_example(void) { + uint32_t raw_data; + uint16_t status; + + // 检查数据是否准备好 + if (ldc1612_is_data_ready(CHANNEL_0)) { + // 读取原始数据 + raw_data = ldc1612_get_raw_channel_result(CHANNEL_0); + + // 检查错误状态 + if (raw_data & 0xF0000000) { + printf("Sensor Error: 0x%08X\n", raw_data); + return; + } + + // 获取有效数据 (28位) + uint32_t sensor_data = raw_data & 0x0FFFFFFF; + printf("Channel 0 Data: %u\n", sensor_data); + + // 转换为频率值 (可选) + float frequency = (float)sensor_data * COIL_FREQ_HZ / 0x10000000; + printf("Frequency: %.2f Hz\n", frequency); + } + + // 读取状态寄存器 + status = ldc1612_get_sensor_status(); + printf("Status: 0x%04X\n", status); +} +``` + +## ⚠️ 常见错误代码 + +| 错误代码 | 说明 | +|---------|------| +| 0xF0000000 | ERR_NC - 未检测到线圈 | +| 0x80000000 | ERR_UR - 欠量程错误 | +| 0x40000000 | ERR_OR - 超量程错误 | +| 0x20000000 | ERR_WD - 看门狗超时 | +| 0x10000000 | ERR_AE - 振幅错误 | + +## 🔍 寄存器配置说明 + +### 当前配置值解析 + +```c +#define LDC1612_CONVERSION_TIME_CH0 0x0546 // 转换时间 +#define LDC1612_DRIVE_CURRENT 0x9000 // 驱动电流 +#define LDC1612_MUX_CONFIG 0x020C // 多路复用配置 +#define LDC1612_SENSOR_CONFIG_CH0 0x1601 // 传感器配置 +#define LC_STABILIZE_TIME_CH0 0x001E // LC稳定时间 +``` + +- **转换时间**: 0x0546 = 1350个参考时钟周期 +- **驱动电流**: 0x9000 = 高驱动电流设置 +- **MUX配置**: 0x020C = 无自动扫描,3.3MHz滤波带宽 +- **传感器配置**: 0x1601 = 启用传感器,连续转换模式 + +## 🛠️ 调试技巧 + +### 1. 检查IIC通信 + +```c +void debug_iic_communication(void) { + printf("=== IIC Communication Test ===\n"); + printf("IIC Type: %s\n", ldc1612_get_iic_type()); + + uint16_t manufacturer_id = ldc1612_get_manufacturer_id(); + uint16_t device_id = ldc1612_get_deveice_id(); + + printf("Manufacturer ID: 0x%04X\n", manufacturer_id); + printf("Device ID: 0x%04X\n", device_id); + + if (manufacturer_id == 0xFFFF || device_id == 0xFFFF) { + printf("ERROR: IIC communication failed!\n"); + } else if (manufacturer_id == 0x5449 && device_id == 0x3055) { + printf("SUCCESS: LDC1612 detected and communicating\n"); + } else { + printf("WARNING: Unexpected device detected\n"); + } +} +``` + +### 2. 自动检测驱动电流 + +```c +void auto_detect_drive_current(void) { + printf("=== Auto Drive Current Detection ===\n"); + ldc1612_drvie_current_detect(CHANNEL_0); +} +``` + +## 📈 性能优化建议 + +1. **硬件IIC vs 软件IIC**: + - 硬件IIC: 更快速,CPU占用率低 + - 软件IIC: 更灵活,便于调试 + +2. **频率设置优化**: + - 根据实际线圈参数调整 `COIL_L_UH` 和 `COIL_C_PF` + - 确保传感器频率在1-10MHz范围内 + +3. **电源管理**: + - 不使用时可调用 `ldc1612_set_sensor_config(LDC1612_SLEEP_MODE)` + +## 📚 参考资料 + +- TI LDC1612 数据手册 +- GD32E230 参考手册 +- 本项目IIC驱动实现: `i2c.c` 和 `soft_i2c.c` diff --git a/Src/command.c b/Src/command.c new file mode 100644 index 0000000..8c6dc46 --- /dev/null +++ b/Src/command.c @@ -0,0 +1,846 @@ +/** + * @file command.c + * @brief 串口命令解析与处理模块实现 + * @details 实现基于状态机的协议解析器,支持 D5 03 LEN [cmd] CRC 格式的命令处理, + * 包含命令帧解析、响应生成和传感器状态管理功能。 + * @author Hulk + * @date 2025-08-13 + * @version 1.0.0 + * @ingroup Command + */ + +#include "command.h" +#include "uart_ring_buffer.h" +#include "led.h" +#include +#include +#include +#include "board_config.h" +#include "gd32e23x_usart.h" +#include "ldc1612.h" + +/* ============================================================================ + * 协议格式说明 + * ============================================================================ */ + +/** + * @name 协议帧格式 + * @{ + * @details + * Host -> Device 命令帧格式: + * [0] HEADER = 0xD5 // 包头标识 + * [1] BOARD_TYPE = 0x03 // 板卡类型标识 + * [2] LEN = 数据区字节数 // 有效载荷长度 + * [3..(3+LEN-1)] 数据 // 命令数据,如 "M1", "M2S123" + * [last] CRC = 校验码 // 从索引1到(last-1)的累加和低8位 + * + * 最小协议包长度为 6 字节 + * 数据示例(两字节命令):"M1" / "M2" / "M3" + * + * Device -> Host 响应帧格式: + * [0] 0xB5 // 响应包头 + * [1] TYPE // 响应类型(0xF0=成功,0xF1..=错误类型) + * [2] LEN // 响应数据长度 + * [3..(3+LEN-1)] 数据 // 响应数据,如 "ok", "err" + * [last] CRC // 校验码(同命令帧规则) + * @} + */ + +/* ============================================================================ + * 协议常量定义 + * ============================================================================ */ + +/** @name 协议帧标识符 + * @{ */ +#define PROTOCOL_PACKAGE_HEADER 0xD5 /**< 命令帧包头标识 */ +#define PROTOCOL_BOARD_TYPE 0x03 /**< 板卡类型标识 */ +/** @} */ + +/** @name 命令长度限制 + * @{ */ +#define COMMAND_MIN_LEN 2 /**< 最小命令长度,如"M1" */ +#define PROTOCOL_MIN_FRAME_LEN (3 + COMMAND_MIN_LEN + 1) /**< 最小完整帧长度:header+type+len+payload+crc = 6 */ +#define PROTOCOL_MAX_FRAME_LEN 16 /**< 最大完整帧长度 */ +/** @} */ + +/** @name 响应帧标识符 + * @{ */ +#define RESP_HEADER 0xB5 /**< 响应帧包头标识 */ +#define RESP_TYPE_OK 0xF0 /**< 成功响应类型 */ +#define RESP_TYPE_CRC_ERR 0xF1 /**< CRC校验错误 */ +#define RESP_TYPE_HEADER_ERR 0xF2 /**< 包头错误 */ +#define RESP_TYPE_TYPE_ERR 0xF3 /**< 类型错误 */ +#define RESP_TYPE_LEN_ERR 0xF4 /**< 长度错误 */ +/** @} */ + +/* ============================================================================ + * 模块内部变量 + * ============================================================================ */ + +/** @brief 传感器周期上报使能标志 */ +volatile bool g_sensor_report_enabled = false; + +/** @name 预设响应数据 + * @{ */ +static const uint8_t s_report_status_ok[] = { 'o', 'k' }; /**< 成功响应数据 */ +static const uint8_t s_report_status_err[] = { 'e','r','r' }; /**< 错误响应数据 */ +/** @} */ + +/* ============================================================================ + * 公共接口函数 + * ============================================================================ */ + +/** + * @brief 查询是否启用周期性传感器上报。 + * @return true 表示启用;false 表示禁用。 + * @ingroup Command + */ +bool get_sensor_report_enabled(void) +{ + return g_sensor_report_enabled; +} + +/** + * @brief 设置是否启用周期性传感器上报标志。 + * @details 本模块内部保存的布尔状态,供其他逻辑决定是否进行周期性数据上报; + * 推荐通过本函数修改而非直接访问全局/静态变量,以便后续扩展(如加锁/回调)。 + * @param status true 启用周期上报;false 禁用。 + * @ingroup Command + */ +void set_sensor_report_status(bool status) +{ + g_sensor_report_enabled = status; +} + +/** + * @brief 计算协议包的 8 位累加校验值(Checksum)。 + * @details 对输入缓冲区逐字节累加并取低 8 位,累加范围为 data[1] 至 data[len-2], + * 即不包含包头 HEADER(索引 0)与尾部 CRC 字节(索引 len-1)。 + * 当 len 小于最小协议帧长度(PACKAGE_MIN_LENGTH)时返回 0。 + * @param data 指向待校验的完整协议包缓冲区。 + * @param len 缓冲区总长度(字节),应满足 header + type + len + payload + crc 的最小格式。 + * @return uint8_t 计算得到的 8 位校验值。 + * @note 本函数实现为简单求和校验(Checksum),非多项式 CRC;与本协议“从索引 1 累加到 len-2”的规则一致。 + * @ingroup Command + */ +static uint8_t command_sum_crc_calc(const uint8_t *data, uint8_t len) +{ + uint16_t crc = 0; + // 仅在满足协议最小帧长时计算(header + type + len + payload + crc) + if (len < PROTOCOL_MIN_FRAME_LEN) return 0; + + // 累加从索引 1 到 len-2 的字节(不含 header 和 crc 字节) + for (uint8_t i = 1; i < (len - 1); i++) + { + crc += data[i]; + } + return (uint8_t)(crc & 0xFF); +} + +/** + * @brief 发送协议响应帧(使用GD32E230标准库)。 + * @details 构造并发送格式为 B5 TYPE LEN [payload] CRC 的响应帧, + * 自动计算CRC校验值并通过串口输出。 + * @param type 响应类型码(如 RESP_TYPE_OK, RESP_TYPE_CRC_ERR 等)。 + * @param payload 指向响应数据的缓冲区,当len为0时可为NULL。 + * @param len 响应数据长度(字节),为0时不复制payload数据。 + * @note 内部使用固定大小缓冲区,超长响应将被丢弃。 + * @warning 使用GD32E230标准库函数发送,确保串口已正确初始化。 + * @ingroup Command + */ +static void send_response(uint8_t type, const uint8_t *payload, uint8_t len) +{ + uint8_t buf_len = (uint8_t)(3 + len + 1); + uint8_t buf[16]; // 简单场景足够,必要时可增大 + if (buf_len > sizeof(buf)) return; // 防御 + + buf[0] = RESP_HEADER; + buf[1] = type; + buf[2] = len; + + // 简化逻辑:只有当len > 0且payload非空时才复制数据 + if (len > 0 && payload != NULL) { + for (uint8_t i = 0; i < len; i++) { + buf[3 + i] = payload[i]; + } + } + + buf[buf_len - 1] = command_sum_crc_calc(buf, buf_len); + + // 使用GD32E230标准库函数逐字节发送(标准库实现) + for (uint8_t i = 0; i < buf_len; i++) { + // 等待发送缓冲区空 + while (usart_flag_get(RS485_PHY, USART_FLAG_TBE) == RESET) {} + usart_data_transmit(RS485_PHY, buf[i]); + } + + // 等待发送完成 + while (usart_flag_get(RS485_PHY, USART_FLAG_TC) == RESET) {} + + // // 使用printf发送(通过重定向到串口) + // for (uint8_t i = 0; i < buf_len; i++) { + // printf("%c", buf[i]); + // } + + // // 刷新缓冲区 + // fflush(stdout); +} + +/** + * @brief 判断字符是否为十进制数字字符。 + * @param c 待检查的字符(ASCII码值)。 + * @return bool 判断结果。 + * @retval true 字符为 '0' 到 '9' 之间的数字字符。 + * @retval false 字符不是十进制数字字符。 + * @ingroup Command + */ +static inline bool is_dec_digit(uint8_t c) { return (c >= '0' && c <= '9'); } + +/** + * @brief 从缓冲区解析十进制无符号整数。 + * @details 从指定位置开始连续读取十进制数字字符,累加构成32位无符号整数。 + * 遇到非数字字符或到达长度限制时停止解析。 + * @param s 指向待解析字符缓冲区的起始位置。 + * @param n 允许解析的最大字符数。 + * @param out 输出参数,存储解析结果,可为NULL。 + * @return uint8_t 实际消耗的字符数。 + * @retval 0 首字符不是数字,解析失败。 + * @retval >0 成功解析的数字字符个数。 + * @note 不处理符号、空白字符或进制前缀。 + * @warning 不进行溢出检查,超出uint32_t范围时按无符号算术溢出处理。 + * @ingroup Command + */ +static uint8_t parse_uint_dec(const uint8_t *s, uint8_t n, uint32_t *out) +{ + uint8_t i = 0; + uint32_t v = 0; + while (i < n && is_dec_digit(s[i])) + { + v = v * 10u + (uint32_t)(s[i] - '0'); + i++; + } + if (i == 0) return 0; // 未读到数字 + if (out) *out = v; // + return i; +} + +/* ============================================================================ + * 命令处理函数 + * ============================================================================ */ + +/** + * @brief 解析并处理完整的命令帧。 + * @details 处理经过协议校验的完整命令帧,支持以下命令格式: + * - 无参数命令:M<数字>(如 M1、M2、M10、M201) + * - 带参数命令:M<数字>S<参数>(如 M100S123,参数为十进制) + * + * 支持的命令: + * - M1: 开启LED,启用传感器上报 + * - M2: 关闭LED,禁用传感器上报 + * - M100S: 设置PWM值(示例) + * + * @param frame 指向完整命令帧的缓冲区(从包头0xD5开始)。 + * @param len 命令帧总长度(字节)。 + * @note 函数内部进行帧格式校验,格式错误时自动发送错误响应。 + * @warning 假设输入帧已通过基本协议校验(包头、类型、CRC等)。 + * @ingroup Command + */ +void handle_command(const uint8_t *frame, uint8_t len) { + // 帧格式:D5 03 LEN [cmd] CRC; cmd 支持变长,如 "M1"、"M10"、"M201"、"M123S400",有最小长度限制和命令长度校验 + uint8_t cmd_len = frame[2]; + if (len < PROTOCOL_MIN_FRAME_LEN || (uint8_t)(3 + cmd_len + 1) != len) return; // 长度不匹配或者小于最小限制 + + const uint8_t *cmd = &frame[3]; // 提取命令部分 + + // 命令必须以 'M' 开头 + if (cmd[0] != 'M'){ + send_response(RESP_TYPE_TYPE_ERR, s_report_status_err, sizeof(s_report_status_err)); + return; + } + + // 从 'M' 后开始解析 + uint8_t cmd_index = 1; + // 解析M后的十进制数,即命令本体 + uint32_t base_cmd = 0; + uint8_t used_base_cmd = parse_uint_dec(&cmd[cmd_index], (cmd_len - cmd_index), &base_cmd); + if (used_base_cmd == 0) + { + // 'M' 后没有数字,格式错误 + send_response(RESP_TYPE_LEN_ERR, s_report_status_err, sizeof(s_report_status_err)); + return; + } + + cmd_index = (uint8_t)(cmd_index + used_base_cmd); // 更新索引到命令后 + + // 情况A:无附加参数的基础命令 + if (cmd_index == cmd_len) { + // 仅基础命令,如 M1, M2, M3 + switch (base_cmd) { + case 1u: // M1: enable sensor report + set_sensor_report_status(true); + return; + case 2u: // M2: disable sensor report + set_sensor_report_status(false); + return; + + // 示例:M3、M10、M201、M100 等(按需添加) + // case 3u: // M3命令 - 高电流驱动测试 + // /** + // * M3命令:使用更高驱动电流测试线圈响应 + // * 响应格式:6字节状态信息 + // * + // * 响应数据解析: + // * [0-1]: 传感器状态寄存器(大端序) + // * bit[15-8]: 预留 + // * bit[7]: DRDY_1 - 通道1数据就绪 + // * bit[6]: DRDY_0 - 通道0数据就绪 + // * bit[5]: UNREAD_CONV - 未读转换结果 + // * bit[4]: ERR_ZC - 零计数错误 + // * bit[3]: ERR_AE - 幅度错误(重点关注) + // * bit[2]: ERR_WD - 看门狗超时 + // * bit[1]: ERR_OR - 过量程错误 + // * bit[0]: ERR_UR - 欠量程错误 + // * [2]: 数据就绪标志 (0x01=就绪, 0x00=未就绪) + // * [3]: 0xA0 - 高电流测试标记 + // * [4]: 幅度错误专用标志 (0xAE=有幅度错误, 0x00=无) + // * [5]: 0x33 - M3命令标记 + // * + // * 分析要点: + // * - 如果[0-1]从0x0008变为其他值,说明高电流有效果 + // * - 如果[2]变为0x01,说明数据开始就绪 + // * - 如果[4]变为0x00,说明幅度错误消失 + // */ + // // 重置传感器 + // ldc1612_reset_sensor(); + // delay_ms(50); + + // // 使用更高的驱动电流重新配置 + // // ldc1612_write_register(SET_DRIVER_CURRENT_REG, 0xA000); + // delay_ms(10); + + // // 重新配置其他参数 + // ldc1612_config_single_channel(CHANNEL_0); + // delay_ms(200); // 更长稳定时间 + + // // 检查结果 + // uint16_t status_m3 = ldc1612_get_sensor_status(); + // bool ready_m3 = ldc1612_is_data_ready(CHANNEL_0); + + // uint8_t m3_info[6]; + // m3_info[0] = (uint8_t)(status_m3 >> 8); + // m3_info[1] = (uint8_t)(status_m3 & 0xFF); + // m3_info[2] = ready_m3 ? 0x01 : 0x00; + // m3_info[3] = 0xA0; // 高电流标记 + // m3_info[4] = (status_m3 & 0x0008) ? 0xAE : 0x00; // 幅度错误标志 + // m3_info[5] = 0x33; // M3命令标记 + + // send_response(RESP_TYPE_OK, m3_info, sizeof(m3_info)); + // return; + // case 4u: // M4命令 - 寄存器诊断 + // /** + // * M4命令:读取关键寄存器进行配置诊断 + // * 响应格式:8字节寄存器信息 + // * + // * 响应数据解析: + // * [0-1]: 状态寄存器 (0x18) - 当前传感器状态 + // * [2-3]: 传感器配置寄存器 (0x1A) - 传感器工作模式 + // * 期望值: 0x1601 (活动模式,单通道) + // * [4-5]: 驱动电流寄存器 (0x1E) - 当前驱动电流设置 + // * 常见值: 0x9000(默认), 0xA000(高), 0xF800(最高) + // * [6]: I2C读取状态 (0x4F='O'=成功, 0xEE=失败) + // * [7]: 0x44 - M4命令标记 + // * + // * 分析要点: + // * - [2-3]应该是0x1601,如果不是说明配置异常 + // * - [4-5]显示实际的驱动电流设置 + // * - [6]必须是0x4F,否则I2C通信有问题 + // */ + // // 简化版本,只读取最关键的寄存器,避免I2C超时 + // uint16_t status_reg = ldc1612_get_sensor_status(); // 0x18 + + // // 逐一安全读取关键寄存器 + // uint8_t data_buf[2] = {0}; + // uint16_t sensor_config = 0; + // uint16_t drive_current = 0; + + // // 尝试读取传感器配置寄存器 + // bool result1_ok = (LDC1612_IIC_READ_16BITS(LDC1612_ADDR, SENSOR_CONFIG_REG, data_buf) == I2C_RESULT_SUCCESS); + // if (result1_ok) { + // sensor_config = (data_buf[0] << 8) | data_buf[1]; + // } + + // // 尝试读取驱动电流寄存器 + // bool result2_ok = (LDC1612_IIC_READ_16BITS(LDC1612_ADDR, SET_DRIVER_CURRENT_REG, data_buf) == I2C_RESULT_SUCCESS); + // if (result2_ok) { + // drive_current = (data_buf[0] << 8) | data_buf[1]; + // } + + // // 构造8字节简化诊断信息 + // uint8_t diag_info[8]; + // diag_info[0] = (uint8_t)(status_reg >> 8); // 状态寄存器高位 + // diag_info[1] = (uint8_t)(status_reg & 0xFF); // 状态寄存器低位 + // diag_info[2] = (uint8_t)(sensor_config >> 8); // 传感器配置寄存器高位 + // diag_info[3] = (uint8_t)(sensor_config & 0xFF); // 传感器配置寄存器低位 + // diag_info[4] = (uint8_t)(drive_current >> 8); // 驱动电流寄存器高位 + // diag_info[5] = (uint8_t)(drive_current & 0xFF); // 驱动电流寄存器低位 + // diag_info[6] = (result1_ok && result2_ok) ? 0x4F : 0xEE; // I2C状态 + // diag_info[7] = 0x44; // M4命令标记 + + // send_response(RESP_TYPE_OK, diag_info, sizeof(diag_info)); + // return; + // case 5u: // M5命令 - 最高电流启动测试 + // // 命令: D5 03 02 4D 35 87 + // // 响应: B5 F0 08 [状态2字节][就绪标志][电流设置2字节][幅度错误标志][M5标记][最高电流标记] CRC + // // 响应格式: + // // [0-1]: 传感器状态寄存器(启动后状态) + // // [2]: 数据就绪标志 (0x01=就绪, 0x00=未就绪) + // // [3-4]: 实际驱动电流设置值(应该是0xF800) + // // [5]: 幅度错误专用标志 (0xAE=仍有错误, 0x00=错误消失) + // // [6]: 0x55 - M5命令标记 + // // [7]: 0xF8 - 最高电流标记 + // // 重置传感器 + // ldc1612_reset_sensor(); + // delay_ms(100); + + // // 使用最高驱动电流并固定配置 + // // ldc1612_write_register(SET_DRIVER_CURRENT_REG, 0xF800); + // delay_ms(10); + + // // 手动配置其他必要寄存器,避免被覆盖 + // // 配置频率分频器为较低频率 (更容易起振) + // uint8_t freq_data[2] = {0x10, 0x00}; // 较低分频 + // LDC1612_IIC_WRITE_16BITS(LDC1612_ADDR, SET_FREQ_REG_START + CHANNEL_0, freq_data); + // delay_ms(10); + + // // 设置较长的LC稳定时间 + // uint8_t lc_data[2] = {0x04, 0x00}; // 更长稳定时间 + // LDC1612_IIC_WRITE_16BITS(LDC1612_ADDR, SET_LC_STABILIZE_REG_START + CHANNEL_0, lc_data); + // delay_ms(10); + + // // 配置MUX为单通道模式 + // // ldc1612_configure_mux_register(0, CHANNEL_0, LDC1612_MUX_RR_SEQUENCE_1, LDC1612_MUX_FILTER_1MHz); + // delay_ms(10); + + // // 启动传感器 + // uint8_t sensor_cfg_data[2] = {0x16, 0x01}; // 活动模式,单通道 + // LDC1612_IIC_WRITE_16BITS(LDC1612_ADDR, SENSOR_CONFIG_REG, sensor_cfg_data); + // delay_ms(200); // 更长稳定时间 + + // // 读取结果 + // uint16_t status_m5 = ldc1612_get_sensor_status(); + // bool ready_m5 = ldc1612_is_data_ready(CHANNEL_0); + + // // 再次确认驱动电流设置 + // uint8_t curr_data[2]; + // LDC1612_IIC_READ_16BITS(LDC1612_ADDR, SET_DRIVER_CURRENT_REG, curr_data); + // uint16_t actual_current = (curr_data[0] << 8) | curr_data[1]; + + // uint8_t m5_info[8]; + // m5_info[0] = (uint8_t)(status_m5 >> 8); + // m5_info[1] = (uint8_t)(status_m5 & 0xFF); + // m5_info[2] = ready_m5 ? 0x01 : 0x00; + // m5_info[3] = (uint8_t)(actual_current >> 8); // 实际电流设置高位 + // m5_info[4] = (uint8_t)(actual_current & 0xFF); // 实际电流设置低位 + // m5_info[5] = (status_m5 & 0x0008) ? 0xAE : 0x00; // 幅度错误标志 + // m5_info[6] = 0x55; // M5命令标记 + // m5_info[7] = 0xF8; // 最高电流标记 + + // send_response(RESP_TYPE_OK, m5_info, sizeof(m5_info)); + // return; + // case 6u: // M6命令 - 芯片功能验证 + // // 命令: D5 03 02 4D 36 88 + // // 响应: B5 F0 0C [写入值2字节][读取值2字节][制造商ID2字节][设备ID2字节][状态2字节][ID读取状态][M6标记] CRC + // // 响应格式: + // // [0-1]: 写入测试值 (0x9000) + // // [2-3]: 读取回的值 + // // [4-5]: 制造商ID (应该是0x5449="TI") + // // [6-7]: 设备ID (应该是0x3055) + // // [8-9]: 当前状态寄存器 + // // [10]: ID读取状态 (0x4F=成功, 0xEE=失败) + // // [11]: 0x66 - M6命令标记 + // // 测试1: 写入和读取特定值到驱动电流寄存器 + // uint8_t test_current_data[2] = {0x90, 0x00}; // 写入0x9000 + // LDC1612_IIC_WRITE_16BITS(LDC1612_ADDR, SET_DRIVER_CURRENT_REG, test_current_data); + // delay_ms(10); + + // // 读取验证 + // uint8_t read_current_data[2]; + // LDC1612_IIC_READ_16BITS(LDC1612_ADDR, SET_DRIVER_CURRENT_REG, read_current_data); + // uint16_t read_current = (read_current_data[0] << 8) | read_current_data[1]; + + // // 测试2: 读取制造商ID和设备ID + // uint8_t manufacturer_data[2]; + // uint8_t device_data[2]; + // bool id_read_ok = true; + + // if (LDC1612_IIC_READ_16BITS(LDC1612_ADDR, 0x7E, manufacturer_data) != I2C_RESULT_SUCCESS) { + // id_read_ok = false; + // } + // if (LDC1612_IIC_READ_16BITS(LDC1612_ADDR, 0x7F, device_data) != I2C_RESULT_SUCCESS) { + // id_read_ok = false; + // } + + // uint16_t manufacturer_id = id_read_ok ? ((manufacturer_data[0] << 8) | manufacturer_data[1]) : 0x0000; + // uint16_t device_id = id_read_ok ? ((device_data[0] << 8) | device_data[1]) : 0x0000; + + // // 测试3: 检查当前状态 + // uint16_t current_status = ldc1612_get_sensor_status(); + + // // 构造12字节测试结果 + // uint8_t test_info[12]; + // test_info[0] = 0x90; // 写入的值高位 + // test_info[1] = 0x00; // 写入的值低位 + // test_info[2] = (uint8_t)(read_current >> 8); // 读取的值高位 + // test_info[3] = (uint8_t)(read_current & 0xFF); // 读取的值低位 + // test_info[4] = (uint8_t)(manufacturer_id >> 8); + // test_info[5] = (uint8_t)(manufacturer_id & 0xFF); + // test_info[6] = (uint8_t)(device_id >> 8); + // test_info[7] = (uint8_t)(device_id & 0xFF); + // test_info[8] = (uint8_t)(current_status >> 8); + // test_info[9] = (uint8_t)(current_status & 0xFF); + // test_info[10] = id_read_ok ? 0x4F : 0xEE; // ID读取状态 + // test_info[11] = 0x66; // M6命令标记 + + // send_response(RESP_TYPE_OK, test_info, sizeof(test_info)); + // return; + // case 7u: // M7命令 - 保守参数测试 + // // 命令: D5 03 02 4D 37 89 + // // 响应: B5 F0 0A [状态2字节][就绪标志][频率设置2字节][幅度错误标志][欠量程错误标志][过量程错误标志][M7标记][低频标记] CRC + // // 响应格式: + // // [0-1]: 状态寄存器 + // // [2]: 数据就绪标志 + // // [3-4]: 实际频率分频器设置 (0x2000=较低频率) + // // [5]: 幅度错误标志 (0xAE=有错误, 0x00=无) + // // [6]: 欠量程错误标志 (0x01=有, 0x00=无) + // // [7]: 过量程错误标志 (0x02=有, 0x00=无) + // // [8]: 0x77 - M7命令标记 + // // [9]: 0x20 - 低频标记 + // // 重置传感器 + // ldc1612_reset_sensor(); + // delay_ms(100); + + // // 使用保守的配置尝试启动线圈 + // // 1. 设置最高驱动电流 + // uint8_t drive_data[2] = {0xF8, 0x00}; // 最高电流 + // LDC1612_IIC_WRITE_16BITS(LDC1612_ADDR, SET_DRIVER_CURRENT_REG, drive_data); + // delay_ms(10); + + // // 2. 设置较低的频率分频器(适合更大电感值) + // uint8_t freq_low_data[2] = {0x20, 0x00}; // 更低频率 + // LDC1612_IIC_WRITE_16BITS(LDC1612_ADDR, SET_FREQ_REG_START + CHANNEL_0, freq_low_data); + // delay_ms(10); + + // // 3. 设置更长的LC稳定时间 + // uint8_t lc_stable_data[2] = {0x08, 0x00}; // 更长稳定时间 + // LDC1612_IIC_WRITE_16BITS(LDC1612_ADDR, SET_LC_STABILIZE_REG_START + CHANNEL_0, lc_stable_data); + // delay_ms(10); + + // // 4. 设置更长的转换时间 + // uint8_t conv_time_data[2] = {0x04, 0x00}; // 更长转换时间 + // LDC1612_IIC_WRITE_16BITS(LDC1612_ADDR, SET_CONVERSION_TIME_REG_START + CHANNEL_0, conv_time_data); + // delay_ms(10); + + // // 5. 设置转换偏移 + // uint8_t conv_offset_data[2] = {0x00, 0x00}; + // LDC1612_IIC_WRITE_16BITS(LDC1612_ADDR, SET_CONVERSION_OFFSET_REG_START + CHANNEL_0, conv_offset_data); + // delay_ms(10); + + // // 6. 配置错误寄存器 - 降低错误敏感度 + // uint8_t error_config_data[2] = {0x00, 0x00}; // 允许所有错误 + // LDC1612_IIC_WRITE_16BITS(LDC1612_ADDR, ERROR_CONFIG_REG, error_config_data); + // delay_ms(10); + + // // 7. 配置MUX寄存器 + // // ldc1612_configure_mux_register(0, CHANNEL_0, LDC1612_MUX_RR_SEQUENCE_1, LDC1612_MUX_FILTER_1MHz); + // delay_ms(10); + + // // 8. 启动传感器 + // uint8_t sensor_start_data[2] = {0x16, 0x01}; // 活动模式 + // LDC1612_IIC_WRITE_16BITS(LDC1612_ADDR, SENSOR_CONFIG_REG, sensor_start_data); + // delay_ms(500); // 给予充分时间稳定 + + // // 检查结果 + // uint16_t status_m7 = ldc1612_get_sensor_status(); + // bool ready_m7 = ldc1612_is_data_ready(CHANNEL_0); + + // // 读取实际配置的频率分频器确认 + // uint8_t freq_readback[2]; + // LDC1612_IIC_READ_16BITS(LDC1612_ADDR, SET_FREQ_REG_START + CHANNEL_0, freq_readback); + // uint16_t freq_actual = (freq_readback[0] << 8) | freq_readback[1]; + + // uint8_t m7_info[10]; + // m7_info[0] = (uint8_t)(status_m7 >> 8); + // m7_info[1] = (uint8_t)(status_m7 & 0xFF); + // m7_info[2] = ready_m7 ? 0x01 : 0x00; + // m7_info[3] = (uint8_t)(freq_actual >> 8); // 实际频率分频器 + // m7_info[4] = (uint8_t)(freq_actual & 0xFF); + // m7_info[5] = (status_m7 & 0x0008) ? 0xAE : 0x00; // 幅度错误 + // m7_info[6] = (status_m7 & 0x0001) ? 0x01 : 0x00; // 欠量程错误 + // m7_info[7] = (status_m7 & 0x0002) ? 0x02 : 0x00; // 过量程错误 + // m7_info[8] = 0x77; // M7命令标记 + // m7_info[9] = 0x20; // 低频标记 + + // send_response(RESP_TYPE_OK, m7_info, sizeof(m7_info)); + // return; + // case 8u: // M8命令 - 极端参数测试 + // // 命令: D5 03 02 4D 38 8A + // // 响应: B5 F0 06 [状态2字节][就绪标志][幅度错误标志][M8标记][极端测试标记] CRC + // // 响应格式: + // // [0-1]: 传感器状态寄存器 + // // [2]: 数据就绪标志 (0x01=就绪, 0x00=未就绪) + // // [3]: 幅度错误标志 (0xAE=仍有错误, 0x00=错误消失) + // // [4]: 0x88 - M8命令标记 + // // [5]: 0xEE - 极端测试标记 + // { + // // 重置传感器 + // ldc1612_reset_sensor(); + // delay_ms(100); + + // // 极端配置1: 极低频率 + // uint8_t extreme_freq[2] = {0x40, 0x00}; + // LDC1612_IIC_WRITE_16BITS(LDC1612_ADDR, SET_FREQ_REG_START + CHANNEL_0, extreme_freq); + // delay_ms(10); + + // // 极端配置2: 最大驱动电流 + // uint8_t max_drive[2] = {0xFF, 0x00}; + // LDC1612_IIC_WRITE_16BITS(LDC1612_ADDR, SET_DRIVER_CURRENT_REG, max_drive); + // delay_ms(10); + + // // 极端配置3: 禁用错误检测 + // uint8_t no_errors[2] = {0x00, 0x00}; + // LDC1612_IIC_WRITE_16BITS(LDC1612_ADDR, ERROR_CONFIG_REG, no_errors); + // delay_ms(10); + + // // 启动传感器 + // uint8_t start_data[2] = {0x16, 0x01}; + // LDC1612_IIC_WRITE_16BITS(LDC1612_ADDR, SENSOR_CONFIG_REG, start_data); + // delay_ms(1000); // 等待1秒 + + // // 读取状态 + // uint16_t status_8 = ldc1612_get_sensor_status(); + // bool ready_8 = ldc1612_is_data_ready(CHANNEL_0); + + // uint8_t m8_result[6]; + // m8_result[0] = (uint8_t)(status_8 >> 8); + // m8_result[1] = (uint8_t)(status_8 & 0xFF); + // m8_result[2] = ready_8 ? 0x01 : 0x00; + // m8_result[3] = (status_8 & 0x0008) ? 0xAE : 0x00; // 幅度错误 + // m8_result[4] = 0x88; // M8标记 + // m8_result[5] = 0xEE; // 极端测试标记 + + // send_response(RESP_TYPE_OK, m8_result, sizeof(m8_result)); + // return; + // } + // case 9u: // M9命令 - 多频率特性测试 + // // 命令: D5 03 02 4D 39 8B + // // 响应: B5 F0 08 [高频状态2字节][高频就绪标志][低频状态2字节][低频就绪标志][M9标记][多频测试标记] CRC + // // 响应格式: + // // [0-1]: 高频测试状态 + // // [2]: 高频就绪标志 (0x01=就绪, 0x00=未就绪) + // // [3-4]: 低频测试状态 + // // [5]: 低频就绪标志 (0x01=就绪, 0x00=未就绪) + // // [6]: 0x99 - M9命令标记 + // // [7]: 0xAA - 多频测试标记 + // { + // // 测试1: 高频配置 + // ldc1612_reset_sensor(); + // delay_ms(50); + + // uint8_t high_freq[2] = {0x04, 0x00}; // 高频 + // uint8_t low_drive[2] = {0x80, 0x00}; // 低电流 + + // LDC1612_IIC_WRITE_16BITS(LDC1612_ADDR, SET_FREQ_REG_START + CHANNEL_0, high_freq); + // LDC1612_IIC_WRITE_16BITS(LDC1612_ADDR, SET_DRIVER_CURRENT_REG, low_drive); + // delay_ms(10); + + // // 启动高频测试 + // uint8_t start_hf[2] = {0x16, 0x01}; + // LDC1612_IIC_WRITE_16BITS(LDC1612_ADDR, SENSOR_CONFIG_REG, start_hf); + // delay_ms(200); + + // uint16_t hf_status = ldc1612_get_sensor_status(); + // bool hf_ready = ldc1612_is_data_ready(CHANNEL_0); + + // // 测试2: 低频配置 + // uint8_t sleep_mode[2] = {0x20, 0x01}; + // LDC1612_IIC_WRITE_16BITS(LDC1612_ADDR, SENSOR_CONFIG_REG, sleep_mode); + // delay_ms(50); + + // uint8_t low_freq[2] = {0x20, 0x00}; // 低频 + // uint8_t high_drive[2] = {0xC0, 0x00}; // 高电流 + + // LDC1612_IIC_WRITE_16BITS(LDC1612_ADDR, SET_FREQ_REG_START + CHANNEL_0, low_freq); + // LDC1612_IIC_WRITE_16BITS(LDC1612_ADDR, SET_DRIVER_CURRENT_REG, high_drive); + // delay_ms(10); + + // // 启动低频测试 + // LDC1612_IIC_WRITE_16BITS(LDC1612_ADDR, SENSOR_CONFIG_REG, start_hf); + // delay_ms(200); + + // uint16_t lf_status = ldc1612_get_sensor_status(); + // bool lf_ready = ldc1612_is_data_ready(CHANNEL_0); + + // uint8_t m9_result[8]; + // m9_result[0] = (uint8_t)(hf_status >> 8); // 高频状态 + // m9_result[1] = (uint8_t)(hf_status & 0xFF); + // m9_result[2] = hf_ready ? 0x01 : 0x00; // 高频就绪 + // m9_result[3] = (uint8_t)(lf_status >> 8); // 低频状态 + // m9_result[4] = (uint8_t)(lf_status & 0xFF); + // m9_result[5] = lf_ready ? 0x01 : 0x00; // 低频就绪 + // m9_result[6] = 0x99; // M9标记 + // m9_result[7] = 0xAA; // 多频测试标记 + + // send_response(RESP_TYPE_OK, m9_result, sizeof(m9_result)); + // return; + // } + // case 201u: // M201命令 + // send_response(RESP_TYPE_OK, s_report_status_ok, sizeof(s_report_status_ok)); + // return; + + default: + // 其它无参数命令在此扩展(示例:M100)处理逻辑该如何待定 + // send_response(RESP_TYPE_OK, s_report_status_ok, sizeof(s_report_status_ok)); + // return; + break; + } + // 未在处理列表的无参数基础命令,回复错误 + send_response(RESP_TYPE_TYPE_ERR, s_report_status_err, sizeof(s_report_status_err)); + return; + } + + // 情况B:有附加参数的命令 + if (cmd[cmd_index] == 'S') { + cmd_index++; + uint32_t param_value = 0; + const uint8_t used_param_cmd = parse_uint_dec(&cmd[cmd_index], (uint8_t)(cmd_len - cmd_index), ¶m_value); + if (used_param_cmd == 0) { + // 'S' 后没有数字,格式错误 + send_response(RESP_TYPE_LEN_ERR, s_report_status_err, sizeof(s_report_status_err)); + return; + } + + switch (base_cmd) + { + // case 100u: + // // set_pwm(param_value); + // printf("Set PWM to %u\n", param_value); + // return; + + default: + break; + } + send_response(RESP_TYPE_TYPE_ERR, s_report_status_err, sizeof(s_report_status_err)); + } +} + +/** + * @brief 处理串口环形缓冲区中的命令数据,解析完整的协议帧。 + * @details 本函数实现一个基于状态机的协议解析器,用于处理格式为 D5 03 LEN [cmd] CRC 的命令帧: + * - 状态1:等待包头字节 PROTOCOL_PACKAGE_HEADER (0xD5) + * - 状态2:接收板卡类型字节 PROTOCOL_BOARD_TYPE (0x03) + * - 状态3:接收长度字段并计算期望的完整帧长度 + * - 状态4:继续接收剩余数据直到完整帧 + * - 状态5:对完整帧进行校验(包头、板卡类型、CRC)并处理 + * + * 函数采用非阻塞方式处理,每次调用处理缓冲区中所有可用数据。 + * 遇到格式错误、长度异常或校验失败时自动重置状态机。 + * + * @note 本函数使用静态变量维护解析状态,因此不可重入。在中断环境中使用需注意并发安全。 + * 协议帧最大长度受 PROTOCOL_MAX_FRAME_LEN 限制,超出范围的帧将被丢弃。 + * + * @warning 函数依赖 uart_ring_buffer_available() 和 uart_ring_buffer_get() + * 正确实现,若这些函数有缺陷可能导致死循环或数据丢失。 + * + * @see handle_command() 用于处理校验通过的完整命令帧 + * @see command_sum_crc_calc() 用于计算和校验 CRC 值 + * @see send_response() 用于发送错误响应 + * + * @ingroup Command + */ +void command_process(void) { + static uint8_t cmd_buf[PROTOCOL_MAX_FRAME_LEN]; + static uint8_t cmd_len = 0; + static uint8_t expected_cmd_len = 0; // 0 表示尚未确定总长度 + + while (uart_ring_buffer_available() > 0) { + int byte = uart_ring_buffer_get(); + if (byte < 0) break; + + if (cmd_len == 0) { + if ((uint8_t)byte == PROTOCOL_PACKAGE_HEADER) { + cmd_buf[cmd_len++] = (uint8_t)byte; + expected_cmd_len = 0; // 等待进一步字段以确定长度 + } else { + // 丢弃非起始字节 + } + continue; + } + + if (cmd_len >= PROTOCOL_MAX_FRAME_LEN) { + // 防御:缓冲溢出,复位状态机 + cmd_len = 0; + expected_cmd_len = 0; + } + + // 缓存后续字节 + cmd_buf[cmd_len++] = (uint8_t)byte; + + // 当到达长度字段(索引 2)后,确定总长度:3 + LEN + 1 + if (cmd_len == 3) { + uint8_t payload_len = cmd_buf[2]; + expected_cmd_len = (uint8_t)(3 + payload_len + 1); + if (expected_cmd_len > PROTOCOL_MAX_FRAME_LEN) { + // 异常:长度超界,复位状态机 + cmd_len = 0; + expected_cmd_len = 0; + } + continue; + } + + if (expected_cmd_len > 0 && cmd_len == expected_cmd_len) { + // 到帧尾,进行各项校验 + bool verification_status = true; + + #ifdef DEBUG_VERBOSE + if (cmd_buf[0] != PROTOCOL_PACKAGE_HEADER) { + send_response(RESP_TYPE_HEADER_ERR, s_report_status_err, sizeof(s_report_status_err)); + verification_status = false; + } + #endif + + if (verification_status && cmd_buf[1] != PROTOCOL_BOARD_TYPE) { + send_response(RESP_TYPE_TYPE_ERR, s_report_status_err, sizeof(s_report_status_err)); + verification_status = false; + } + if (verification_status) { + uint8_t crc_calc = command_sum_crc_calc(cmd_buf, expected_cmd_len); + uint8_t crc_recv = cmd_buf[expected_cmd_len - 1]; + if (crc_calc != crc_recv) { + send_response(RESP_TYPE_CRC_ERR, s_report_status_err, sizeof(s_report_status_err)); + verification_status = false; + } + } + + if (verification_status) { + handle_command(cmd_buf, expected_cmd_len); + } + + // 复位,等待下一帧 + cmd_len = 0; + expected_cmd_len = 0; + } + } +} + +void eddy_current_report(void) { + // if (!g_sensor_report_enabled) return; + uint32_t raw_result = ldc1612_get_raw_channel_result(CHANNEL_0); + + uint8_t sensor_data[4]; + sensor_data[0] = (uint8_t)(raw_result >> 24); + sensor_data[1] = (uint8_t)(raw_result >> 16); + sensor_data[2] = (uint8_t)(raw_result >> 8); + sensor_data[3] = (uint8_t)(raw_result & 0xFF); + + send_response(RESP_TYPE_OK, sensor_data, sizeof(sensor_data)); +} \ No newline at end of file diff --git a/Src/gd32e23x_it.c b/Src/gd32e23x_it.c index b8fa768..aa19aa8 100644 --- a/Src/gd32e23x_it.c +++ b/Src/gd32e23x_it.c @@ -34,6 +34,9 @@ OF SUCH DAMAGE. #include "gd32e23x_it.h" #include "systick.h" +#include "uart.h" +#include "uart_ring_buffer.h" +#include "led.h" /*! \brief this function handles NMI exception @@ -93,7 +96,14 @@ void PendSV_Handler(void) \param[out] none \retval none */ -void SysTick_Handler(void) -{ +void SysTick_Handler(void) { + led_heart_beat(); // LED心跳指示灯 delay_decrement(); } + +void USART0_IRQHandler(void) { + if (RESET != usart_interrupt_flag_get(USART0, USART_INT_FLAG_RBNE)) { + uint8_t data = usart_data_receive(USART0); + (void)uart_ring_buffer_put(data); // 缓冲满时丢弃,返回值可用于统计 + } +} \ No newline at end of file diff --git a/Src/i2c.c b/Src/i2c.c new file mode 100644 index 0000000..e241b58 --- /dev/null +++ b/Src/i2c.c @@ -0,0 +1,677 @@ +// +// Created by dell on 24-12-20. +// + +#include "i2c.h" + +/*! + \brief configure the GPIO ports + \param[in] none + \param[out] none + \retval none +*/ +void i2c_gpio_config(void) { + /* enable IIC GPIO clock */ + rcu_periph_clock_enable(RCU_GPIO_I2C); + + /* connect I2C_SCL_PIN to I2C_SCL */ + gpio_af_set(I2C_SCL_PORT, I2C_GPIO_AF, I2C_SCL_PIN); + /* connect I2C_SDA_PIN to I2C_SDA */ + gpio_af_set(I2C_SDA_PORT, I2C_GPIO_AF, I2C_SDA_PIN); + /* configure GPIO pins of I2C */ + gpio_mode_set(I2C_SCL_PORT, GPIO_MODE_AF, GPIO_PUPD_PULLUP, I2C_SCL_PIN); + gpio_output_options_set(I2C_SCL_PORT, GPIO_OTYPE_OD, GPIO_OSPEED_50MHZ, I2C_SCL_PIN); + gpio_mode_set(I2C_SDA_PORT, GPIO_MODE_AF, GPIO_PUPD_PULLUP, I2C_SDA_PIN); + gpio_output_options_set(I2C_SDA_PORT, GPIO_OTYPE_OD, GPIO_OSPEED_50MHZ, I2C_SDA_PIN); +} + +/*! + \brief configure the I2CX interface + \param[in] none + \param[out] none + \retval none +*/ +i2c_result_t i2c_config(void) { + /* configure I2C GPIO */ + i2c_gpio_config(); + /* enable I2C clock */ + rcu_periph_clock_enable(RCU_I2C); + /* configure I2C clock */ + i2c_clock_config(I2C0, I2C_SPEED, I2C_DTCY_2); + /* configure I2C address */ + i2c_mode_addr_config(I2C0, I2C_I2CMODE_ENABLE, I2C_ADDFORMAT_7BITS, 0xA0); + /* enable I2CX */ + i2c_enable(I2C0); + /* enable acknowledge */ + i2c_ack_config(I2C0, I2C_ACK_ENABLE); + + return I2C_RESULT_SUCCESS; +} + +/* wait for SCL to go high, return true if successful, false if timeout */ +static bool i2c_wait_scl_high(uint16_t max_wait_time) { + while (max_wait_time--) { + if (gpio_input_bit_get(I2C_SCL_PORT, I2C_SCL_PIN)) { + return true; + } + delay_10us(1); + } + return false; +} + +/* generate one manual SCL pulse; return true if SCL observed high (no stuck/overstretch) */ +static bool i2c_generate_scl_pulse(void) { + GPIO_BC(I2C_SCL_PORT) = I2C_SCL_PIN; /* drive SCL low */ + delay_10us(1); + GPIO_BOP(I2C_SCL_PORT) = I2C_SCL_PIN; /* release SCL (open-drain -> high via pull-up) */ + return i2c_wait_scl_high(200); /* wait up to ~2ms for clock stretching release */ +} + +/*! + \brief reset I2C bus + \param[in] none + \param[out] none + \retval none +*/ +i2c_result_t i2c_bus_reset(void) { + /* 1. Disable & deinit peripheral so pins can be fully controlled */ + i2c_disable(I2C0); + i2c_deinit(I2C0); + +#ifdef DEBUG_VERBOSE + printf("I2C bus reset\r\n"); +#endif + + /* 2. Configure SCL/SDA as GPIO open-drain outputs with pull-up and release them */ + gpio_mode_set(I2C_SCL_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLUP, I2C_SCL_PIN); + gpio_mode_set(I2C_SDA_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLUP, I2C_SDA_PIN); + gpio_output_options_set(I2C_SCL_PORT, GPIO_OTYPE_OD, GPIO_OSPEED_50MHZ, I2C_SCL_PIN); + gpio_output_options_set(I2C_SDA_PORT, GPIO_OTYPE_OD, GPIO_OSPEED_50MHZ, I2C_SDA_PIN); + gpio_bit_set(I2C_SCL_PORT, I2C_SCL_PIN); /* release SCL */ + gpio_bit_set(I2C_SDA_PORT, I2C_SDA_PIN); /* release SDA */ + +#ifdef DEBUG_VERBOSE + printf("I2C bus reset: SCL = %d, SDA = %d\r\n", gpio_input_bit_get(I2C_SCL_PORT, I2C_SCL_PIN), gpio_input_bit_get(I2C_SDA_PORT, I2C_SDA_PIN)); +#endif + + /* 3. Double sample to confirm bus state */ + delay_10us(1); + bool scl_value1 = gpio_input_bit_get(I2C_SCL_PORT, I2C_SCL_PIN); + bool sda_value1 = gpio_input_bit_get(I2C_SDA_PORT, I2C_SDA_PIN); + delay_10us(1); + bool scl_value2 = gpio_input_bit_get(I2C_SCL_PORT, I2C_SCL_PIN); + bool sda_value2 = gpio_input_bit_get(I2C_SDA_PORT, I2C_SDA_PIN); + + /* 4. If SCL low -> stuck (cannot proceed) */ + if (!scl_value2) { +#ifdef DEBUG_VERBOSE + printf("I2C bus reset: SCL stuck low\r\n"); +#endif + return I2C_RECOVERY_SCL_STUCK_LOW; + } + + /* 5. Fast path: bus idle */ + if (scl_value1 && sda_value1 && scl_value2 && sda_value2) { + i2c_config(); +#ifdef DEBUG_VERBOSE + printf("I2C bus reset: bus idle\r\n"); +#endif + return I2C_RECOVERY_OK; + } + + /* 6. SDA low: attempt to free by generating up to I2C_RECOVERY_CLOCKS pulses */ + if (scl_value2 && !sda_value2) { + bool sda_released = false; +#ifdef DEBUG_VERBOSE + printf("I2C bus reset: SCL will try to free SDA\r\n"); +#endif + for (uint8_t i = 0; i < I2C_RECOVERY_CLOCKS && !sda_released; i++) { + if (!i2c_generate_scl_pulse()) { + return I2C_RECOVERY_SCL_STUCK_LOW; /* SCL failed to go high */ + } + if (gpio_input_bit_get(I2C_SDA_PORT, I2C_SDA_PIN)) { + sda_released = true; + } + } + if (!sda_released) { + return I2C_RECOVERY_SDA_STUCK_LOW; + } + /* 7. Generate a STOP condition to leave bus in idle state */ +#ifdef DEBUG_VERBOSE + printf("I2C bus reset: generating STOP condition\r\n"); +#endif + gpio_bit_reset(I2C_SDA_PORT, I2C_SDA_PIN); /* SDA low */ + delay_10us(1); + gpio_bit_set(I2C_SCL_PORT, I2C_SCL_PIN); /* ensure SCL high */ + delay_10us(1); + gpio_bit_set(I2C_SDA_PORT, I2C_SDA_PIN); /* SDA rising while SCL high -> STOP */ + delay_10us(1); + } + +#ifdef DEBUG_VERBOSE + printf("I2C bus reset: bus recovered\r\n"); +#endif + /* 8. Reconfigure & enable peripheral */ + i2c_config(); + return I2C_RECOVERY_OK; +} + +/** + * @brief 扫描I2C总线,查找连接的设备 + * + * 该函数会扫描I2C总线上的所有地址(1到126),并尝试与每个地址进行通信。 + * 如果在某个地址上发现了设备,则会打印出该设备的地址。 + * 最后会打印出找到的设备总数。 + */ +void i2c_scan(void) { + uint32_t timeout; + uint8_t address; + int found_devices = 0; + + // printf("Scanning I2C bus...\r\n"); + const char* msg1 = "Scanning I2C bus...\r\n"; + for (uint8_t i = 0; msg1[i] != '\0'; i++) { + while (usart_flag_get(I2C_DEBUG_UART, USART_FLAG_TBE) == RESET) {} + usart_data_transmit(I2C_DEBUG_UART, msg1[i]); + } + while (usart_flag_get(I2C_DEBUG_UART, USART_FLAG_TC) == RESET) {} + + for (address = 1; address < 127; address++) { + timeout = 0; + + // 生成起始条件 + while (i2c_flag_get(I2C0, I2C_FLAG_I2CBSY) && (timeout < I2C_TIME_OUT)) + timeout++; + if (timeout >= I2C_TIME_OUT) { + continue; // 超时,跳过该地址 + } + i2c_start_on_bus(I2C0); + timeout = 0; + + // 等待起始条件发送完成 + while (!i2c_flag_get(I2C0, I2C_FLAG_SBSEND) && (timeout < I2C_TIME_OUT)) + timeout++; + if (timeout >= I2C_TIME_OUT) { + continue; // 超时,跳过该地址 + } + i2c_master_addressing(I2C0, (address << 1), I2C_TRANSMITTER); + timeout = 0; + + // 等待地址发送完成 + while (!i2c_flag_get(I2C0, I2C_FLAG_ADDSEND) && (timeout < I2C_TIME_OUT)) + timeout++; + if (timeout < I2C_TIME_OUT) { + i2c_flag_clear(I2C0, I2C_FLAG_ADDSEND); + // printf("Found device at 0x%02X\r\n", address); + const char* msg2_prefix = "Found device at 0x"; + for (uint8_t i = 0; msg2_prefix[i] != '\0'; i++) { + while (usart_flag_get(I2C_DEBUG_UART, USART_FLAG_TBE) == RESET) {} + usart_data_transmit(I2C_DEBUG_UART, msg2_prefix[i]); + } + // 发送地址的十六进制表示 + uint8_t hex_chars[] = "0123456789ABCDEF"; + while (usart_flag_get(I2C_DEBUG_UART, USART_FLAG_TBE) == RESET) {} + usart_data_transmit(I2C_DEBUG_UART, hex_chars[(address >> 4) & 0x0F]); + while (usart_flag_get(I2C_DEBUG_UART, USART_FLAG_TBE) == RESET) {} + usart_data_transmit(I2C_DEBUG_UART, hex_chars[address & 0x0F]); + const char* msg2_suffix = "\r\n"; + for (uint8_t i = 0; msg2_suffix[i] != '\0'; i++) { + while (usart_flag_get(I2C_DEBUG_UART, USART_FLAG_TBE) == RESET) {} + usart_data_transmit(I2C_DEBUG_UART, msg2_suffix[i]); + } + while (usart_flag_get(I2C_DEBUG_UART, USART_FLAG_TC) == RESET) {} + found_devices++; + } + + // 生成停止条件 + i2c_stop_on_bus(I2C0); + + timeout = 0; + + while (i2c_flag_get(I2C0, I2C_FLAG_STPDET) && (timeout < I2C_TIME_OUT)) + timeout++; + } + + if (found_devices == 0) { + // printf("No I2C devices found.\r\n"); + const char* msg3 = "No I2C devices found.\r\n"; + for (uint8_t i = 0; msg3[i] != '\0'; i++) { + while (usart_flag_get(I2C_DEBUG_UART, USART_FLAG_TBE) == RESET) {} + usart_data_transmit(I2C_DEBUG_UART, msg3[i]); + } + while (usart_flag_get(I2C_DEBUG_UART, USART_FLAG_TC) == RESET) {} + } else { + // printf("Total %d I2C devices found.\r\n", found_devices); + const char* msg4_prefix = "Total "; + for (uint8_t i = 0; msg4_prefix[i] != '\0'; i++) { + while (usart_flag_get(I2C_DEBUG_UART, USART_FLAG_TBE) == RESET) {} + usart_data_transmit(I2C_DEBUG_UART, msg4_prefix[i]); + } + // 发送设备数量 + if (found_devices >= 10) { + while (usart_flag_get(I2C_DEBUG_UART, USART_FLAG_TBE) == RESET) {} + usart_data_transmit(I2C_DEBUG_UART, '0' + (found_devices / 10)); + } + while (usart_flag_get(I2C_DEBUG_UART, USART_FLAG_TBE) == RESET) {} + usart_data_transmit(I2C_DEBUG_UART, '0' + (found_devices % 10)); + const char* msg4_suffix = " I2C devices found.\r\n"; + for (uint8_t i = 0; msg4_suffix[i] != '\0'; i++) { + while (usart_flag_get(I2C_DEBUG_UART, USART_FLAG_TBE) == RESET) {} + usart_data_transmit(I2C_DEBUG_UART, msg4_suffix[i]); + } + while (usart_flag_get(I2C_DEBUG_UART, USART_FLAG_TC) == RESET) {} + } +} + +i2c_result_t i2c_write_16bits(uint8_t slave_addr, uint8_t reg_addr, uint8_t data[2]) { + i2c_state_t state = I2C_STATE_START; + uint16_t timeout = 0; + uint8_t retry_count = 0; + + /* parameter validation */ + if (data == NULL || slave_addr > 0x7F) { + return I2C_RESULT_INVALID_PARAM; + } + + while (retry_count < I2C_MAX_RETRY) { + switch (state) { + case I2C_STATE_START: + timeout = 0; + + /* wait for bus to be idle */ + while (i2c_flag_get(I2C0, I2C_FLAG_I2CBSY) && (timeout < I2C_TIME_OUT)) { + timeout++; + } + if (timeout >= I2C_TIME_OUT) { + state = I2C_STATE_ERROR; + break; + } + + i2c_start_on_bus(I2C0); + timeout = 0; + state = I2C_STATE_SEND_ADDRESS; + break; + + case I2C_STATE_SEND_ADDRESS: + /* wait for start condition to be sent. SBSEND flag */ + while((!i2c_flag_get(I2C0, I2C_FLAG_SBSEND)) && (timeout < I2C_TIME_OUT)) { + timeout++; + } + if (timeout >= I2C_TIME_OUT) { + state = I2C_STATE_ERROR; + break; + } + + /* send slave address */ + i2c_master_addressing(I2C0, slave_addr << 1, I2C_TRANSMITTER); + timeout = 0; + state = I2C_STATE_CLEAR_ADDRESS; + break; + + case I2C_STATE_CLEAR_ADDRESS: + /* wait for address to be acknowledged.ADDSEND set means i2c slave sends ACK */ + while ((!i2c_flag_get(I2C0, I2C_FLAG_ADDSEND)) && (!i2c_flag_get(I2C0, I2C_FLAG_AERR)) && (timeout < I2C_TIME_OUT)) { + timeout++; + } + if (timeout >= I2C_TIME_OUT) { + state = I2C_STATE_ERROR; + break; + } else if (i2c_flag_get(I2C0, I2C_FLAG_ADDSEND)) + { + i2c_flag_clear(I2C0, I2C_FLAG_ADDSEND); + timeout =0; + state = I2C_STATE_TRANSMIT_REG; + break; + } else { + i2c_flag_clear(I2C0, I2C_FLAG_AERR); + timeout =0; +#ifdef DEBUG_VERBOES + printf("IIC write failed for Error Slave Address. \n"); +#endif + return I2C_RESULT_NACK; + } + + case I2C_STATE_TRANSMIT_REG: + /* wait until the transmit data buffer is empty */ + while ((!i2c_flag_get(I2C0, I2C_FLAG_TBE)) && (timeout < I2C_TIME_OUT)) { + timeout++; + } + if (timeout >= I2C_TIME_OUT) { + state = I2C_STATE_ERROR; + break; + } + + /* send register address */ + i2c_data_transmit(I2C0, reg_addr); + timeout = 0; + state = I2C_STATE_TRANSMIT_DATA; + break; + + case I2C_STATE_TRANSMIT_DATA: + /* wait until the transmit data buffer is empty */ + while ((!i2c_flag_get(I2C0, I2C_FLAG_TBE)) && (timeout < I2C_TIME_OUT)) { + timeout++; + } + if (timeout >= I2C_TIME_OUT) { + state = I2C_STATE_ERROR; + break; + } + + /* send register MSB value */ + i2c_data_transmit(I2C0, data[0]); + timeout = 0; + + /* wait until the transmit data buffer is empty */ + while ((!i2c_flag_get(I2C0, I2C_FLAG_TBE)) && (timeout < I2C_TIME_OUT)) { + timeout++; + } + if (timeout >= I2C_TIME_OUT) { + state = I2C_STATE_ERROR; + break; + } + + if (i2c_flag_get(I2C0, I2C_FLAG_AERR)) { + i2c_stop_on_bus(I2C0); + return I2C_RESULT_NACK; + } else if (i2c_flag_get(I2C0, I2C_FLAG_BERR) || i2c_flag_get(I2C0, I2C_FLAG_LOSTARB)) { + // 可按需清标志 + i2c_stop_on_bus(I2C0); + return I2C_RESULT_ERROR; + } + + /* send register LSB value */ + i2c_data_transmit(I2C0, data[1]); + timeout = 0; + + /* wait until BTC bit is set */ + while (!i2c_flag_get(I2C0, I2C_FLAG_BTC) && (timeout < I2C_TIME_OUT)) { + timeout++; + } + if (timeout >= I2C_TIME_OUT) { + state = I2C_STATE_ERROR; + break; + } + + state = I2C_STATE_STOP; + break; + + case I2C_STATE_STOP: + /* send a stop condition to I2C bus */ + i2c_stop_on_bus(I2C0); + + timeout = 0; + while ((I2C_CTL0(I2C0) & I2C_CTL0_STOP) && (timeout < I2C_TIME_OUT)) { + timeout++; + } + if (timeout >= I2C_TIME_OUT) { + state = I2C_STATE_ERROR; + break; + } + + /* i2c master sends STOP signal successfully */ + /* success */ + return I2C_RESULT_SUCCESS; + + case I2C_STATE_ERROR: + /* send a stop condition to I2C bus */ + i2c_stop_on_bus(I2C0); + + timeout = 0; + while ((I2C_CTL0(I2C0) & I2C_CTL0_STOP) && (timeout < I2C_TIME_OUT)) { + timeout++; + } + if (timeout >= I2C_TIME_OUT) { + return I2C_RESULT_ERROR; + } + + i2c_flag_clear(I2C0, I2C_FLAG_AERR); + i2c_flag_clear(I2C0, I2C_FLAG_BERR); + i2c_flag_clear(I2C0, I2C_FLAG_LOSTARB); + + retry_count ++; + if (retry_count >= I2C_MAX_RETRY) + { +#ifdef DEBUG_VERBOES + printf("IIC write failed after %d retries\n", I2C_MAX_RETRY); +#endif + return I2C_RESULT_ERROR; + } + + /* reset state machine for retry */ + state = I2C_STATE_START; + timeout = 0; + + /* small delay before retry */ + delay_10us(10); + break; + + default: + state = I2C_STATE_START; + break; + } + } + return I2C_RESULT_TIMEOUT; +} + +i2c_result_t i2c_read_16bits(uint8_t slave_addr, uint8_t reg_addr, uint8_t *data) { + i2c_state_t state = I2C_STATE_START; + uint16_t timeout = 0; + uint8_t retry_count = 0; + bool write_phase = true; + + // 参数检查:防止空指针和非法地址 + if (data == NULL || slave_addr > 0x7F) { + return I2C_RESULT_INVALID_PARAM; + } + + /* enable acknowledge */ + i2c_ack_config(I2C0, I2C_ACK_ENABLE); + + while (retry_count < (uint8_t)I2C_MAX_RETRY) { + switch (state) { + case I2C_STATE_START: + timeout = 0; + + // wait for bus to be idle + while (i2c_flag_get(I2C0, I2C_FLAG_I2CBSY) && (timeout < I2C_TIME_OUT)) { + timeout++; + } + if (timeout >= I2C_TIME_OUT) { + state = I2C_STATE_ERROR; + break; + } + + // send start condition + i2c_start_on_bus(I2C0); + state = I2C_STATE_SEND_ADDRESS; + timeout = 0; + break; + + case I2C_STATE_SEND_ADDRESS: + /* wait for start condition to be sent */ + while ((!i2c_flag_get(I2C0, I2C_FLAG_SBSEND)) && (timeout < I2C_TIME_OUT)) { + timeout++; + } + if (timeout >= I2C_TIME_OUT) { + state = I2C_STATE_ERROR; + break; + } + + // send slave address + if (write_phase) { + /* write phase: send address with write bit */ + i2c_master_addressing(I2C0, (slave_addr << 1), I2C_TRANSMITTER); + } else { + /* read phase: send address with read bit */ + i2c_master_addressing(I2C0, (slave_addr << 1) | 0x01, I2C_RECEIVER); + } + + state = I2C_STATE_CLEAR_ADDRESS; + timeout = 0; + break; + + case I2C_STATE_CLEAR_ADDRESS: + /* wait for address to be acknowledged */ + while ((!i2c_flag_get(I2C0, I2C_FLAG_ADDSEND)) && (timeout < I2C_TIME_OUT)) { + timeout++; + } + if (timeout >= I2C_TIME_OUT) { + state = I2C_STATE_ERROR; + break; + } + + if (write_phase) { + /* clear address flag (write phase) */ + i2c_flag_clear(I2C0, I2C_FLAG_ADDSEND); + state = I2C_STATE_TRANSMIT_DATA; + } else { + /* READ phase for 2 bytes: set POS=NEXT and disable ACK BEFORE clearing ADDR */ + i2c_ackpos_config(I2C0, I2C_ACKPOS_NEXT); + i2c_ack_config(I2C0, I2C_ACK_DISABLE); + + /* now clear address flag to release SCL and enter data phase */ + i2c_flag_clear(I2C0, I2C_FLAG_ADDSEND); + + state = I2C_STATE_RECEIVE_DATA; + } + + timeout = 0; + break; + + case I2C_STATE_TRANSMIT_DATA: + /* wait for transmit buffer to be empty */ + while ((!i2c_flag_get(I2C0, I2C_FLAG_TBE)) && (timeout < I2C_TIME_OUT)) { + timeout++; + } + if (timeout >= I2C_TIME_OUT) { + state = I2C_STATE_ERROR; + break; + } + + /* send register address */ + i2c_data_transmit(I2C0, reg_addr); + state = I2C_STATE_RESTART; + timeout = 0; + break; + + case I2C_STATE_RESTART: + /* wait for byte transfer complete BTC: Bit Transfer Complete */ + while ((!i2c_flag_get(I2C0, I2C_FLAG_BTC)) && (timeout < I2C_TIME_OUT)) { + timeout++; + } + if (timeout >= I2C_TIME_OUT) { + state = I2C_STATE_ERROR; + break; + } + + /* generate repeated start condition */ + i2c_start_on_bus(I2C0); + + /* wait for repeated start condition to be sent */ + timeout = 0; + while ((!i2c_flag_get(I2C0, I2C_FLAG_SBSEND)) && (timeout < I2C_TIME_OUT)) { + timeout++; + } + if (timeout >= I2C_TIME_OUT) { + state = I2C_STATE_ERROR; + break; + } + + /* send slave address with read bit (R/W bit is set by library) */ + i2c_master_addressing(I2C0, (slave_addr << 1), I2C_RECEIVER); + + /* switch to read phase */ + write_phase = false; + state = I2C_STATE_CLEAR_ADDRESS; + timeout = 0; + break; + + case I2C_STATE_RECEIVE_DATA: + /* Wait for BTC (both bytes received) */ + while ((!i2c_flag_get(I2C0, I2C_FLAG_BTC)) && (timeout < I2C_TIME_OUT)) { + timeout++; + } + if (timeout >= I2C_TIME_OUT) { + state = I2C_STATE_ERROR; + break; + } + + /* Send STOP before reading the last two bytes */ + i2c_stop_on_bus(I2C0); + + /* Read the two bytes back-to-back */ + data[0] = i2c_data_receive(I2C0); + data[1] = i2c_data_receive(I2C0); + + state = I2C_STATE_STOP; + break; + + case I2C_STATE_STOP: + /* wait for stop condition to complete */ + while ((I2C_CTL0(I2C0) & I2C_CTL0_STOP) && (timeout < I2C_TIME_OUT)) { + timeout++; + } + if (timeout >= I2C_TIME_OUT) { + state = I2C_STATE_ERROR; + break; + } + + /* i2c master sends STOP signal successfully */ + /* success */ + return I2C_RESULT_SUCCESS; + + case I2C_STATE_ERROR: + /* send stop condition to release bus */ + i2c_stop_on_bus(I2C0); + + retry_count++; + if (retry_count >= I2C_MAX_RETRY) { +#ifdef DEBUG_VERBOES + printf("IIC read failed after %d retries\n", I2C_RETRY_MAX); +#endif + return I2C_RESULT_ERROR; + } + + /* reset state machine for retry */ + state = I2C_STATE_START; + write_phase = true; + timeout = 0; + + /* small delay before retry */ + delay_10us(10); + break; + + default: + state = I2C_STATE_START; + break; + } + } + return I2C_RESULT_TIMEOUT; +} + +#ifdef DEBUG_VERBOSE +/*! + \brief get status string for debugging + \param[in] status: i2c_status_t value + \param[out] none + \retval const char* status string +*/ +const char* i2c_get_status_string(i2c_result_t status) { + switch (status) { + case I2C_RESULT_SUCCESS: + return "SUCCESS"; + case I2C_RESULT_TIMEOUT: + return "TIMEOUT"; + case I2C_RESULT_NACK: + return "NACK"; + case I2C_RESULT_BUS_BUSY: + return "BUS_BUSY"; + case I2C_RESULT_ERROR: + return "ERROR"; + case I2C_RESULT_INVALID_PARAM: + return "INVALID_PARAM"; + default: + return "UNKNOWN"; + } +} +#endif diff --git a/Src/ldc1612.c b/Src/ldc1612.c new file mode 100644 index 0000000..ee2cbe2 --- /dev/null +++ b/Src/ldc1612.c @@ -0,0 +1,236 @@ +// +// Created by dell on 24-12-3. +// + +#include "ldc1612.h" + +/*! + \brief 写入寄存器 + \param[in] reg_addr: 寄存器地址 + \param[in] value: 写入值 + \param[out] none + \retval i2c_result_t +*/ +static i2c_result_t ldc1612_write_register(uint8_t reg_addr, uint16_t value) { + uint8_t data[2]; + data[0] = (value >> 8) & 0xFF; + data[1] = value & 0xFF; + + return LDC1612_IIC_WRITE_16BITS(LDC1612_ADDR, reg_addr, data); +} + +/*! + \brief 读取寄存器 + \param[in] reg_addr: 寄存器地址 + \param[out] value: 读取值指针 + \retval i2c_status_t +*/ +static i2c_result_t ldc1612_read_register(uint8_t reg_addr, uint16_t *value) { + uint8_t data[2]; + i2c_result_t status; + + if (value == NULL) { + return I2C_RESULT_INVALID_PARAM; + } + + status = LDC1612_IIC_READ_16BITS(LDC1612_ADDR, reg_addr, data); + if (status == I2C_RESULT_SUCCESS) { + *value = ((uint16_t)data[0] << 8) | data[1]; + } + + return status; +} + +/*! + \brief 计算并获取频率分频值 + \param[in] channel: 通道号 + \param[out] none + \retval 计算得到的频率分频值 +*/ +static uint16_t ldc1612_calculate_freq_divider(uint8_t channel) { + uint16_t value; + uint16_t fin_div, freq_div; + float sensor_freq; + + sensor_freq = 1 / (2 * 3.14 * sqrt(COIL_L_UH * COIL_C_PF * pow(10, -18))) * pow(10, -6); + fin_div = (uint16_t) (sensor_freq / 8.75 + 1); + + if (fin_div * 4 < 40) { + freq_div = 2; + } else { + freq_div = 4; + } + + value = fin_div << 12; + value |= freq_div; + + return value; +} + +uint16_t ldc1612_get_manufacturer_id(void) { + uint8_t data[2] = {0}; + + LDC1612_IIC_READ_16BITS(LDC1612_ADDR, READ_MANUFACTURER_ID, data); + return (data[0] << 8) | data[1]; +} + +uint16_t ldc1612_get_deveice_id(void) { + uint8_t data[2] = {0}; + + LDC1612_IIC_READ_16BITS(LDC1612_ADDR, READ_DEVICE_ID, data); + return (data[0] << 8) | data[1]; +} + +/** @brief reset sensor. + + * */ +ldc1612_status_t ldc1612_reset_sensor(void) { + i2c_result_t state = ldc1612_write_register(SENSOR_RESET_REG, LDC1612_RESET_DEV); + return (state == I2C_RESULT_SUCCESS) ? LDC1612_STATUS_SUCCESS : LDC1612_STATUS_ERROR; +} + +ldc1612_status_t ldc1612_init(void) { + i2c_result_t i2c_status; + uint16_t manufacturer_id, device_id; + + /* reset LDC1612 sensor */ + i2c_status = ldc1612_reset_sensor(); + if (i2c_status != I2C_RESULT_SUCCESS) { + return LDC1612_STATUS_ERROR; + } + + delay_ms(100); + + manufacturer_id = ldc1612_get_manufacturer_id(); + device_id = ldc1612_get_deveice_id(); + + if (manufacturer_id != 0x5449 || device_id != 0x3055) { + return LDC1612_STATUS_ERROR; + } + + return LDC1612_STATUS_SUCCESS; +} + +/*! + \brief 配置单通道模式 + \param[in] channel: 通道号 (0或1) + \param[out] none + \retval ldc1612_status_t +*/ +ldc1612_status_t ldc1612_config_single_channel(uint8_t channel) { + i2c_result_t status; + + if (channel > 1) { + return LDC1612_STATUS_INVALID_PARAM; + } + + /* 配置顺序严格按照TI官方文档要求 */ + + /* Step 1: 确保传感器处于睡眠模式 - 配置前必须 */ + status = ldc1612_write_register(SENSOR_CONFIG_REG, LDC1612_SLEEP_MODE); + if (status != I2C_RESULT_SUCCESS) return LDC1612_STATUS_ERROR; + delay_ms(10); + + /* Step 2: 配置频率分频 - 必须在其他配置之前 */ + uint16_t freq_divider = ldc1612_calculate_freq_divider(channel); + ldc1612_write_register(SET_FREQ_REG_START + channel, freq_divider); + delay_ms(5); + + /* Step 3: 配置LC稳定时间 - 影响测量精度 */ + ldc1612_write_register(SET_LC_STABILIZE_REG_START + channel, LC_STABILIZE_TIME_CH0); + + /* Step 4: 配置转换时间 - 影响测量速度和精度 */ + ldc1612_write_register(SET_CONVERSION_TIME_REG_START + channel, LDC1612_CONVERSION_TIME_CH0); + + /* Step 5: 配置转换偏移 */ + ldc1612_write_register(SET_CONVERSION_OFFSET_REG_START + channel, SET_CONVERSION_OFFSET_CH0); + + /* Step 6: 配置驱动电流 - 影响传感器灵敏度 */ + ldc1612_write_register(SET_DRIVER_CURRENT_REG + channel, LDC1612_DRIVE_CURRENT); + + /* Step 7: 配置多路复用器 - 设置通道选择和滤波 */ + // ldc1612_configure_mux_register(LDC1612_MUX_AUTOSCAN_DISABLE, LDC1612_MUX_RR_SEQUENCE_0, LDC1612_MUX_FILTER_ALL_LOW, LDC1612_MUX_FILTER_NONE); + ldc1612_write_register(MUL_CONFIG_REG, LDC1612_MUX_CONFIG); + + /* Step 8: 配置错误输出 */ + ldc1612_write_register(ERROR_CONFIG_REG, LDC1612_ERROR_CONFIG); + + /* Step 9: 最后启动传感器 - 必须最后一步 */ + status = ldc1612_write_register(SENSOR_CONFIG_REG, LDC1612_SENSOR_CONFIG_CH0); + if (status != I2C_RESULT_SUCCESS) return LDC1612_STATUS_ERROR; + + /* Step 10: 等待传感器稳定 */ + delay_ms(50); + + return LDC1612_STATUS_SUCCESS; +} + +/** @brief read the raw channel result from register. + @param channel LDC1612 has total two channels. + @param result raw data + * */ +uint32_t ldc1612_get_raw_channel_result(uint8_t channel) { + uint32_t raw_value = 0; + uint8_t value[2] = {0}; + + /* Read MSW */ + LDC1612_IIC_READ_16BITS(LDC1612_ADDR, CONVERTION_RESULT_REG_START + channel, value); + raw_value |= (uint32_t)(((uint16_t)value[0] << 8) | value[1]) << 16; + + /* Read LSW */ + LDC1612_IIC_READ_16BITS(LDC1612_ADDR, CONVERTION_RESULT_REG_START + channel + 1, value); + raw_value |= (uint32_t)(((uint16_t)value[0] << 8) | value[1]); + + uint32_t calibration_value = raw_value & 0x0FFFFFFF; + if (calibration_value == 0x0FFFFFFF) { + return 0xF0000000; /* No coil */ + } + uint8_t error_code = (uint8_t)(raw_value >> 24); + if (error_code & 0x80) return 0x80000000; /* Under range */ + if (error_code & 0x40) return 0x40000000; /* Over range */ + if (error_code & 0x20) return 0x20000000; /* Watchdog */ + if (error_code & 0x10) return 0x10000000; /* Amplitude error */ + + return raw_value; +} + + +// void ldc1612_drvie_current_detect(uint8_t channel) { +// uint8_t data[2] = {0}; +// uint16_t init_value = 0 , drive_current = 0; + +// ldc1612_set_sensor_config(LDC1612_SLEEP_MODE); +// ldc1612_configure_frequency(channel); +// LDC1612_IIC_READ_16BITS(LDC1612_ADDR, SENSOR_CONFIG_REG, data); +// ldc1612_set_sensor_config(LDC1612_SLEEP_MODE); +// ldc1612_set_sensor_config(LDC1612_SENSOR_CONFIG_CH0); //0x1A --0x1601 +// delay_ms(10); +// LDC1612_IIC_READ_16BITS(LDC1612_ADDR, SET_DRIVER_CURRENT_REG, data); + +// init_value = (((data[0] << 8) | data[1]) >> 6) & 0x1F; +// drive_current = (init_value << 11) | 0x0000; +// printf("init value: 0x%x\tdrive current: 0x%x\n", init_value, drive_current); +// } + +/** @brief Get sensor status register + @return Status register value + * */ +uint16_t ldc1612_get_sensor_status(void) { + uint8_t data[2] = {0}; + LDC1612_IIC_READ_16BITS(LDC1612_ADDR, SENSOR_STATUS_REG, data); + return (data[0] << 8) | data[1]; +} + +/** @brief Check if data is ready for specific channel + @param channel Channel to check (0 or 1) + @return true if data is ready, false otherwise + * */ +bool ldc1612_is_data_ready(uint8_t channel) { + uint16_t status = ldc1612_get_sensor_status(); + if (channel == 0) { + return (status & 0x0040) != 0; // DRDY_0 bit + } else if (channel == 1) { + return (status & 0x0080) != 0; // DRDY_1 bit + } + return false; +} \ No newline at end of file diff --git a/Src/led.c b/Src/led.c index 7028315..1661867 100644 --- a/Src/led.c +++ b/Src/led.c @@ -1,20 +1,57 @@ #include "led.h" +/** + * @brief LED心跳指示灯功能 + * @details 实现类似心跳的LED闪烁模式:快闪两次然后暂停 + * 适合在SysTick中断中调用,通过计数器控制闪烁节拍 + * @note 假设SysTick中断频率为1ms,心跳周期约为2秒 + * 心跳模式:亮200ms->灭200ms->亮200ms->灭1400ms(循环) + */ +void led_heart_beat(void) +{ + static uint16_t heart_beat_counter = 0; + + // 心跳周期:2000ms (假设SysTick为1ms中断) + // 模式:亮200ms -> 灭200ms -> 亮200ms -> 灭1400ms + heart_beat_counter++; + + if (heart_beat_counter <= 200) { + // 第一次亮:0-200ms + led_on(); + } + else if (heart_beat_counter <= 400) { + // 第一次灭:200-400ms + led_off(); + } + else if (heart_beat_counter <= 600) { + // 第二次亮:400-600ms + led_on(); + } + else if (heart_beat_counter <= 2000) { + // 长时间灭:600-2000ms + led_off(); + } + else { + // 重置计数器,开始新的心跳周期 + heart_beat_counter = 0; + } +} + void led_init(void) { rcu_periph_clock_enable(LED_RCU); gpio_mode_set(LED_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, LED_PIN); gpio_output_options_set(LED_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, LED_PIN); - gpio_bit_reset(LED_PORT, LED_PIN); -} - -void led_on(void) { gpio_bit_set(LED_PORT, LED_PIN); } -void led_off(void) { +void led_on(void) { gpio_bit_reset(LED_PORT, LED_PIN); } +void led_off(void) { + gpio_bit_set(LED_PORT, LED_PIN); +} + void led_toggle(void) { gpio_bit_toggle(LED_PORT, LED_PIN); } diff --git a/Src/main.c b/Src/main.c index fe3ea4a..b97a62a 100644 --- a/Src/main.c +++ b/Src/main.c @@ -36,7 +36,11 @@ OF SUCH DAMAGE. #include "systick.h" #include "uart.h" #include "led.h" +#include "command.h" #include +#include "i2c.h" +#include "board_config.h" +#include "ldc1612.h" /*! \brief main function @@ -46,17 +50,43 @@ OF SUCH DAMAGE. */ int main(void) { - systick_config(); - uart0_init(115200); - // uart1_init(115200); // 如需使用USART1请初始化 - // printf("Hello USART0!\r\n"); - // uart_set_printf_port(UART_PRINTF_USART1); // 切换printf到USART1 - // uart_set_printf_port(UART_PRINTF_BOTH); // 同时输出到USART0和USART1 + setbuf(stdout, NULL); + systick_config(); + rs485_init(); led_init(); + +#ifdef DEBUG_VERBOSE + char hello_world[] = {"Hello World!\r\n"}; + + for (uint8_t i = 0; i < sizeof(hello_world); i++) + { + while (usart_flag_get(RS485_PHY, USART_FLAG_TBE) == RESET) {} + usart_data_transmit(RS485_PHY, hello_world[i]); + } + + while (usart_flag_get(RS485_PHY, USART_FLAG_TC) == RESET) {} + + +#endif + + i2c_config(); + +#ifdef DEBUG_VERBOSE + i2c_scan(); + + i2c_bus_reset(); +#endif + + ldc1612_init(); + ldc1612_config_single_channel(CHANNEL_0); + while(1){ - led_toggle(); - delay_ms(200); + command_process(); + delay_ms(10); + if (g_sensor_report_enabled) { + eddy_current_report(); + } } } diff --git a/Src/sensor_example.c b/Src/sensor_example.c new file mode 100644 index 0000000..d0c3c9d --- /dev/null +++ b/Src/sensor_example.c @@ -0,0 +1,224 @@ +// +// Sensor Usage Example +// 传感器使用示例代码 +// + +#include "ldc1612.h" +// #include "tmp112.h" +#include "i2c.h" + +/*! + \brief 传感器初始化示例 + \param[in] none + \param[out] none + \retval none +*/ +void sensors_init_example(void) { + ldc1612_status_t ldc_status; + // tmp112a_status_t tmp_status; + + /* 初始化I2C总线 */ + i2c_status_t i2c_status = i2c_config(); + if (i2c_status != I2C_STATUS_SUCCESS) { + // 使用串口发送错误信息 + const char* error_msg = "I2C init failed\r\n"; + for (uint8_t i = 0; error_msg[i] != '\0'; i++) { + while (usart_flag_get(RS485_PHY, USART_FLAG_TBE) == RESET) {} + usart_data_transmit(RS485_PHY, error_msg[i]); + } + while (usart_flag_get(RS485_PHY, USART_FLAG_TC) == RESET) {} + return; + } + + /* 扫描I2C总线 */ + // i2c_scan(); + + /* 初始化LDC1612 */ + ldc_status = ldc1612_init(); + if (ldc_status == LDC1612_STATUS_SUCCESS) { + const char* msg = "LDC1612 init success\r\n"; + for (uint8_t i = 0; msg[i] != '\0'; i++) { + while (usart_flag_get(RS485_PHY, USART_FLAG_TBE) == RESET) {} + usart_data_transmit(RS485_PHY, msg[i]); + } + while (usart_flag_get(RS485_PHY, USART_FLAG_TC) == RESET) {} + + /* 配置通道0 */ + ldc_status = ldc1612_config_single_channel(LDC1612_CHANNEL_0); + if (ldc_status != LDC1612_STATUS_SUCCESS) { + const char* error = "LDC1612 config failed\r\n"; + for (uint8_t i = 0; error[i] != '\0'; i++) { + while (usart_flag_get(RS485_PHY, USART_FLAG_TBE) == RESET) {} + usart_data_transmit(RS485_PHY, error[i]); + } + while (usart_flag_get(RS485_PHY, USART_FLAG_TC) == RESET) {} + } + } else { + const char* error = "LDC1612 init failed: "; + for (uint8_t i = 0; error[i] != '\0'; i++) { + while (usart_flag_get(RS485_PHY, USART_FLAG_TBE) == RESET) {} + usart_data_transmit(RS485_PHY, error[i]); + } + const char* status_str = ldc1612_get_status_string(ldc_status); + for (uint8_t i = 0; status_str[i] != '\0'; i++) { + while (usart_flag_get(RS485_PHY, USART_FLAG_TBE) == RESET) {} + usart_data_transmit(RS485_PHY, status_str[i]); + } + const char* newline = "\r\n"; + for (uint8_t i = 0; newline[i] != '\0'; i++) { + while (usart_flag_get(RS485_PHY, USART_FLAG_TBE) == RESET) {} + usart_data_transmit(RS485_PHY, newline[i]); + } + while (usart_flag_get(RS485_PHY, USART_FLAG_TC) == RESET) {} + } + + /* 初始化TMP112A */ + // tmp_status = tmp112a_init(); + // if (tmp_status == TMP112A_STATUS_SUCCESS) { + // const char* msg = "TMP112A init success\r\n"; + // for (uint8_t i = 0; msg[i] != '\0'; i++) { + // while (usart_flag_get(RS485_PHY, USART_FLAG_TBE) == RESET) {} + // usart_data_transmit(RS485_PHY, msg[i]); + // } + // while (usart_flag_get(RS485_PHY, USART_FLAG_TC) == RESET) {} + + // /* 设置温度阈值 */ + // tmp_status = tmp112a_set_thresholds(-10.0f, 50.0f); + // if (tmp_status != TMP112A_STATUS_SUCCESS) { + // const char* error = "TMP112A threshold config failed\r\n"; + // for (uint8_t i = 0; error[i] != '\0'; i++) { + // while (usart_flag_get(RS485_PHY, USART_FLAG_TBE) == RESET) {} + // usart_data_transmit(RS485_PHY, error[i]); + // } + // while (usart_flag_get(RS485_PHY, USART_FLAG_TC) == RESET) {} + // } + // } else { + // const char* error = "TMP112A init failed: "; + // for (uint8_t i = 0; error[i] != '\0'; i++) { + // while (usart_flag_get(RS485_PHY, USART_FLAG_TBE) == RESET) {} + // usart_data_transmit(RS485_PHY, error[i]); + // } + // const char* status_str = tmp112a_get_status_string(tmp_status); + // for (uint8_t i = 0; status_str[i] != '\0'; i++) { + // while (usart_flag_get(RS485_PHY, USART_FLAG_TBE) == RESET) {} + // usart_data_transmit(RS485_PHY, status_str[i]); + // } + // const char* newline = "\r\n"; + // for (uint8_t i = 0; newline[i] != '\0'; i++) { + // while (usart_flag_get(RS485_PHY, USART_FLAG_TBE) == RESET) {} + // usart_data_transmit(RS485_PHY, newline[i]); + // } + // while (usart_flag_get(RS485_PHY, USART_FLAG_TC) == RESET) {} + // } +} + +/*! + \brief 传感器读取示例 + \param[in] none + \param[out] none + \retval none +*/ +void sensors_read_example(void) { + ldc1612_result_t ldc_result; + // tmp112a_result_t tmp_result; + ldc1612_status_t ldc_status; + // tmp112a_status_t tmp_status; + + /* 读取LDC1612数据 */ + ldc_status = ldc1612_read_channel(LDC1612_CHANNEL_0, &ldc_result); + if (ldc_status == LDC1612_STATUS_SUCCESS) { + if (!ldc_result.error_flag) { + const char* msg = "LDC1612 Data: 0x"; + for (uint8_t i = 0; msg[i] != '\0'; i++) { + while (usart_flag_get(RS485_PHY, USART_FLAG_TBE) == RESET) {} + usart_data_transmit(RS485_PHY, msg[i]); + } + + /* 发送32位十六进制数据 */ + uint8_t hex_chars[] = "0123456789ABCDEF"; + for (int8_t i = 7; i >= 0; i--) { + uint8_t nibble = (ldc_result.frequency >> (i * 4)) & 0x0F; + while (usart_flag_get(RS485_PHY, USART_FLAG_TBE) == RESET) {} + usart_data_transmit(RS485_PHY, hex_chars[nibble]); + } + + const char* newline = "\r\n"; + for (uint8_t i = 0; newline[i] != '\0'; i++) { + while (usart_flag_get(RS485_PHY, USART_FLAG_TBE) == RESET) {} + usart_data_transmit(RS485_PHY, newline[i]); + } + while (usart_flag_get(RS485_PHY, USART_FLAG_TC) == RESET) {} + } else { + const char* error = "LDC1612 Error Code: 0x"; + for (uint8_t i = 0; error[i] != '\0'; i++) { + while (usart_flag_get(RS485_PHY, USART_FLAG_TBE) == RESET) {} + usart_data_transmit(RS485_PHY, error[i]); + } + + uint8_t hex_chars[] = "0123456789ABCDEF"; + while (usart_flag_get(RS485_PHY, USART_FLAG_TBE) == RESET) {} + usart_data_transmit(RS485_PHY, hex_chars[(ldc_result.error_code >> 4) & 0x0F]); + while (usart_flag_get(RS485_PHY, USART_FLAG_TBE) == RESET) {} + usart_data_transmit(RS485_PHY, hex_chars[ldc_result.error_code & 0x0F]); + + const char* newline = "\r\n"; + for (uint8_t i = 0; newline[i] != '\0'; i++) { + while (usart_flag_get(RS485_PHY, USART_FLAG_TBE) == RESET) {} + usart_data_transmit(RS485_PHY, newline[i]); + } + while (usart_flag_get(RS485_PHY, USART_FLAG_TC) == RESET) {} + } + } + + /* 读取TMP112A数据 */ + // tmp_status = tmp112a_read_temperature(&tmp_result); + // if (tmp_status == TMP112A_STATUS_SUCCESS) { + // const char* msg = "Temperature: "; + // for (uint8_t i = 0; msg[i] != '\0'; i++) { + // while (usart_flag_get(RS485_PHY, USART_FLAG_TBE) == RESET) {} + // usart_data_transmit(RS485_PHY, msg[i]); + // } + + // /* 简单的温度显示(整数部分) */ + // int16_t temp_int = (int16_t)tmp_result.temperature_c; + // if (temp_int < 0) { + // while (usart_flag_get(RS485_PHY, USART_FLAG_TBE) == RESET) {} + // usart_data_transmit(RS485_PHY, '-'); + // temp_int = -temp_int; + // } + + // if (temp_int >= 100) { + // while (usart_flag_get(RS485_PHY, USART_FLAG_TBE) == RESET) {} + // usart_data_transmit(RS485_PHY, '0' + (temp_int / 100)); + // temp_int %= 100; + // } + // if (temp_int >= 10) { + // while (usart_flag_get(RS485_PHY, USART_FLAG_TBE) == RESET) {} + // usart_data_transmit(RS485_PHY, '0' + (temp_int / 10)); + // temp_int %= 10; + // } + // while (usart_flag_get(RS485_PHY, USART_FLAG_TBE) == RESET) {} + // usart_data_transmit(RS485_PHY, '0' + temp_int); + + // const char* unit = " C"; + // for (uint8_t i = 0; unit[i] != '\0'; i++) { + // while (usart_flag_get(RS485_PHY, USART_FLAG_TBE) == RESET) {} + // usart_data_transmit(RS485_PHY, unit[i]); + // } + + // if (tmp_result.alert_flag) { + // const char* alert = " [ALERT]"; + // for (uint8_t i = 0; alert[i] != '\0'; i++) { + // while (usart_flag_get(RS485_PHY, USART_FLAG_TBE) == RESET) {} + // usart_data_transmit(RS485_PHY, alert[i]); + // } + // } + + // const char* newline = "\r\n"; + // for (uint8_t i = 0; newline[i] != '\0'; i++) { + // while (usart_flag_get(RS485_PHY, USART_FLAG_TBE) == RESET) {} + // usart_data_transmit(RS485_PHY, newline[i]); + // } + // while (usart_flag_get(RS485_PHY, USART_FLAG_TC) == RESET) {} + // } +} diff --git a/Src/soft_i2c.c b/Src/soft_i2c.c new file mode 100644 index 0000000..05544c2 --- /dev/null +++ b/Src/soft_i2c.c @@ -0,0 +1,234 @@ +// +// Created by dell on 24-12-28. +// + +#include "soft_i2c.h" + +/*! + \brief delay + \param[in] none + \param[out] none + \retval none +*/ +void soft_i2c_delay(void) { + delay_10us(2); // Adjust delay as needed + /* delay to freq + * 15KHz: delay_us(20); + * 65KHz: delay_us(1); + */ +} + +/*! + \brief configure the software IIC GPIO + \param[in] none + \param[out] none + \retval none +*/ +void soft_i2c_config(void) { + rcu_periph_clock_enable(RCU_GPIO_I2C); + + gpio_mode_set(I2C_SCL_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLUP, I2C_SCL_PIN); + gpio_output_options_set(I2C_SCL_PORT, GPIO_OTYPE_OD, GPIO_OSPEED_50MHZ, I2C_SCL_PIN); + + gpio_mode_set(I2C_SDA_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLUP, I2C_SDA_PIN); + gpio_output_options_set(I2C_SDA_PORT, GPIO_OTYPE_OD, GPIO_OSPEED_50MHZ, I2C_SDA_PIN); + + I2C_SCL_HIGH(); + I2C_SDA_HIGH(); +} + +/*! + \brief generate I2C start signal + \param[in] none + \param[out] none + \retval none +*/ +void soft_i2c_start(void) { + I2C_SDA_HIGH(); + I2C_SCL_HIGH(); + soft_i2c_delay(); + I2C_SDA_LOW(); + soft_i2c_delay(); + I2C_SCL_LOW(); +} + +/*! + \brief generate I2C stop signal + \param[in] none + \param[out] none + \retval none +*/ +void soft_i2c_stop(void) { + I2C_SCL_LOW(); // 确保时钟为低 + I2C_SDA_LOW(); // 拉低数据线 + soft_i2c_delay(); + I2C_SCL_HIGH(); // 拉高时钟 + soft_i2c_delay(); + I2C_SDA_HIGH(); // 在时钟高电平时拉高数据线产生停止条件 + soft_i2c_delay(); // 添加缺失的延时 +} + +/*! + \brief send I2C ACK signal + \param[in] none + \param[out] none + \retval none +*/ +void soft_i2c_send_ack(void) { + // sda_out(); + I2C_SDA_LOW(); + soft_i2c_delay(); + I2C_SCL_HIGH(); + soft_i2c_delay(); + I2C_SCL_LOW(); + soft_i2c_delay(); + I2C_SDA_HIGH(); +} + +/*! + \brief send I2C NACK signal + \param[in] none + \param[out] none + \retval none +*/ +void soft_i2c_send_nack(void) { + I2C_SDA_HIGH(); + soft_i2c_delay(); + I2C_SCL_HIGH(); + soft_i2c_delay(); + I2C_SCL_LOW(); + soft_i2c_delay(); + I2C_SDA_HIGH(); +} + +/*! + \brief wait I2C ACK signal + \param[in] none + \param[out] none + \retval 0: ACK received, 1: ACK not received +*/ +uint8_t soft_i2c_wait_ack(void) { + I2C_SDA_HIGH(); // 释放SDA线,让从设备控制 + soft_i2c_delay(); + I2C_SCL_HIGH(); // 拉高时钟 + soft_i2c_delay(); + uint8_t ack = !I2C_SDA_READ(); // 读取ACK信号(低电平为ACK) + I2C_SCL_LOW(); // 拉低时钟 + soft_i2c_delay(); // 添加缺失的延时 + return ack; +} + +/*! + \brief send a byte via I2C + \param[in] byte: byte to be sent + \param[out] none + \retval none +*/ +void soft_i2c_send_byte(uint8_t byte) { + // sda_out(); + for (int i = 0; i < 8; i++) { + if (byte & 0x80) { + I2C_SDA_HIGH(); + } else { + I2C_SDA_LOW(); + } + byte <<= 1; + soft_i2c_delay(); + I2C_SCL_HIGH(); + soft_i2c_delay(); + I2C_SCL_LOW(); + soft_i2c_delay(); + } +} + +/*! + \brief receive a byte via I2C + \param[in] ack: 1: send ACK, 0: send NACK + \param[out] none + \retval received byte +*/ +uint8_t soft_i2c_receive_byte(uint8_t ack) { + uint8_t byte = 0; + I2C_SDA_HIGH(); + for (int i = 0; i < 8; i++) { + byte <<= 1; + I2C_SCL_HIGH(); + soft_i2c_delay(); + if (I2C_SDA_READ()) { + byte |= 0x01; + } + I2C_SCL_LOW(); + soft_i2c_delay(); + } + if (ack) { + soft_i2c_send_ack(); + } else { + soft_i2c_send_nack(); + } + return byte; +} + +uint8_t soft_i2c_write_16bits(uint8_t slave_addr, uint8_t reg_addr, uint8_t data[2]) { + /* 参数验证 */ + if (data == NULL || slave_addr > 0x7F) { + return SOFT_I2C_FAIL; + } + + soft_i2c_start(); + soft_i2c_send_byte(slave_addr << 1); // 修复:左移1位,添加写位 + if (!soft_i2c_wait_ack()) { + soft_i2c_stop(); + return SOFT_I2C_FAIL; + } + soft_i2c_send_byte(reg_addr); + if (!soft_i2c_wait_ack()) { + soft_i2c_stop(); + return SOFT_I2C_FAIL; + } + soft_i2c_send_byte(data[0]); + if (!soft_i2c_wait_ack()) { + soft_i2c_stop(); + return SOFT_I2C_FAIL; + } + soft_i2c_send_byte(data[1]); + if (!soft_i2c_wait_ack()) { // 修复:添加错误处理 + soft_i2c_stop(); + return SOFT_I2C_FAIL; + } + soft_i2c_stop(); + return SOFT_I2C_OK; +} + +uint8_t soft_i2c_read_16bits(uint8_t slave_addr, uint8_t reg_addr, uint8_t *data) +{ + /* 参数验证 */ + if (data == NULL || slave_addr > 0x7F) { + return SOFT_I2C_FAIL; + } + + /* 写阶段:发送寄存器地址 */ + soft_i2c_start(); + soft_i2c_send_byte(slave_addr << 1); // 修复:左移1位,写操作 + if (!soft_i2c_wait_ack()) { + soft_i2c_stop(); + return SOFT_I2C_FAIL; + } + soft_i2c_send_byte(reg_addr); + if (!soft_i2c_wait_ack()) { + soft_i2c_stop(); + return SOFT_I2C_FAIL; + } + + /* 读阶段:重新开始并读取数据 */ + soft_i2c_start(); // 重新开始 + soft_i2c_send_byte((slave_addr << 1) | 0x01); // 修复:正确的读地址 + if (!soft_i2c_wait_ack()) { + soft_i2c_stop(); + return SOFT_I2C_FAIL; + } + soft_i2c_delay(); + data[0] = soft_i2c_receive_byte(1); // 第一个字节发送ACK + data[1] = soft_i2c_receive_byte(0); // 最后一个字节发送NACK + soft_i2c_stop(); + return SOFT_I2C_OK; +} \ No newline at end of file diff --git a/Src/systick.c b/Src/systick.c index aa39531..fd3d707 100644 --- a/Src/systick.c +++ b/Src/systick.c @@ -1,83 +1,118 @@ -/*! - \file systick.c - \brief the systick configuration file - - \version 2025-02-10, V2.4.0, demo for GD32E23x -*/ - -/* - Copyright (c) 2025, GigaDevice Semiconductor Inc. - - Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - - 1. Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - 2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - 3. Neither the name of the copyright holder nor the names of its contributors - may be used to endorse or promote products derived from this software without - specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, -INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT -NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY -OF SUCH DAMAGE. -*/ - +/** + * ************************************************************************ + * + * @file systick.c + * @author GD32 + * @brief 通过 SysTick 定时器进行微秒级别和毫秒级别的延时函数 + * + * ************************************************************************ + * @copyright Copyright (c) 2024 GD32 + * ************************************************************************ + */ #include "gd32e23x.h" #include "systick.h" -volatile static uint32_t delay; +volatile static uint32_t delay_count = 0; -/*! - \brief configure systick - \param[in] none - \param[out] none - \retval none -*/ +/** + * ************************************************************************ + * @brief 配置 SysTick 定时器 + * + * + * ************************************************************************ + */ void systick_config(void) { - /* setup systick timer for 1000Hz interrupts */ - if (SysTick_Config(SystemCoreClock / 1000U)){ - /* capture error */ - while (1){ - } - } - /* configure the systick handler priority */ + //设置了 SysTick 定时器的时钟源为 HCLK + systick_clksource_set(SYSTICK_CLKSOURCE_HCLK); + + // 配置SysTick为1ms周期中断 + // 注意:SysTick_Config会自动设置时钟源为HCLK,所以需要使用SystemCoreClock/1000 + SysTick_Config(SystemCoreClock / 1000U); // 1ms中断 NVIC_SetPriority(SysTick_IRQn, 0x00U); } -/*! - \brief delay a time in milliseconds - \param[in] count: count in milliseconds - \param[out] none - \retval none -*/ +/** + * ************************************************************************ + * @brief delay_ms 毫秒延时函数 + * + * @param[in] count 毫秒值 + * + * ************************************************************************ + */ +void delay_10us(uint32_t count) +{ + // 基于系统时钟的简单循环延时 + // 这是一个粗略的估计,实际延时可能有偏差 实测10.2us + uint32_t loops_per_10us = SystemCoreClock / 1700000; // 粗略估计,每10微秒的循环次数 + + for(uint32_t i = 0; i < count; i++) { + for(volatile uint32_t j = 0; j < loops_per_10us; j++); + } +} + +/** + * ************************************************************************ + * @brief delay_ms 毫秒延时函数 + * + * @param[in] count 毫秒值 + * + * ************************************************************************ + */ void delay_ms(uint32_t count) { - delay = count; - - while(0U != delay){ - } + delay_count = count; // 设置延时计数 + while (delay_count != 0U); } -/*! - \brief delay decrement - \param[in] none - \param[out] none - \retval none -*/ +/** + * ************************************************************************ + * @brief 每个 SysTick 中断调用时,减少延时计数 + * + * @param[in] void + * + * ************************************************************************ + */ void delay_decrement(void) { - if (0U != delay){ - delay--; + if (delay_count != 0U) + { + delay_count--; } } + +// /** +// * ************************************************************************ +// * @brief delay_ms_safe 毫秒延时函数(不干扰SysTick中断) +// * @details 使用简单循环实现延时,不会重新配置SysTick +// * @param[in] count 毫秒值 +// * ************************************************************************ +// */ +// void delay_ms_safe(uint32_t count) +// { +// // 基于系统时钟的简单循环延时 +// // 这是一个粗略的估计,实际延时可能有偏差 +// uint32_t loops_per_ms = SystemCoreClock / 14000; // 粗略估计 + +// for(uint32_t i = 0; i < count; i++) { +// for(volatile uint32_t j = 0; j < loops_per_ms; j++); +// } +// } + +// /** +// * ************************************************************************ +// * @brief delay_us_safe 微秒延时函数(不干扰SysTick中断) +// * @details 使用简单循环实现延时,不会重新配置SysTick +// * @param[in] count 微秒值 +// * ************************************************************************ +// */ +// void delay_us_safe(uint32_t count) +// { +// // 基于系统时钟的简单循环延时 +// // 这是一个粗略的估计,实际延时可能有偏差 +// uint32_t loops_per_us = SystemCoreClock / 22000000; // 粗略估计,每微秒的循环次数 + +// for(uint32_t i = 0; i < count; i++) { +// for(volatile uint32_t j = 0; j < loops_per_us; j++); +// } +// } \ No newline at end of file diff --git a/Src/tmp112.c b/Src/tmp112.c new file mode 100644 index 0000000..12828a4 --- /dev/null +++ b/Src/tmp112.c @@ -0,0 +1,323 @@ +// +// Created by dell on 24-12-20. +// TMP112A Temperature Sensor Driver Implementation +// + +#include "tmp112.h" + +/* Private function prototypes */ +static i2c_status_t tmp112a_write_register(uint8_t reg_addr, uint16_t value); +static i2c_status_t tmp112a_read_register(uint8_t reg_addr, uint16_t *value); +static float tmp112a_raw_to_celsius(uint16_t raw_data); +static uint16_t tmp112a_celsius_to_raw(float temperature); + +/*! + \brief 初始化TMP112A传感器 + \param[in] none + \param[out] none + \retval tmp112a_status_t +*/ +tmp112a_status_t tmp112a_init(void) { + i2c_status_t i2c_status; + + /* 配置传感器为默认设置 */ + i2c_status = tmp112a_config(TMP112A_CONFIG_DEFAULT); + if (i2c_status != I2C_STATUS_SUCCESS) { + return TMP112A_STATUS_ERROR; + } + + /* 等待配置生效 */ + delay_ms(1); + + return TMP112A_STATUS_SUCCESS; +} + +/*! + \brief 配置TMP112A传感器 + \param[in] config: 配置值 + \param[out] none + \retval tmp112a_status_t +*/ +tmp112a_status_t tmp112a_config(uint16_t config) { + i2c_status_t status = tmp112a_write_register(TMP112A_CONFIG_REG, config); + return (status == I2C_STATUS_SUCCESS) ? TMP112A_STATUS_SUCCESS : TMP112A_STATUS_ERROR; +} + +/*! + \brief 读取温度 + \param[in] none + \param[out] result: 结果结构体指针 + \retval tmp112a_status_t +*/ +tmp112a_status_t tmp112a_read_temperature(tmp112a_result_t *result) { + uint16_t raw_data; + i2c_status_t status; + + if (result == NULL) { + return TMP112A_STATUS_INVALID_PARAM; + } + + /* 读取温度寄存器 */ + status = tmp112a_read_register(TMP112A_TEMP_REG, &raw_data); + if (status != I2C_STATUS_SUCCESS) { + return TMP112A_STATUS_ERROR; + } + + /* 解析温度数据 */ + result->raw_data = raw_data; + result->temperature_c = tmp112a_raw_to_celsius(raw_data); + result->temperature_f = result->temperature_c * 9.0f / 5.0f + 32.0f; + + /* 检查温度范围 */ + if (result->temperature_c < TMP112A_TEMP_MIN || result->temperature_c > TMP112A_TEMP_MAX) { + return TMP112A_STATUS_OUT_OF_RANGE; + } + + /* 检查报警标志 */ + uint16_t config_reg; + status = tmp112a_read_register(TMP112A_CONFIG_REG, &config_reg); + if (status == I2C_STATUS_SUCCESS) { + result->alert_flag = (config_reg & TMP112A_CONFIG_AL) ? true : false; + } else { + result->alert_flag = false; + } + + return TMP112A_STATUS_SUCCESS; +} + +/*! + \brief 设置温度阈值 + \param[in] low_temp: 低温阈值 (°C) + \param[in] high_temp: 高温阈值 (°C) + \param[out] none + \retval tmp112a_status_t +*/ +tmp112a_status_t tmp112a_set_thresholds(float low_temp, float high_temp) { + uint16_t low_raw, high_raw; + i2c_status_t status; + + /* 参数验证 */ + if (low_temp < TMP112A_TEMP_MIN || low_temp > TMP112A_TEMP_MAX || + high_temp < TMP112A_TEMP_MIN || high_temp > TMP112A_TEMP_MAX || + low_temp >= high_temp) { + return TMP112A_STATUS_INVALID_PARAM; + } + + /* 转换温度为原始值 */ + low_raw = tmp112a_celsius_to_raw(low_temp); + high_raw = tmp112a_celsius_to_raw(high_temp); + + /* 写入低温阈值 */ + status = tmp112a_write_register(TMP112A_TLOW_REG, low_raw); + if (status != I2C_STATUS_SUCCESS) { + return TMP112A_STATUS_ERROR; + } + + /* 写入高温阈值 */ + status = tmp112a_write_register(TMP112A_THIGH_REG, high_raw); + if (status != I2C_STATUS_SUCCESS) { + return TMP112A_STATUS_ERROR; + } + + return TMP112A_STATUS_SUCCESS; +} + +/*! + \brief 进入关机模式 + \param[in] none + \param[out] none + \retval tmp112a_status_t +*/ +tmp112a_status_t tmp112a_shutdown(void) { + uint16_t config_reg; + i2c_status_t status; + + /* 读取当前配置 */ + status = tmp112a_read_register(TMP112A_CONFIG_REG, &config_reg); + if (status != I2C_STATUS_SUCCESS) { + return TMP112A_STATUS_ERROR; + } + + /* 设置关机位 */ + config_reg |= TMP112A_CONFIG_SD; + + /* 写回配置 */ + status = tmp112a_write_register(TMP112A_CONFIG_REG, config_reg); + return (status == I2C_STATUS_SUCCESS) ? TMP112A_STATUS_SUCCESS : TMP112A_STATUS_ERROR; +} + +/*! + \brief 退出关机模式 + \param[in] none + \param[out] none + \retval tmp112a_status_t +*/ +tmp112a_status_t tmp112a_wakeup(void) { + uint16_t config_reg; + i2c_status_t status; + + /* 读取当前配置 */ + status = tmp112a_read_register(TMP112A_CONFIG_REG, &config_reg); + if (status != I2C_STATUS_SUCCESS) { + return TMP112A_STATUS_ERROR; + } + + /* 清除关机位 */ + config_reg &= ~TMP112A_CONFIG_SD; + + /* 写回配置 */ + status = tmp112a_write_register(TMP112A_CONFIG_REG, config_reg); + if (status != I2C_STATUS_SUCCESS) { + return TMP112A_STATUS_ERROR; + } + + /* 等待传感器启动 */ + delay_ms(1); + + return TMP112A_STATUS_SUCCESS; +} + +/*! + \brief 单次转换 + \param[in] none + \param[out] result: 结果结构体指针 + \retval tmp112a_status_t +*/ +tmp112a_status_t tmp112a_one_shot(tmp112a_result_t *result) { + uint16_t config_reg; + i2c_status_t status; + uint8_t timeout = 100; // 100ms超时 + + if (result == NULL) { + return TMP112A_STATUS_INVALID_PARAM; + } + + /* 读取当前配置 */ + status = tmp112a_read_register(TMP112A_CONFIG_REG, &config_reg); + if (status != I2C_STATUS_SUCCESS) { + return TMP112A_STATUS_ERROR; + } + + /* 启动单次转换 */ + config_reg |= TMP112A_CONFIG_OS; + status = tmp112a_write_register(TMP112A_CONFIG_REG, config_reg); + if (status != I2C_STATUS_SUCCESS) { + return TMP112A_STATUS_ERROR; + } + + /* 等待转换完成 */ + do { + delay_ms(1); + status = tmp112a_read_register(TMP112A_CONFIG_REG, &config_reg); + if (status != I2C_STATUS_SUCCESS) { + return TMP112A_STATUS_ERROR; + } + timeout--; + } while ((config_reg & TMP112A_CONFIG_OS) && timeout > 0); + + if (timeout == 0) { + return TMP112A_STATUS_TIMEOUT; + } + + /* 读取转换结果 */ + return tmp112a_read_temperature(result); +} + +/*! + \brief 获取状态字符串 + \param[in] status: 状态码 + \param[out] none + \retval const char* 状态字符串 +*/ +const char* tmp112a_get_status_string(tmp112a_status_t status) { + switch (status) { + case TMP112A_STATUS_SUCCESS: + return "SUCCESS"; + case TMP112A_STATUS_ERROR: + return "ERROR"; + case TMP112A_STATUS_TIMEOUT: + return "TIMEOUT"; + case TMP112A_STATUS_INVALID_PARAM: + return "INVALID_PARAM"; + case TMP112A_STATUS_OUT_OF_RANGE: + return "OUT_OF_RANGE"; + default: + return "UNKNOWN"; + } +} + +/* Private Functions Implementation */ + +/*! + \brief 写入寄存器 + \param[in] reg_addr: 寄存器地址 + \param[in] value: 写入值 + \param[out] none + \retval i2c_status_t +*/ +static i2c_status_t tmp112a_write_register(uint8_t reg_addr, uint16_t value) { + uint8_t data[2]; + data[0] = (value >> 8) & 0xFF; + data[1] = value & 0xFF; + + return i2c_write_16bits(TMP112A_ADDR, reg_addr, data); +} + +/*! + \brief 读取寄存器 + \param[in] reg_addr: 寄存器地址 + \param[out] value: 读取值指针 + \retval i2c_status_t +*/ +static i2c_status_t tmp112a_read_register(uint8_t reg_addr, uint16_t *value) { + uint8_t data[2]; + i2c_status_t status; + + if (value == NULL) { + return I2C_STATUS_INVALID_PARAM; + } + + status = i2c_read_16bits(TMP112A_ADDR, reg_addr, data); + if (status == I2C_STATUS_SUCCESS) { + *value = ((uint16_t)data[0] << 8) | data[1]; + } + + return status; +} + +/*! + \brief 将原始数据转换为摄氏度 + \param[in] raw_data: 原始数据 + \param[out] none + \retval float 温度值(°C) +*/ +static float tmp112a_raw_to_celsius(uint16_t raw_data) { + int16_t temp_raw; + + /* TMP112A使用12位分辨率,数据在高12位 */ + temp_raw = (int16_t)(raw_data >> 4); + + /* 处理负数 */ + if (temp_raw & 0x800) { + temp_raw |= 0xF000; // 符号扩展 + } + + /* 转换为摄氏度 */ + return (float)temp_raw * TMP112A_TEMP_RESOLUTION; +} + +/*! + \brief 将摄氏度转换为原始数据 + \param[in] temperature: 温度值(°C) + \param[out] none + \retval uint16_t 原始数据 +*/ +static uint16_t tmp112a_celsius_to_raw(float temperature) { + int16_t temp_raw; + + /* 转换为原始值 */ + temp_raw = (int16_t)(temperature / TMP112A_TEMP_RESOLUTION); + + /* 移位到高12位 */ + return (uint16_t)(temp_raw << 4); +} diff --git a/Src/uart.c b/Src/uart.c index a2bba48..6d699c9 100644 --- a/Src/uart.c +++ b/Src/uart.c @@ -2,65 +2,71 @@ #include "gd32e23x_usart.h" #include "gd32e23x_rcu.h" #include "gd32e23x_gpio.h" +#include "board_config.h" -void uart0_init(uint32_t baudrate) { +void rs485_init(void) { + + #ifndef RS485_MAX13487 /* 使能 GPIOA 和 USART0 时钟 */ - rcu_periph_clock_enable(RCU_GPIOA); - rcu_periph_clock_enable(RCU_USART0); + rcu_periph_clock_enable(RS485_GPIO_RCU); + rcu_periph_clock_enable(RS485_RCU); - /* 配置 PA9 为 USART0_TX,PA10 为 USART0_RX */ - gpio_af_set(GPIOA, GPIO_AF_1, GPIO_PIN_9 | GPIO_PIN_10); - gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_9 | GPIO_PIN_10); - gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_9 | GPIO_PIN_10); + /* 配置 PA2 为 USART0_TX,PA3 为 USART0_RX */ + gpio_af_set(RS485_GPIO_PORT, GPIO_AF_1, RS485_TX_PIN | RS485_RX_PIN | RS485_EN_PIN); + + gpio_mode_set(RS485_GPIO_PORT, GPIO_MODE_AF, GPIO_PUPD_PULLUP, RS485_TX_PIN | RS485_RX_PIN); + gpio_output_options_set(RS485_GPIO_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, RS485_TX_PIN | RS485_RX_PIN); + + gpio_mode_set(RS485_GPIO_PORT, GPIO_MODE_AF, GPIO_PUPD_NONE, RS485_EN_PIN); + gpio_output_options_set(RS485_GPIO_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, RS485_EN_PIN); /* 配置波特率、数据位、停止位等 */ - usart_deinit(USART0); - usart_baudrate_set(USART0, baudrate); - usart_receive_config(USART0, USART_RECEIVE_ENABLE); - usart_transmit_config(USART0, USART_TRANSMIT_ENABLE); - usart_enable(USART0); -} + usart_deinit(RS485_PHY); + usart_word_length_set(RS485_PHY, USART_WL_8BIT); + usart_stop_bit_set(RS485_PHY, USART_STB_1BIT); + usart_parity_config(RS485_PHY, USART_PM_NONE); + usart_baudrate_set(RS485_PHY, RS485_BAUDRATE); + usart_receive_config(RS485_PHY, USART_RECEIVE_ENABLE); + usart_transmit_config(RS485_PHY, USART_TRANSMIT_ENABLE); -void uart1_init(uint32_t baudrate) { - rcu_periph_clock_enable(RCU_GPIOA); - rcu_periph_clock_enable(RCU_USART1); - // USART1 默认引脚为 PA2 (TX), PA3 (RX) - gpio_af_set(GPIOA, GPIO_AF_1, GPIO_PIN_2 | GPIO_PIN_3); - gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_2 | GPIO_PIN_3); - gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_2 | GPIO_PIN_3); - usart_deinit(USART1); - usart_baudrate_set(USART1, baudrate); - usart_receive_config(USART1, USART_RECEIVE_ENABLE); - usart_transmit_config(USART1, USART_TRANSMIT_ENABLE); - usart_enable(USART1); -} + usart_driver_assertime_config(RS485_PHY, 0x01); + usart_driver_deassertime_config(RS485_PHY, 0x10); -static uart_printf_port_t g_printf_port = UART_PRINTF_USART0; + usart_rs485_driver_enable(RS485_PHY); -void uart_set_printf_port(uart_printf_port_t port) { - g_printf_port = port; -} + usart_enable(RS485_PHY); -// printf 重定向,支持多串口 -int __io_putchar(int ch) { - switch (g_printf_port) { - case UART_PRINTF_USART0: - while (usart_flag_get(USART0, USART_FLAG_TBE) == RESET) {} - usart_data_transmit(USART0, (uint8_t)ch); - break; - case UART_PRINTF_USART1: - while (usart_flag_get(USART1, USART_FLAG_TBE) == RESET) {} - usart_data_transmit(USART1, (uint8_t)ch); - break; - case UART_PRINTF_BOTH: - while (usart_flag_get(USART0, USART_FLAG_TBE) == RESET) {} - usart_data_transmit(USART0, (uint8_t)ch); - while (usart_flag_get(USART1, USART_FLAG_TBE) == RESET) {} - usart_data_transmit(USART1, (uint8_t)ch); - break; - default: - break; + nvic_irq_enable(USART0_IRQn, 0); + usart_interrupt_enable(RS485_PHY, USART_INT_RBNE); + // usart_interrupt_enable(RS485_PHY, USART_INT_IDLE); + + #else + rcu_periph_clock_enable(RS485_GPIO_RCU); + rcu_periph_clock_enable(RS485_RCU); + + gpio_af_set(RS485_GPIO_PORT, GPIO_AF_1, GPIO_PIN_2 | GPIO_PIN_3); + + /* configure USART Tx&Rx as alternate function push-pull */ + gpio_mode_set(RS485_GPIO_PORT, GPIO_MODE_AF, GPIO_PUPD_PULLUP, RS485_TX_PIN | RS485_RX_PIN); + gpio_output_options_set(RS485_GPIO_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_10MHZ, RS485_TX_PIN | RS485_RX_PIN); + + /* configure RS485 EN Pin */ + gpio_mode_set(RS485_GPIO_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, RS485_EN_PIN); + gpio_output_options_set(RS485_GPIO_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, RS485_EN_PIN); + gpio_bit_write(RS485_GPIO_PORT, RS485_EN_PIN, SET); + + /* USART configure */ + usart_deinit(RS485_PHY); + usart_baudrate_set(RS485_PHY, RS485_BAUDRATE); + usart_receive_config(RS485_PHY, USART_RECEIVE_ENABLE); + usart_transmit_config(RS485_PHY, USART_TRANSMIT_ENABLE); + + usart_enable(RS485_PHY); + + nvic_irq_enable(USART0_IRQn, 0); + usart_interrupt_enable(RS485_PHY, USART_INT_RBNE); + usart_interrupt_enable(RS485_PHY, USART_INT_IDLE); + + #endif // RS485_MAX13487 } - return ch; -} diff --git a/Src/uart_ring_buffer.c b/Src/uart_ring_buffer.c new file mode 100644 index 0000000..73554f0 --- /dev/null +++ b/Src/uart_ring_buffer.c @@ -0,0 +1,104 @@ +/** + * @file uart_ring_buffer.c + * @brief 字节环形接收缓冲区的实现。 + * @details 适用于中断接收(写)与主循环解析(读)的典型串口场景; + * 采用“预留一格”区分空/满,最大可用容量为 UART_RX_BUFFER_SIZE-1。 + * @ingroup RingBuffer + */ +#include "uart_ring_buffer.h" + +static volatile uint8_t uart_rx_buffer[UART_RX_BUFFER_SIZE]; +static volatile uint8_t write_index = 0; +static volatile uint8_t read_index = 0; +static volatile uint32_t dropped_bytes = 0; + +/** + * @brief 重置环形缓冲区状态。 + * @details 将读指针、写指针与丢弃计数清零,不清空数据区内容。 + * @note 内部工具函数;对外请优先使用 uart_ring_buffer_init()/uart_ring_buffer_clear()。 + * @ingroup RingBuffer + */ +static void uart_ring_buffer_reset_state(void) { + write_index = 0; + read_index = 0; + dropped_bytes = 0; +} + +/** + * @brief 初始化环形缓冲区。 + * @details 调用内部重置逻辑,复位读写索引与丢弃计数,准备接收数据。 + * @note 若在中断环境使用,初始化前建议关闭相关接收中断以避免并发竞争。 + * @ingroup RingBuffer + */ +void uart_ring_buffer_init(void) { + uart_ring_buffer_reset_state(); +} + +/** + * @brief 获取当前可读的字节数。 + * @details 通过读/写指针的快照计算可读长度,范围为 [0, UART_RX_BUFFER_SIZE-1]。 + * @return uint8_t 可读字节数。 + * @note 预留一个空槽区分“空/满”,因此满时返回 UART_RX_BUFFER_SIZE-1。 + * @ingroup RingBuffer + */ +uint8_t uart_ring_buffer_available(void) { + /* 使用快照减少并发不一致窗口 */ + uint8_t w = write_index; + uint8_t r = read_index; + return (uint8_t)((w + UART_RX_BUFFER_SIZE - r) % UART_RX_BUFFER_SIZE); +} + +/** + * @brief 从环形缓冲区读取一个字节。 + * @details 若缓冲区非空,返回队头字节并推进读指针;若为空,返回 -1。 + * @return int 读取到的字节(0..255),或 -1 表示缓冲区为空。 + * @ingroup RingBuffer + */ +int uart_ring_buffer_get(void) { + if (read_index == write_index) return -1; // 空 + uint8_t data = uart_rx_buffer[read_index]; + read_index = (read_index + 1) % UART_RX_BUFFER_SIZE; + return data; +} + +/** + * @brief 向环形缓冲区写入一个字节。 + * @details 尝试写入一个新字节;若缓冲区已满则丢弃并计数。 + * @param data 待写入的字节。 + * @return bool 是否写入成功。 + * @retval true 写入成功。 + * @retval false 写入失败(缓冲区已满,数据被丢弃并计数)。 + * @note 如需“覆盖写入”策略,可在满时先推进读指针再写入。 + * @ingroup RingBuffer + */ +bool uart_ring_buffer_put(uint8_t data) { + uint8_t next = (write_index + 1) % UART_RX_BUFFER_SIZE; + if (next != read_index) { // 缓冲区未满 + uart_rx_buffer[write_index] = data; + write_index = next; + return true; + } else { + /* 缓冲区已满,丢弃新字节并计数 */ + dropped_bytes++; + return false; + } +} + +/** + * @brief 清空环形缓冲区。 + * @details 复位读写索引与丢弃计数,相当于逻辑上丢弃所有已接收数据,不擦除数据区内容。 + * @ingroup RingBuffer + */ +void uart_ring_buffer_clear(void) { + uart_ring_buffer_reset_state(); +} + +/** + * @brief 获取因满而被丢弃的字节累计数量。 + * @details 写入时缓冲区满会丢弃新字节并累加计数;该计数在 init/clear 时清零。 + * @return uint32_t 丢弃的累计字节数。 + * @ingroup RingBuffer + */ +uint32_t uart_ring_buffer_drop_count(void) { + return dropped_bytes; +} diff --git a/cmake/project_config.cmake b/cmake/project_config.cmake index fa305ee..c4c7858 100644 --- a/cmake/project_config.cmake +++ b/cmake/project_config.cmake @@ -7,8 +7,8 @@ set(VERSION "V${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}") string(TIMESTAMP BUILD_DATE "%Y-%m-%d") # 编译条件(如IIC类型等) -set(IIC_TYPE "AutoDetectDriveCurrent") -# set(IIC_TYPE "HW-IIC") +# set(IIC_TYPE "AutoDetectDriveCurrent") +set(IIC_TYPE "HW-IIC") # 其它自定义宏 add_definitions(-DIIC_TYPE=${IIC_TYPE}) diff --git a/i2c_wait.c b/i2c_wait.c new file mode 100644 index 0000000..83cc1cd --- /dev/null +++ b/i2c_wait.c @@ -0,0 +1,542 @@ + + +/* Private function prototypes */ +static i2c_status_t ldc1612_write_register(uint8_t reg_addr, uint16_t value); +static i2c_status_t ldc1612_read_register(uint8_t reg_addr, uint16_t *value); +static uint16_t ldc1612_calculate_clock_dividers(uint8_t channel); +static uint32_t ldc1612_parse_raw_result(uint32_t raw_result); + +/*! + \brief 初始化LDC1612传感器 + \param[in] none + \param[out] none + \retval ldc1612_status_t +*/ +ldc1612_status_t ldc1612_init(void) { + i2c_status_t i2c_status; + uint16_t device_id, manufacturer_id; + + /* 复位传感器 */ + i2c_status = ldc1612_reset(); + if (i2c_status != I2C_STATUS_SUCCESS) { + return LDC1612_STATUS_ERROR; + } + + /* 等待复位完成 */ + delay_ms(10); + + /* 验证设备ID */ + device_id = ldc1612_get_device_id(); + manufacturer_id = ldc1612_get_manufacturer_id(); + + if (device_id != 0x3055 || manufacturer_id != 0x5449) { + return LDC1612_STATUS_ERROR; + } + + return LDC1612_STATUS_SUCCESS; +} + +/*! + \brief 复位LDC1612传感器 + \param[in] none + \param[out] none + \retval ldc1612_status_t +*/ +ldc1612_status_t ldc1612_reset(void) { + i2c_status_t status = ldc1612_write_register(LDC1612_RESET_DEV, LDC1612_RESET_VALUE); + return (status == I2C_STATUS_SUCCESS) ? LDC1612_STATUS_SUCCESS : LDC1612_STATUS_ERROR; +} + +/*! + \brief 配置单通道模式 + \param[in] channel: 通道号 (0或1) + \param[out] none + \retval ldc1612_status_t +*/ +ldc1612_status_t ldc1612_config_single_channel(uint8_t channel) { + i2c_status_t status; + uint16_t clock_dividers; + + if (channel > 1) { + return LDC1612_STATUS_INVALID_PARAM; + } + + /* 进入休眠模式进行配置 */ + status = ldc1612_write_register(LDC1612_CONFIG, LDC1612_SENSOR_CONFIG_SLEEP); + if (status != I2C_STATUS_SUCCESS) return LDC1612_STATUS_ERROR; + + /* 计算并设置时钟分频 */ + clock_dividers = ldc1612_calculate_clock_dividers(channel); + status = ldc1612_write_register(LDC1612_CLOCK_DIVIDERS_CH0 + channel, clock_dividers); + if (status != I2C_STATUS_SUCCESS) return LDC1612_STATUS_ERROR; + + /* 设置稳定时间 */ + status = ldc1612_write_register(LDC1612_SETTLECOUNT_CH0 + channel, LDC1612_SETTLECOUNT_CH0_DEFAULT); + if (status != I2C_STATUS_SUCCESS) return LDC1612_STATUS_ERROR; + + /* 设置转换时间 */ + status = ldc1612_write_register(LDC1612_RCOUNT_CH0 + channel, LDC1612_CONVERSION_TIME_CH0); + if (status != I2C_STATUS_SUCCESS) return LDC1612_STATUS_ERROR; + + /* 设置错误配置 */ + status = ldc1612_write_register(LDC1612_ERROR_CONFIG, LDC1612_ERROR_CONFIG_DEFAULT); + if (status != I2C_STATUS_SUCCESS) return LDC1612_STATUS_ERROR; + + /* 设置驱动电流 */ + status = ldc1612_write_register(LDC1612_DRIVE_CURRENT_CH0 + channel, LDC1612_DRIVE_CURRENT_DEFAULT); + if (status != I2C_STATUS_SUCCESS) return LDC1612_STATUS_ERROR; + + /* 设置MUX配置 */ + status = ldc1612_write_register(LDC1612_MUX_CONFIG, LDC1612_MUX_CONFIG_DEFAULT); + if (status != I2C_STATUS_SUCCESS) return LDC1612_STATUS_ERROR; + + /* 退出休眠模式,开始转换 */ + status = ldc1612_write_register(LDC1612_CONFIG, LDC1612_SENSOR_CONFIG_ACTIVE); + if (status != I2C_STATUS_SUCCESS) return LDC1612_STATUS_ERROR; + + return LDC1612_STATUS_SUCCESS; +} + +/*! + \brief 读取制造商ID + \param[in] none + \param[out] none + \retval uint16_t 制造商ID +*/ +uint16_t ldc1612_get_manufacturer_id(void) { + uint16_t id = 0; + ldc1612_read_register(LDC1612_MANUFACTURER_ID, &id); + return id; +} + +/*! + \brief 读取设备ID + \param[in] none + \param[out] none + \retval uint16_t 设备ID +*/ +uint16_t ldc1612_get_device_id(void) { + uint16_t id = 0; + ldc1612_read_register(LDC1612_DEVICE_ID, &id); + return id; +} + +/*! + \brief 读取通道原始数据 + \param[in] channel: 通道号 + \param[out] result: 结果结构体指针 + \retval ldc1612_status_t +*/ +ldc1612_status_t ldc1612_read_channel(uint8_t channel, ldc1612_result_t *result) { + uint16_t msb, lsb; + uint32_t raw_data; + i2c_status_t status; + + if (channel > 1 || result == NULL) { + return LDC1612_STATUS_INVALID_PARAM; + } + + /* 读取MSB */ + status = ldc1612_read_register(LDC1612_DATA_CH0_MSB + (channel * 2), &msb); + if (status != I2C_STATUS_SUCCESS) return LDC1612_STATUS_ERROR; + + /* 读取LSB */ + status = ldc1612_read_register(LDC1612_DATA_CH0_LSB + (channel * 2), &lsb); + if (status != I2C_STATUS_SUCCESS) return LDC1612_STATUS_ERROR; + + /* 组合32位数据 */ + raw_data = ((uint32_t)msb << 16) | lsb; + + /* 解析结果 */ + result->raw_data = raw_data; + result->frequency = ldc1612_parse_raw_result(raw_data); + + /* 检查错误 */ + if (result->frequency >= 0x10000000) { + result->error_flag = true; + result->error_code = (result->frequency >> 24) & 0xFF; + return LDC1612_STATUS_ERROR; + } else { + result->error_flag = false; + result->error_code = 0; + } + + return LDC1612_STATUS_SUCCESS; +} + +/*! + \brief 设置驱动电流 + \param[in] channel: 通道号 + \param[in] current: 电流值 + \param[out] none + \retval ldc1612_status_t +*/ +ldc1612_status_t ldc1612_set_drive_current(uint8_t channel, uint16_t current) { + if (channel > 1) { + return LDC1612_STATUS_INVALID_PARAM; + } + + i2c_status_t status = ldc1612_write_register(LDC1612_DRIVE_CURRENT_CH0 + channel, current); + return (status == I2C_STATUS_SUCCESS) ? LDC1612_STATUS_SUCCESS : LDC1612_STATUS_ERROR; +} + +/*! + \brief 自动检测驱动电流 + \param[in] channel: 通道号 + \param[out] none + \retval ldc1612_status_t +*/ +ldc1612_status_t ldc1612_auto_detect_drive_current(uint8_t channel) { + uint16_t config_value, drive_current_reg; + uint16_t init_value, drive_current; + i2c_status_t status; + + if (channel > 1) { + return LDC1612_STATUS_INVALID_PARAM; + } + + /* 进入休眠模式 */ + status = ldc1612_write_register(LDC1612_CONFIG, LDC1612_SENSOR_CONFIG_SLEEP); + if (status != I2C_STATUS_SUCCESS) return LDC1612_STATUS_ERROR; + + /* 设置时钟分频 */ + uint16_t clock_dividers = ldc1612_calculate_clock_dividers(channel); + status = ldc1612_write_register(LDC1612_CLOCK_DIVIDERS_CH0 + channel, clock_dividers); + if (status != I2C_STATUS_SUCCESS) return LDC1612_STATUS_ERROR; + + /* 读取当前配置并禁用Rp覆盖 */ + status = ldc1612_read_register(LDC1612_CONFIG, &config_value); + if (status != I2C_STATUS_SUCCESS) return LDC1612_STATUS_ERROR; + + config_value &= ~(1 << 12); // 禁用RP_OVERRIDE_EN + status = ldc1612_write_register(LDC1612_CONFIG, config_value); + if (status != I2C_STATUS_SUCCESS) return LDC1612_STATUS_ERROR; + + /* 启动测量 */ + status = ldc1612_write_register(LDC1612_CONFIG, LDC1612_SENSOR_CONFIG_ACTIVE); + if (status != I2C_STATUS_SUCCESS) return LDC1612_STATUS_ERROR; + + /* 等待至少一次转换完成 */ + delay_ms(10); + + /* 读取初始驱动电流值 */ + status = ldc1612_read_register(LDC1612_DRIVE_CURRENT_CH0 + channel, &drive_current_reg); + if (status != I2C_STATUS_SUCCESS) return LDC1612_STATUS_ERROR; + + init_value = (drive_current_reg >> 6) & 0x1F; + drive_current = (init_value << 11) | 0x0000; + + /* 写入检测到的驱动电流 */ + status = ldc1612_write_register(LDC1612_DRIVE_CURRENT_CH0 + channel, drive_current); + if (status != I2C_STATUS_SUCCESS) return LDC1612_STATUS_ERROR; + + return LDC1612_STATUS_SUCCESS; +} + +/*! + \brief 获取状态字符串 + \param[in] status: 状态码 + \param[out] none + \retval const char* 状态字符串 +*/ +const char* ldc1612_get_status_string(ldc1612_status_t status) { + switch (status) { + case LDC1612_STATUS_SUCCESS: + return "SUCCESS"; + case LDC1612_STATUS_ERROR: + return "ERROR"; + case LDC1612_STATUS_TIMEOUT: + return "TIMEOUT"; + case LDC1612_STATUS_INVALID_PARAM: + return "INVALID_PARAM"; + case LDC1612_STATUS_NO_COIL: + return "NO_COIL"; + case LDC1612_STATUS_UNDER_RANGE: + return "UNDER_RANGE"; + case LDC1612_STATUS_OVER_RANGE: + return "OVER_RANGE"; + default: + return "UNKNOWN"; + } +} + +/* Private Functions Implementation */ + +/*! + \brief 写入寄存器 + \param[in] reg_addr: 寄存器地址 + \param[in] value: 写入值 + \param[out] none + \retval i2c_status_t +*/ +static i2c_status_t ldc1612_write_register(uint8_t reg_addr, uint16_t value) { + uint8_t data[2]; + data[0] = (value >> 8) & 0xFF; + data[1] = value & 0xFF; + + return i2c_write_16bits(LDC1612_ADDR, reg_addr, data); +} + +/*! + \brief 读取寄存器 + \param[in] reg_addr: 寄存器地址 + \param[out] value: 读取值指针 + \retval i2c_status_t +*/ +static i2c_status_t ldc1612_read_register(uint8_t reg_addr, uint16_t *value) { + uint8_t data[2]; + i2c_status_t status; + + if (value == NULL) { + return I2C_STATUS_INVALID_PARAM; + } + + status = i2c_read_16bits(LDC1612_ADDR, reg_addr, data); + if (status == I2C_STATUS_SUCCESS) { + *value = ((uint16_t)data[0] << 8) | data[1]; + } + + return status; +} + +/*! + \brief 计算时钟分频值 + \param[in] channel: 通道号 + \param[out] none + \retval uint16_t 分频值 +*/ +static uint16_t ldc1612_calculate_clock_dividers(uint8_t channel) { + uint16_t fin_div, fref_div; + float sensor_freq; + + /* 计算传感器频率 (MHz) */ + sensor_freq = 1.0f / (2.0f * 3.14159f * sqrtf(LDC1612_COIL_L_UH * LDC1612_COIL_C_PF * 1e-18f)) * 1e-6f; + + /* 计算FIN分频 */ + fin_div = (uint16_t)(sensor_freq / 8.75f + 1); + + /* 计算FREF分频 */ + if (fin_div * 4 < 40) { + fref_div = 2; + } else { + fref_div = 4; + } + + return (fin_div << 12) | fref_div; +} + +/*! + \brief 解析原始结果 + \param[in] raw_result: 原始数据 + \param[out] none + \retval uint32_t 解析后的数据 +*/ +static uint32_t ldc1612_parse_raw_result(uint32_t raw_result) { + uint32_t calibration_value; + uint8_t error_code; + + calibration_value = raw_result & 0x0FFFFFFF; + + /* 检查无线圈错误 */ + if (calibration_value == 0x0FFFFFFF) { + return LDC1612_ERROR_NO_COIL; + } + + error_code = (raw_result >> 24) & 0xFF; + + /* 检查各种错误 */ + if (error_code & 0x80) { + return LDC1612_ERROR_UNDER_RANGE; + } + if (error_code & 0x40) { + return LDC1612_ERROR_OVER_RANGE; + } + if (error_code & 0x20) { + return LDC1612_ERROR_WATCHDOG; + } + if (error_code & 0x10) { + return LDC1612_ERROR_AMPLITUDE; + } + + return calibration_value; +} + + +// ldc1612.h +// +// Created by dell on 24-12-3. +// LDC1612 Inductive Sensor Driver Header +// + +#ifndef LDC1612_H +#define LDC1612_H + +#include "gd32e23x_it.h" +#include "gd32e23x.h" +#include "systick.h" +#include +#include +#include +#include +#include +#include "board_config.h" +#include "i2c.h" + +/******************************************************************************/ +/* LDC1612 I2C Address */ +#define LDC1612_ADDR (0x2B) // 7-bit address + +/* Register Addresses */ +/******************************************************************************/ +#define LDC1612_DATA_CH0_MSB 0x00 +#define LDC1612_DATA_CH0_LSB 0x01 +#define LDC1612_DATA_CH1_MSB 0x02 +#define LDC1612_DATA_CH1_LSB 0x03 +#define LDC1612_RCOUNT_CH0 0x08 +#define LDC1612_RCOUNT_CH1 0x09 +#define LDC1612_OFFSET_CH0 0x0C +#define LDC1612_OFFSET_CH1 0x0D +#define LDC1612_SETTLECOUNT_CH0 0x10 +#define LDC1612_SETTLECOUNT_CH1 0x11 +#define LDC1612_CLOCK_DIVIDERS_CH0 0x14 +#define LDC1612_CLOCK_DIVIDERS_CH1 0x15 +#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 +#define LDC1612_DRIVE_CURRENT_CH1 0x1F +#define LDC1612_MANUFACTURER_ID 0x7E +#define LDC1612_DEVICE_ID 0x7F + +/* Channel Definitions */ +/******************************************************************************/ +#define LDC1612_CHANNEL_0 0 +#define LDC1612_CHANNEL_1 1 + +/* Configuration Values */ +/******************************************************************************/ +#define LDC1612_CONVERSION_TIME_CH0 0x0546 // 转换时间 +#define LDC1612_DRIVE_CURRENT_DEFAULT 0x9000 // 驱动电流 +#define LDC1612_MUX_CONFIG_DEFAULT 0x020C // 无自动扫描,滤波器带宽3.3MHz +#define LDC1612_SENSOR_CONFIG_ACTIVE 0x1601 // 激活配置 +#define LDC1612_SENSOR_CONFIG_SLEEP 0x2801 // 休眠配置 +#define LDC1612_ERROR_CONFIG_DEFAULT 0x0000 // 错误配置 +#define LDC1612_SETTLECOUNT_CH0_DEFAULT 0x001E // 稳定时间 +#define LDC1612_RESET_VALUE 0x8000 // 复位值 + +/* Coil Parameters */ +/******************************************************************************/ +#define LDC1612_COIL_RP_KOHM 7.2f // 并联电阻 (kΩ) +#define LDC1612_COIL_L_UH 33.0f // 电感值 (μH) +#define LDC1612_COIL_C_PF 150.0f // 电容值 (pF) +#define LDC1612_COIL_Q_FACTOR 35.97f // 品质因数 +#define LDC1612_COIL_FREQ_HZ 2262000 // 谐振频率 (Hz) + +/* Error Codes */ +/******************************************************************************/ +#define LDC1612_ERROR_NONE 0x00000000 +#define LDC1612_ERROR_NO_COIL 0xF0000000 +#define LDC1612_ERROR_UNDER_RANGE 0x80000000 +#define LDC1612_ERROR_OVER_RANGE 0x40000000 +#define LDC1612_ERROR_WATCHDOG 0x20000000 +#define LDC1612_ERROR_AMPLITUDE 0x10000000 + +/* Status Definitions */ +/******************************************************************************/ +typedef enum { + LDC1612_STATUS_SUCCESS = 0, + LDC1612_STATUS_ERROR, + LDC1612_STATUS_TIMEOUT, + LDC1612_STATUS_INVALID_PARAM, + LDC1612_STATUS_NO_COIL, + LDC1612_STATUS_UNDER_RANGE, + LDC1612_STATUS_OVER_RANGE +} ldc1612_status_t; + +typedef struct { + uint32_t raw_data; + uint32_t frequency; + float distance_mm; + bool error_flag; + uint8_t error_code; +} ldc1612_result_t; + +/******************************************************************************/ +/* Function Declarations */ + +/*! + \brief 初始化LDC1612传感器 + \param[in] none + \param[out] none + \retval ldc1612_status_t +*/ +ldc1612_status_t ldc1612_init(void); + +/*! + \brief 复位LDC1612传感器 + \param[in] none + \param[out] none + \retval ldc1612_status_t +*/ +ldc1612_status_t ldc1612_reset(void); + +/*! + \brief 配置单通道模式 + \param[in] channel: 通道号 (0或1) + \param[out] none + \retval ldc1612_status_t +*/ +ldc1612_status_t ldc1612_config_single_channel(uint8_t channel); + +/*! + \brief 读取制造商ID + \param[in] none + \param[out] none + \retval uint16_t 制造商ID +*/ +uint16_t ldc1612_get_manufacturer_id(void); + +/*! + \brief 读取设备ID + \param[in] none + \param[out] none + \retval uint16_t 设备ID +*/ +uint16_t ldc1612_get_device_id(void); + +/*! + \brief 读取通道原始数据 + \param[in] channel: 通道号 + \param[out] result: 结果结构体指针 + \retval ldc1612_status_t +*/ +ldc1612_status_t ldc1612_read_channel(uint8_t channel, ldc1612_result_t *result); + +/*! + \brief 设置驱动电流 + \param[in] channel: 通道号 + \param[in] current: 电流值 + \param[out] none + \retval ldc1612_status_t +*/ +ldc1612_status_t ldc1612_set_drive_current(uint8_t channel, uint16_t current); + +/*! + \brief 自动检测驱动电流 + \param[in] channel: 通道号 + \param[out] none + \retval ldc1612_status_t +*/ +ldc1612_status_t ldc1612_auto_detect_drive_current(uint8_t channel); + +/*! + \brief 获取状态字符串 + \param[in] status: 状态码 + \param[out] none + \retval const char* 状态字符串 +*/ +const char* ldc1612_get_status_string(ldc1612_status_t status); + +#endif //LDC1612_H