DO=MOSI DI=MISO

MCLK Master clock
SCLK Serial data bit clock
LRCK  Serial data left and right channel frame clock
TDMIN TDM data in
SDOUT/AD2 Serial data output/ I2 C address AD2
MOSI  主输出,从输入 ==>DO  数据输出
MISO 主输入,从输出==>DI数据输入
#include <stdio.h> #include <stdint.h> #include <string.h> #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "driver/spi_master.h" #include "esp_log.h" #include "driver/gpio.h" // ======================= // 引脚定义 // ======================= #define PIN_MISO 16 // ESP32 接 GMD1032 DO/SDO #define PIN_MOSI 19 // ESP32 接 GMD1032 DI/SDI #define PIN_CLK 18 #define PIN_CS 17 #define TAG "GMD1032" // 使用 SPI2_HOST #define MY_SPI_HOST SPI2_HOST // ======================= // GMD1032 命令表 // ======================= #define CMD_READ_CELL_VOLTAGE 0xD0 #define CMD_READ_STATUS_REG 0xC9 #define CMD_WAKEUP_CLEAR_STATUS 0x22 #define CMD_CLEAR_ISENSE_ACC 0x23 #define CMD_CLEAR_ADC_RESULT 0x24 #define CMD_RESTART_ADC 0x2E #define CMD_ENTER_MEASURE_MODE 0x2A #define CMD_SELF_TEST_CFG_FUSE 0x31 // 对应 CRC8(来自手册或计算) #define CRC8_0x22 0x2E #define CRC8_0x23 0x29 #define CRC8_0x24 0x3C #define CRC8_0x2A 0x16 #define CRC8_0x2E 0x0A #define CRC8_0x31 0x57 #define CRC8_0xC9 0xB1 #define CRC8_0xD0 0xFE // ======================= // CRC8 实现 (X^8 + X^2 + X + 1, Init: 0x41) // ======================= uint8_t crc8(const uint8_t *data, size_t len) { uint8_t crc = 0x41; for (size_t i = 0; i < len; i++) { crc ^= data[i]; for (uint8_t j = 0; j < 8; j++) { if (crc & 0x80) { crc = (crc << 1) ^ 0x07; } else { crc <<= 1; } } } return crc; } // ======================= // CRC10 实现 (X^10 + X^7 + X^3 + X^2 + X + 1, Init: 0x10) // 输入字节左移 2 位以对齐 10bit 框架(常见于 BMS 芯片) // ======================= uint16_t crc10(const uint8_t *data, size_t len) { uint16_t crc = 0x10; for (size_t i = 0; i < len; i++) { crc ^= (uint16_t)(data[i]) << 2; for (uint8_t j = 0; j < 8; j++) { if (crc & 0x400) { crc = (crc << 1) ^ 0x26D; } else { crc <<= 1; } crc &= 0x3FF; } } return crc; } // ======================= // 验证响应帧的 CRC10,并输出详细信息 // ======================= bool validate_response(uint8_t *data) { if (data == NULL) return false; uint8_t input[7]; memcpy(input, data, 6); input[6] = data[6] & 0x3F; // 屏蔽高2位 uint16_t calc_crc = crc10(input, 7); uint16_t recv_crc = ((data[6] & 0xC0) << 4) | data[7]; recv_crc &= 0x3FF; ESP_LOGI(TAG, "CRC10 Debug: Calc=0x%03X, Recv=0x%03X", calc_crc, recv_crc); if (calc_crc != recv_crc) { ESP_LOGW(TAG, "CRC10 mismatch! Expected: 0x%03X, Got: 0x%03X", calc_crc, recv_crc); return false; } ESP_LOGI(TAG, "CRC10 validation PASSED."); return true; } // ======================= // 强制发送命令并始终打印原始响应(无论成败) // ======================= esp_err_t send_command_and_read_response(uint8_t cmd_code, uint8_t crc8_val, uint8_t *out_rx_data, size_t rx_len) { uint8_t local_rx[8] = {0}; uint8_t *rx_data = (out_rx_data != NULL) ? out_rx_data : local_rx; uint8_t tx_data[8] = {cmd_code, crc8_val, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; spi_transaction_t t = { .length = 8 * 8, .tx_buffer = tx_data, .rx_buffer = rx_data, }; gpio_set_level(PIN_CS, 0); esp_rom_delay_us(15); esp_err_t ret = spi_device_polling_transmit(spi, &t); esp_rom_delay_us(15); gpio_set_level(PIN_CS, 1); // === 强制打印 TX 和 RX 原始数据 === ESP_LOG_BUFFER_HEX_LEVEL("TX_CMD", tx_data, 8, ESP_LOG_INFO); ESP_LOG_BUFFER_HEX_LEVEL("RX_RAW_FORCE", rx_data, 8, ESP_LOG_INFO); if (ret != ESP_OK) { ESP_LOGE(TAG, "SPI transaction failed: %s", esp_err_to_name(ret)); } return ret; } // ======================= // 通用带重试的命令发送函数(增强版日志) // ======================= bool send_gmd1032_command(uint8_t cmd, uint8_t expected_crc8, uint8_t *rx_buf, size_t rx_len, int retries) { uint8_t crc = crc8(&cmd, 1); if (expected_crc8 != 0xFF && crc != expected_crc8) { ESP_LOGW(TAG, "CRC8 mismatch! Expected: 0x%02X, Calculated: 0x%02X", expected_crc8, crc); return false; } for (int i = 0; i < retries; i++) { ESP_LOGI(TAG, "Attempt %d/%d for command 0x%02X", i + 1, retries, cmd); uint8_t temp_rx[8] = {0}; uint8_t *use_rx = (rx_buf != NULL) ? rx_buf : temp_rx; esp_err_t ret = send_command_and_read_response(cmd, crc, use_rx, rx_len); if (ret == ESP_OK) { if (rx_len == 0) { ESP_LOGI(TAG, "No response expected for command 0x%02X.", cmd); return true; } else if (validate_response(use_rx)) { return true; } else { ESP_LOGW(TAG, "CRC10 failed on attempt %d.", i + 1); } } else { ESP_LOGE(TAG, "SPI error on attempt %d: %s", i + 1, esp_err_to_name(ret)); } vTaskDelay(10 / portTICK_PERIOD_MS); } ESP_LOGE(TAG, "Command 0x%02X failed after %d retries.", cmd, retries); return false; } // ======================= // 【新增】模拟响应测试函数 // ======================= void run_simulation_test(void) { esp_log_level_set(TAG, ESP_LOG_VERBOSE); // 确保 HEX 能打印出来 ESP_LOGI(TAG, "=== Running CRC10 Simulation Test ==="); // 构造一个模拟的有效响应帧 uint8_t fake_response[8] = { 0x0E, 0x10, 0x0E, 0x70, 0x0E, 0x46, 0x00, 0x00 }; uint8_t crc_input[7]; memcpy(crc_input, fake_response, 6); crc_input[6] = fake_response[6] & 0x3F; uint16_t correct_crc = crc10(crc_input, 7); ESP_LOGI(TAG, "Simulated CRC10: 0x%03X", correct_crc); // 填入 CRC fake_response[6] |= ((correct_crc >> 4) & 0xC0); fake_response[7] = correct_crc & 0xFF; ESP_LOGI(TAG, "Fake Response Frame Generated:"); ESP_LOG_BUFFER_HEX("FAKE_FRAME", fake_response, 8); // 正确调用 bool result = validate_response(fake_response); ESP_LOGI(TAG, "Simulation Test Result: %s", result ? "PASSED" : "FAILED"); if (result) { ESP_LOGI(TAG, "CRC10 implementation is CORRECT. Problem likely in HARDWARE connection."); } else { ESP_LOGE(TAG, "CRC10 logic has BUGS! Please check algorithm or bit alignment."); } } // ======================= // 初始化 SPI 总线 // ======================= spi_device_handle_t spi; esp_err_t init_spi(void) { spi_bus_config_t buscfg = { .miso_io_num = PIN_MISO, .mosi_io_num = PIN_MOSI, .sclk_io_num = PIN_CLK, .quadwp_io_num = -1, .quadhd_io_num = -1, }; esp_err_t ret = spi_bus_initialize(MY_SPI_HOST, &buscfg, SPI_DMA_DISABLED); if (ret != ESP_OK) { ESP_LOGE(TAG, "Failed to initialize SPI bus: %s", esp_err_to_name(ret)); return ret; } spi_device_interface_config_t devcfg = { .mode = 1, .clock_speed_hz = 100 * 1000, .spics_io_num = -1, .queue_size = 1, }; // 使用全局 spi 变量 ret = spi_bus_add_device(MY_SPI_HOST, &devcfg, &spi); if (ret != ESP_OK) { ESP_LOGE(TAG, "Failed to add SPI device: %s", esp_err_to_name(ret)); return ret; } gpio_config_t io_conf = { .intr_type = GPIO_INTR_DISABLE, .mode = GPIO_MODE_OUTPUT, .pin_bit_mask = (1ULL << PIN_CS), }; gpio_config(&io_conf); gpio_set_level(PIN_CS, 1); ESP_LOGI(TAG, "SPI initialized successfully."); return ESP_OK; } // ======================= // 发送命令并读取响应(可使用全局 spi) // ======================= esp_err_t send_command_and_read_response(uint8_t cmd_code, uint8_t crc8_val, uint8_t *out_rx_data, size_t rx_len) { uint8_t local_rx[8] = {0}; uint8_t *rx_data = (out_rx_data != NULL) ? out_rx_data : local_rx; uint8_t tx_data[8] = {cmd_code, crc8_val, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; spi_transaction_t t = { .length = 8 * 8, .tx_buffer = tx_data, .rx_buffer = rx_data, }; gpio_set_level(PIN_CS, 0); esp_rom_delay_us(15); // 使用全局 spi 变量 esp_err_t ret = spi_device_polling_transmit(spi, &t); esp_rom_delay_us(15); gpio_set_level(PIN_CS, 1); ESP_LOG_BUFFER_HEXDUMP("TX_CMD", tx_data, 8, ESP_LOG_INFO); ESP_LOG_BUFFER_HEXDUMP("RX_RAW_FORCE", rx_data, 8, ESP_LOG_INFO); if (ret != ESP_OK) { ESP_LOGE(TAG, "SPI transaction failed: %s", esp_err_to_name(ret)); } return ret; } // ======================= // 主任务:读取电池电压等 // ======================= void gmd1032_task(void *arg) { uint8_t rx_data[8]; if (init_spi() != ESP_OK) { ESP_LOGE(TAG, "SPI init failed"); vTaskDelete(NULL); return; } ESP_LOGI(TAG, "Starting real GMD1032 communication..."); while (1) { ESP_LOGI(TAG, "=== New Cycle ==="); if (send_gmd1032_command(CMD_READ_CELL_VOLTAGE, CRC8_0xD0, rx_data, 8, 3)) { uint16_t cell1 = ((rx_data[0] & 0x0F) << 8) | rx_data[1]; uint16_t cell2 = ((rx_data[2] & 0x0F) << 8) | rx_data[3]; uint16_t cell3 = ((rx_data[4] & 0x0F) << 8) | rx_data[5]; ESP_LOGI(TAG, "Cell1: %.3f V", cell1 * 0.001f); ESP_LOGI(TAG, "Cell2: %.3f V", cell2 * 0.001f); ESP_LOGI(TAG, "Cell3: %.3f V", cell3 * 0.001f); } vTaskDelay(1000 / portTICK_PERIOD_MS); } } // ======================= // 入口函数 // ======================= void app_main(void) { // 🔍 第一步:运行模拟测试,验证 CRC 是否正确 run_simulation_test(); // 给用户一点时间看日志 vTaskDelay(2000 / portTICK_PERIOD_MS); // 启动主任务 xTaskCreate(gmd1032_task, "gmd1032_task", 4096, NULL, 10, NULL); }
09-23
#include <stdio.h> #include <stdint.h> #include <string.h> #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "driver/spi_master.h" #include "esp_log.h" #include "driver/gpio.h" // ======================= // 引脚定义 // ======================= #define PIN_MISO 11 // ESP32 接 GMD1032 DO/SDO #define PIN_MOSI 14 // ESP32 接 GMD1032 DI/SDI #define PIN_CLK 13 #define PIN_CS 12 #define TAG "GMD1032" // 使用 SPI2_HOST #define MY_SPI_HOST SPI2_HOST // ======================= // GMD1032 命令表 // ======================= #define CMD_READ_CELL_VOLTAGE 0xD0 #define CMD_READ_STATUS_REG 0xC9 #define CMD_WAKEUP_CLEAR_STATUS 0x22 #define CMD_CLEAR_ISENSE_ACC 0x23 #define CMD_CLEAR_ADC_RESULT 0x24 #define CMD_RESTART_ADC 0x2E #define CMD_ENTER_MEASURE_MODE 0x2A #define CMD_SELF_TEST_CFG_FUSE 0x31 // 对应 CRC8(来自手册或计算) #define CRC8_0x22 0x2E #define CRC8_0x23 0x29 #define CRC8_0x24 0x3C #define CRC8_0x2A 0x16 #define CRC8_0x2E 0x0A #define CRC8_0x31 0x57 #define CRC8_0xC9 0xB1 #define CRC8_0xD0 0xFE // ======================= // 全局 SPI 句柄 // ======================= spi_device_handle_t spi; // ======================= // CRC8 实现 (X^8 + X^2 + X + 1, Init: 0x41) // ======================= uint8_t crc8(const uint8_t *data, size_t len) { uint8_t crc = 0x41; for (size_t i = 0; i < len; i++) { crc ^= data[i]; for (uint8_t j = 0; j < 8; j++) { if (crc & 0x80) { crc = (crc << 1) ^ 0x07; } else { crc <<= 1; } } } return crc; } // ======================= // CRC10 实现 (X^10 + X^7 + X^3 + X^2 + X + 1, Init: 0x10) // 输入字节左移 2 位以对齐 10bit 框架(常见于 BMS 芯片) // ======================= uint16_t crc10(const uint8_t *data, size_t len) { uint16_t crc = 0x10; for (size_t i = 0; i < len; i++) { crc ^= (uint16_t)(data[i]) << 2; for (uint8_t j = 0; j < 8; j++) { if (crc & 0x400) { crc = (crc << 1) ^ 0x26D; } else { crc <<= 1; } crc &= 0x3FF; } } return crc; } // ======================= // 验证响应帧的 CRC10,并输出详细信息 // ======================= bool validate_response(uint8_t *data) { if (data == NULL) return false; uint8_t input[7]; memcpy(input, data, 6); input[6] = data[6] & 0x3F; // 屏蔽高2位 uint16_t calc_crc = crc10(input, 7); uint16_t recv_crc = ((data[6] & 0xC0) << 4) | data[7]; recv_crc &= 0x3FF; ESP_LOGI(TAG, "CRC10 Debug: Calc=0x%03X, Recv=0x%03X", calc_crc, recv_crc); if (calc_crc != recv_crc) { ESP_LOGW(TAG, "CRC10 mismatch! Expected: 0x%03X, Got: 0x%03X", calc_crc, recv_crc); return false; } ESP_LOGI(TAG, "CRC10 validation PASSED."); return true; } // ======================= // 【统一】发送命令并读取响应(使用全局 spi 和 HEXDUMP) // ======================= esp_err_t send_command_and_read_response(uint8_t cmd_code, uint8_t crc8_val, uint8_t *out_rx_data, size_t rx_len) { uint8_t local_rx[8] = {0}; uint8_t *rx_data = (out_rx_data != NULL) ? out_rx_data : local_rx; uint8_t tx_data[8] = {cmd_code, crc8_val, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; spi_transaction_t t = { .length = 8 * 8, .tx_buffer = tx_data, .rx_buffer = rx_data, }; esp_rom_delay_us(15); esp_err_t ret = spi_device_polling_transmit(spi, &t); // 使用全局 spi esp_rom_delay_us(15); // 打印发送与接收数据(使用支持级别控制的 HEXDUMP) ESP_LOG_BUFFER_HEXDUMP("TX_CMD", tx_data, 8, ESP_LOG_INFO); ESP_LOG_BUFFER_HEXDUMP("RX_RAW_FORCE", rx_data, 8, ESP_LOG_INFO); if (ret != ESP_OK) { ESP_LOGE(TAG, "SPI transaction failed: %s", esp_err_to_name(ret)); } return ret; } // ======================= // 通用带重试的命令发送函数(增强版日志) // ======================= bool send_gmd1032_command(uint8_t cmd, uint8_t expected_crc8, uint8_t *rx_buf, size_t rx_len, int retries) { uint8_t crc = crc8(&cmd, 1); if (expected_crc8 != 0xFF && crc != expected_crc8) { ESP_LOGW(TAG, "CRC8 mismatch! Expected: 0x%02X, Calculated: 0x%02X", expected_crc8, crc); return false; } for (int i = 0; i < retries; i++) { ESP_LOGI(TAG, "Attempt %d/%d for command 0x%02X", i + 1, retries, cmd); uint8_t temp_rx[8] = {0}; uint8_t *use_rx = (rx_buf != NULL) ? rx_buf : temp_rx; esp_err_t ret = send_command_and_read_response(cmd, crc, use_rx, rx_len); if (ret == ESP_OK) { if (rx_len == 0) { ESP_LOGI(TAG, "No response expected for command 0x%02X.", cmd); return true; } else if (validate_response(use_rx)) { return true; } else { ESP_LOGW(TAG, "CRC10 failed on attempt %d.", i + 1); } } else { ESP_LOGE(TAG, "SPI error on attempt %d: %s", i + 1, esp_err_to_name(ret)); } vTaskDelay(10 / portTICK_PERIOD_MS); } ESP_LOGE(TAG, "Command 0x%02X failed after %d retries.", cmd, retries); return false; } // ======================= // 【新增】模拟响应测试函数 // ======================= void run_simulation_test(void) { esp_log_level_set(TAG, ESP_LOG_VERBOSE); // 确保 HEX 输出可见 ESP_LOGI(TAG, "=== Running CRC10 Simulation Test ==="); // 构造一个模拟的有效响应帧 uint8_t fake_response[8] = { 0x0E, 0x10, // Cell1: 3.600V 0x0E, 0x70, // Cell2: 3.700V 0x0E, 0x46, // Cell3: 3.650V 0x00, 0x00 // CRC 占位符 }; uint8_t crc_input[7]; memcpy(crc_input, fake_response, 6); crc_input[6] = fake_response[6] & 0x3F; uint16_t correct_crc = crc10(crc_input, 7); ESP_LOGI(TAG, "Simulated CRC10: 0x%03X", correct_crc); // 填入 CRC 到第6、7字节 fake_response[6] |= ((correct_crc >> 4) & 0xC0); // 高2位放入 byte[6] bit7~6 fake_response[7] = correct_crc & 0xFF; ESP_LOGI(TAG, "Fake Response Frame Generated:"); ESP_LOG_BUFFER_HEXDUMP("FAKE_FRAME", fake_response, 8, ESP_LOG_INFO); bool result = validate_response(fake_response); ESP_LOGI(TAG, "Simulation Test Result: %s", result ? "✅ PASSED" : "❌ FAILED"); if (result) { ESP_LOGI(TAG, "CRC10 implementation is CORRECT. Problem likely in HARDWARE connection."); } else { ESP_LOGE(TAG, "CRC10 logic has BUGS! Please check algorithm or bit alignment."); } } // ======================= // 初始化 SPI 总线 // ======================= esp_err_t init_spi(void) { spi_bus_config_t buscfg = { .miso_io_num = PIN_MISO, .mosi_io_num = PIN_MOSI, .sclk_io_num = PIN_CLK, .quadwp_io_num = -1, .quadhd_io_num = -1, }; esp_err_t ret = spi_bus_initialize(MY_SPI_HOST, &buscfg, SPI_DMA_DISABLED); if (ret != ESP_OK) { ESP_LOGE(TAG, "Failed to initialize SPI bus: %s", esp_err_to_name(ret)); return ret; } spi_device_interface_config_t devcfg = { .mode = 3, // CPOL=1, CPHA=1 (SPI Mode 3) .clock_speed_hz = 500 * 1000, // 500 kHz(符合手册要求) .spics_io_num = PIN_CS, // ← 改为使用硬件 CS .queue_size = 1, }; ret = spi_bus_add_device(MY_SPI_HOST, &devcfg, &spi); if (ret != ESP_OK) { ESP_LOGE(TAG, "Failed to add SPI device: %s", esp_err_to_name(ret)); return ret; } ESP_LOGI(TAG, "SPI initialized successfully."); return ESP_OK; } // ======================= // 主任务:读取电池电压等 // ======================= void gmd1032_task(void *arg) { uint8_t rx_data[8]; if (init_spi() != ESP_OK) { ESP_LOGE(TAG, "SPI init failed"); vTaskDelete(NULL); return; } ESP_LOGI(TAG, "Starting real GMD1032 communication..."); while (1) { ESP_LOGI(TAG, "=== New Cycle ==="); if (send_gmd1032_command(CMD_READ_CELL_VOLTAGE, CRC8_0xD0, rx_data, 8, 3)) { uint16_t cell1 = ((rx_data[0] & 0x0F) << 8) | rx_data[1]; uint16_t cell2 = ((rx_data[2] & 0x0F) << 8) | rx_data[3]; uint16_t cell3 = ((rx_data[4] & 0x0F) << 8) | rx_data[5]; ESP_LOGI(TAG, "Cell1: %.3f V", cell1 * 0.001f); ESP_LOGI(TAG, "Cell2: %.3f V", cell2 * 0.001f); ESP_LOGI(TAG, "Cell3: %.3f V", cell3 * 0.001f); } vTaskDelay(1000 / portTICK_PERIOD_MS); } } // ======================= // 入口函数 // ======================= void app_main(void) { // 第一步:运行模拟测试,验证 CRC 是否正确 run_simulation_test(); // 给用户一点时间看日志 vTaskDelay(2000 / portTICK_PERIOD_MS); // 启动主任务 xTaskCreate(gmd1032_task, "gmd1032_task", 4096, NULL, 10, NULL); } I (10576) GMD1032: === New Cycle === I (10576) GMD1032: Attempt 1/3 for command 0xD0 I (10576) TX_CMD: 0x3fc9ddf8 d0 fe ff ff ff ff ff ff |........| I (10576) RX_RAW_FORCE: 0x3fc9deb0 ff ff 00 00 00 00 00 00 |........| I (10586) GMD1032: CRC10 Debug: Calc=0x000, Recv=0x000 I (10586) GMD1032: CRC10 validation PASSED. I (10596) GMD1032: Cell1: 4.095 V I (10596) GMD1032: Cell2: 0.000 V I (10596) GMD1032: Cell3: 0.000 V
最新发布
09-24
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值