“int”到“char”截断的编译原理(关于char c = ‘\0xab’的研究)

本文详细解析了C语言中字符常量的存储机制,包括8进制、纯字符和整数形式的表示。讨论了不同情况下可能出现的截断警告和错误,如超过ASCII码范围的转换。并指出,这种截断在某些情况下可以提高程序容错率,类似于浮点数赋值时的四舍五入处理。
#include <stdio.h>

void main() {
	char c1 = '\111111';
	char c2 = '\111aaa';
	char c3 = '\aaaa';
	char c4 = '\aa11';
	char c5 = '1111';
	char c6 = 'aaaa';

	printf(" c1=%c\n c2=%c\n c3=%c\n c4=%c\n c5=%c\n c6=%c\n",c1,c2,c3,c4,c5,c6);
}

c1~c6 都为可接受的最长位数。
输出
在这里插入图片描述
调试警告,无报错
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
对于

char c7 = 128;

在这里插入图片描述
对于

char c8 = 256;

在这里插入图片描述


情形1:char c1 = '\111111';

警告:int到char截断;常量值截断。

情形2:char c1 = '\411111';

报错:超出char长度。


总结:

字符常量值

vc编译系统里自先存储的字符常量值对应的整数值为0~255,即ASCLL码范围。只用了8位2进制数的内存来存储。 -128~-1 为负整数到字符转换时,按照8位负数补码对应的0~255的8位正数补码的转换方式进行转换。因此,转义符号中的“int”转换为“char”时,超过 -128~255数值的需要进行“int”到“char”截断。

0~127 是标准ASCLL码,128~255是扩展的。普通“int”转换为“char”时,超过 -128~127数值的需要进行截断常量值。

匹配机制:

(1)对于 char c = ‘\123456’; 8进制转义字符形式:
按8进制匹配,依次向后正确匹配最多3位8进制整数 ‘\111’ 小于255则可行,否则报错“超出长度”。(若前三位为 “\118” ,则只正确匹配到 ‘\11’ )。对于,正确匹配之后还存在有数,只判断其位数不大于3位则可行——截断警告、将最后一个字符作为值,否则报错“超出长度”。

(2)对于 char c = ‘abcd’; 纯字符形式:
相似的原理,字符型的正确匹配只能匹配一位且任意字符都是匹配的,所以直接进行位数判断,正确匹配的第一位加上之后不大于3位的字符,共4位。超过则报错“超出长度”。

(3)对于 char c = 123456; 纯整数形式:
按照8位2进制补码对应于0~255的8位2进制补码 进行匹配。

注意: ‘\xff’ 16进制形式的字符转换按严格标准,不存在截断问题。

留给读者

C语言中,
char c = ‘aaaa’;
char c = ‘\aaaa’;
char c = ‘\111111’;
只是截断警告,为什么允许存在,有什么作用和应用呢?

参考其他的截断问题:

float a = 1.1234567; // a = 1.123457

float有效位为小数点后6位,此时超出的第7位需进行截断,并进行了四舍五入处理。这是修正了float变量的近似赋值,在对精度要求不高的常见情况下,既提高了程序容错率,又得到了相对正确的近似结果,允许存在。

所以,转换char的截断是不是也仅仅是为了提高程序容错率而存在的呢?

#include "spi_server.h" // spi #include <SPI.h> #include <ESP32SPISlave.h> #include "helper.h" ESP32SPISlave slave; // 创建ESP32SPISlave对象 #define SPI_SPLAVE_ENABLE 1 // Adafruit_SPIDevice spi_dev = Adafruit_SPIDevice(SPIDEVICE_CS, 1000000, SPI_BITORDER_MSBFIRST, SPI_MODE0); #define MY_CS 12 #define MY_SCK 13 #define MY_MOSI 14 #define MY_MISO 11 // 自定义spi引脚 // SPIClass my_spi(HSPI); // 创建SPIClass对象my_spi #define SPI_READ_TIMEROUT 999999 // 10 // 50ms static constexpr size_t SPI_BUFFER_SIZE = 16 * 2; // 上位机 测试42 字节数据正常,模式1,定义SPI通信缓冲区大小为32字节 static constexpr size_t QUEUE_SIZE = 1; static uint8_t spi_tx_buf[SPI_BUFFER_SIZE]{1, 2, 3, 4, 5, 6, 7, 8};//发送缓冲区,预置测试数据1-8 static uint8_t spi_rx_buf[SPI_BUFFER_SIZE]{0, 0, 0, 0, 0, 0, 0, 0};//接收缓冲区,初始化为全0 static uint8_t spi_tx_cmd_buf[SPI_BUFFER_SIZE]{0, 0, 0, 0, 0, 0, 0, 0};//命令发送缓冲区,初始化为全0 #define SPI_TX_CACHE_BUF_LEN 1024//发送缓存总大小为1024字节 #define SPI_TX_PAGE_BUF_LEN (SPI_BUFFER_SIZE - 3) // spi 回复数据的最大长度,单次SPI通信有效数据长度 #define SPI_USER_CMD_NULL 0//空指令 #define SPI_USER_CMD_READ 1//读数据 #define SPI_USER_CMD_WRITE 2//写数据 static volatile uint8_t spi_tx_cache_buf[SPI_TX_CACHE_BUF_LEN]{0, 0, 0, 0, 0, 0, 0, 0}; static volatile int spi_tx_cache_buf_cnt = 0; // spi 发送缓存区数据长度 // static int spi_tx_page_cnt=0;//spi 发送缓存区数据长度 static volatile int spi_current_cmd = 0, spi_current_cmd_len = 0; static uint8_t spi_send_busy = 0;//发送忙标志位 static uint8_t spi_send_mode = 1; // 发送模式选择发串口 int spi_slave_data_cache_add(uint8_t *data, uint16_t len) { if (len > SPI_TX_CACHE_BUF_LEN) { /* code */ len = SPI_TX_CACHE_BUF_LEN; } for (int i = 0; i < len; i++) { /* code */ spi_tx_cache_buf[i] = data[i]; } spi_tx_cache_buf_cnt = len; return 0; } void IRAM_ATTR my_post_setup_cb(spi_slave_transaction_t *trans)//强制将函数编译到内部RAM,确保中断快速响应 { static uint8_t test_send = 0x11;//静态测试变量 int over_len = spi_tx_cache_buf_cnt - spi_current_cmd_len * SPI_TX_PAGE_BUF_LEN; // 剩余待发送数据量 = 总缓存量 - 已发送页数×单页容量 // Serial.printf("spi slave read over_len = %d cache= %d\r\n", over_len,spi_tx_cache_buf_cnt); if (over_len < 0)//数据已全部发送完成时的保护判断 { return; } if (over_len > SPI_TX_PAGE_BUF_LEN)//单包数据超限时截断处理 { over_len = SPI_TX_PAGE_BUF_LEN; } memset((void *)spi_tx_buf, 0, SPI_BUFFER_SIZE);//清空发送缓冲区(32字节全置0) spi_tx_buf[0] = 0xab; spi_tx_buf[1] = over_len; // 实际长度 spi_tx_buf[2] = spi_current_cmd_len; // 包号 for (int i = 0; i < over_len; i++)//将缓存数据拷贝到发送缓冲区(偏移3字节协议头) { /* code */ spi_tx_buf[3 + i] = spi_tx_cache_buf[spi_current_cmd_len * SPI_TX_PAGE_BUF_LEN + i]; } spi_current_cmd_len++;//包序号自增 if (spi_current_cmd_len >= 3)//包序号归零 { /* code */ spi_current_cmd_len = 0; } // Serial.printf("spi slave read cmd_len = %d ,%02x %02x %02x\n", over_len,spi_tx_buf[1], spi_tx_buf[2], spi_tx_buf[3]); } /*** * spi slave 任务 * 模式设置4 字节 * spi cmd: 0xaa 0x01 ,切换模式1,发串口1数据 * * 数据读取 64字节,发aa 01 0xff ...... * spi ack: 0xaa 0x01 ,剩余字节全部回复串口数据 */ static void spi_slave_task(void *pvParameters)//FreeRTOS任务函数标准定义 { size_t received_bytes; // 记录接收到的数据字节数 while (1)//无限循环,保持任务持续运行 { /* code */ // initializeBuffers(tx_buf, rx_buf, BUFFER_SIZE); int cs = digitalRead(MY_CS);//读取片选信号(CS)引脚状态,检测主设备是否选中本从设备 #if SPI_SPLAVE_ENABLE//条件编译开关,控制SPI从设备功能是否启用 if (cs == 0)//当片选信号为低电平时(表示被主设备选中)执行通信 { memset(spi_rx_buf, 0, SPI_BUFFER_SIZE);//清空接受缓存区 // start and wait to complete one BIG transaction (same data will be received from slave) received_bytes = slave.transfer(spi_tx_buf, spi_rx_buf, SPI_BUFFER_SIZE, SPI_READ_TIMEROUT); //tx_buf:发送缓冲区,rx_buf:接收缓冲区,BUFFER_SIZE:传输长度,READ_TIMEROUT:超时时间 // verify and dump difference with received data if (received_bytes > 0)//成功接收到数据时处理数据 { printf_log_hex("slave", spi_rx_buf, received_bytes); printf_log_hex("slave", spi_tx_buf, received_bytes); if (spi_rx_buf[1] == 0xaa && spi_rx_buf[2] == 0x01) // 当收到0xAA01时设置为读取模式,读取数据cmd { spi_current_cmd = SPI_USER_CMD_READ; // spi_current_cmd_len = spi_rx_buf[3]; } my_post_setup_cb(NULL);//调用之前分析的发送回调函数 } } #endif vTaskDelay(5 / portTICK_PERIOD_MS);//任务延时5ms } } /** * spi slave 初始化 * SPI_MODE1 * sck 13 * miso 14 * mosi 11 * ss 12 * */ void spi_slave_server_init(void) { #if SPI_SPLAVE_ENABLE slave.setDataMode(SPI_MODE3); // default: SPI_MODE1 slave.setQueueSize(QUEUE_SIZE); // default: 1 slave.begin(HSPI, 13, 11, 14, 12); // default: HSPI (please refer README for pin assignments) // pinMode(10, INPUT); // slave.setPostSetupCb(my_post_setup_cb); xTaskCreate(spi_slave_task, "spi_slave_task", 8 * 1024, NULL, 2, NULL); #endif } 以上是spi通信代码 #include "main.h" #include <WiFi.h> #include "esp_uart_driver.h" #include "uart_msg_analy.h" #include "network_app.h" #include "spi_server.h" uint32_t loop_test_timer_cnt=0; #define UART1_TX_PIN 4 #define UART1_RX_PIN 5 void setup() { int httpCode; uart0_init(115200, (void *)0);// 初始化主串口 // 需要放在Setup里初始化 uart1_init(115200, UART1_RX_PIN,UART1_TX_PIN,(void *)uart1_msg_analy);// 配置第二个串口 // 初始化随机数生成器 esp_random(); // 初始化随机数生成器 wifi_app_init();// 启动WiFi功能 spi_slave_server_init();//初始化SPI从机模式 Serial.printf("wifi spi project done... %s %s\r\n", __DATE__, __TIME__);//打印初始化完成信息 } int test_cnt = 0; char receivedChar = 0; void loop() { uart_process(); if(doDelayMillisTime(2000, &loop_test_timer_cnt, false))//2秒间隔的定时输出,通过loop_test_timer_cnt变量维护定时状态 { Serial.printf("loop... %d\r\n", test_cnt++);//循环输出计数 } vTaskDelay(100 / portTICK_PERIOD_MS); } boolean doDelayMillisTime(uint32_t interval, uint32_t *previousMillis, boolean state)//非阻塞式定时器;state:状态翻转标志 { uint32_t currentMillis = (uint32_t)SYSTEM_TIMER_MS();//获取系统时间 if (currentMillis - *previousMillis >= interval)// interval 定时间隔(ms);previousMillis 存储上次触发时间的指针 { *previousMillis = currentMillis; state = !state; } return state; } void printf_log_hex(char *hex_name, uint8_t *data, uint32_t len)//十六进制数据打印工具 { Serial.printf("%s ,len is:%d \r\n", hex_name, len); for (int i = 0; i < len; i++) { Serial.printf("%02x ", data[i]);//单字节总显示2位 } Serial.printf("\r\n");//保证终端正确换行 } 以上是主函数,修改两者至可从监视窗口查看spi驱动情况
09-25
#include <SPI.h> #include <BLEDevice.h> #include <BLE2902.h> #include <Arduino.h> // ==================== 引脚定义 ==================== #define SCK_PIN 13 #define MISO_PIN 11 #define MOSI_PIN 14 #define CS_PIN 12 // ==================== 启用调试模式 ==================== #define DEBUG_GMD1032 // ==================== GMD1032 命令列表 ==================== const uint16_t CMD_START_MEASUREMENT = 0x2A16; const uint16_t CMD_CLEAR_ADC_RESULTS = 0x243C; const uint16_t CMD_READ_CFG0 = 0xC08E; const uint16_t CMD_READ_STS0 = 0xC9B1; // CELL 分组命令 const uint16_t CMD_READ_CELL_1_3 = 0xD0FE; const uint16_t CMD_READ_CELL_4_6 = 0xD1F9; const uint16_t CMD_READ_CELL_7_9 = 0xD2F0; const uint16_t CMD_READ_CELL_10_12 = 0xD3F7; const uint16_t CMD_READ_CELL_13_15 = 0xD4E2; const uint16_t CMD_READ_CELL_16_18 = 0xD5E5; // DIS 分组命令 const uint16_t CMD_READ_DIS_1_3 = 0xD6EC; const uint16_t CMD_READ_DIS_4_6 = 0xD7EB; const uint16_t CMD_READ_DIS_7_9 = 0xD8C6; const uint16_t CMD_READ_DIS_10_12 = 0xD9C1; const uint16_t CMD_READ_DIS_13_15 = 0xDAC8; const uint16_t CMD_READ_DIS_16_18 = 0xDBCF; // 其他测量命令 const uint16_t CMD_READ_VREF2_V3 = 0xDFD3; const uint16_t CMD_READ_V5_DIETEMP_VSTK = 0xE06E; // ==================== SPI 设置 ==================== SPISettings spiSettings(1000000, MSBFIRST, SPI_MODE3); // 1MHz, Mode3 (CPOL=1, CPHA=1) // ==================== BLE 配置 ==================== #define SERVICE_UUID “4fafc201-1fb5-459e-8fcc-c5c9c331914b” #define CHARACTERISTIC_UUID “beb5483e-36e1-4688-b7f5-ea07361b26a8” BLECharacteristic* pCharacteristic = nullptr; bool deviceConnected = false; float sharedCellVoltages[16] = {0}; float sharedDisVoltages[16] = {0}; float sharedVref2 = 0.0f, sharedV3 = 0.0f, sharedV5 = 0.0f, sharedDieTemp = 0.0f, sharedVstk = 0.0f; portMUX_TYPE sensorMutex = portMUX_INITIALIZER_UNLOCKED; // 保护共享数据 // 回调类声明 class MyServerCallbacks : public BLEServerCallbacks { void onConnect(BLEServer* pServer) override { deviceConnected = true; Serial.println(“设备接入~”); } void onDisconnect(BLEServer* pServer) override { deviceConnected = false; Serial.println("设备断开~"); pServer->startAdvertising(); // 重新开始广播 } }; class MyCharacteristicCallbacks : public BLECharacteristicCallbacks { void onWrite(BLECharacteristic* pCharacteristic) override { std::string value = pCharacteristic->getValue(); if (!value.empty()) { Serial.print("特征值已更新,新值为: "); Serial.println(value.c_str()); } } }; // ==================== 辅助函数:打印二进制 ==================== void printBinary(uint8_t b) { for (int i = 7; i >= 0; i–) { Serial.write((b & (1 << i)) ? ‘1’ : ‘0); } } // ==================== 唤醒芯片 ==================== void wakeUpGMD1032() { SPI.beginTransaction(spiSettings); digitalWrite(CS_PIN, LOW); delayMicroseconds(10); SPI.transfer(0x00); digitalWrite(CS_PIN, HIGH); SPI.endTransaction(); delay(5); } // ==================== 发送命令 ==================== bool sendCommand(uint16_t command) { SPI.beginTransaction(spiSettings); digitalWrite(CS_PIN, LOW); SPI.transfer16(command); digitalWrite(CS_PIN, HIGH); SPI.endTransaction(); return true; } // ==================== 解析逆序字段 ==================== void parseReverseFields(uint8_t* buffer, uint16_t* results, int count) { for (int i = 0; i < count; i++) { int bufIdx = i * 2; results[count - 1 - i] = (buffer[bufIdx] << 8) | buffer[bufIdx + 1]; } } // ==================== 读取所有 CELL 电压 ==================== bool readAllCellVoltages(float* cellVoltages) { const uint16_t cmds[] = { CMD_READ_CELL_1_3, CMD_READ_CELL_4_6, CMD_READ_CELL_7_9, CMD_READ_CELL_10_12, CMD_READ_CELL_13_15, CMD_READ_CELL_16_18 }; const int groupCount = 6; const int perGroup[] = {3, 3, 3, 3, 3, 3}; SPI.beginTransaction(spiSettings); for (int g = 0; g < groupCount; g++) { digitalWrite(CS_PIN, LOW); SPI.transfer16(cmds[g]); uint8_t buffer[8]; for (int i = 0; i < 8; i++) { buffer[i] = SPI.transfer(0x00); } digitalWrite(CS_PIN, HIGH); uint16_t raw[3]; parseReverseFields(buffer, raw, perGroup[g]); for (int i = 0; i < perGroup[g]; i++) { int idx = g * 3 + i; if (idx >= 16) continue; float voltage = (raw[i] - 10240) * 0.0001f; cellVoltages[idx] = voltage; } } SPI.endTransaction(); return true; } // ==================== 读取所有 DIS 电压 ==================== bool readAllDisVoltages(float* disVoltages) { const uint16_t cmds[] = { CMD_READ_DIS_1_3, CMD_READ_DIS_4_6, CMD_READ_DIS_7_9, CMD_READ_DIS_10_12, CMD_READ_DIS_13_15, CMD_READ_DIS_16_18 }; const int groupCount = 6; const int perGroup[] = {3, 3, 3, 3, 3, 3}; SPI.beginTransaction(spiSettings); for (int g = 0; g < groupCount; g++) { digitalWrite(CS_PIN, LOW); SPI.transfer16(cmds[g]); uint8_t buffer[8]; for (int i = 0; i < 8; i++) { buffer[i] = SPI.transfer(0x00); } digitalWrite(CS_PIN, HIGH); uint16_t raw[3]; parseReverseFields(buffer, raw, perGroup[g]); for (int i = 0; i < perGroup[g]; i++) { int idx = g * 3 + i; if (idx >= 16) continue; float voltage = (raw[i] - 10240) * 0.0001f; disVoltages[idx] = voltage; } } SPI.endTransaction(); return true; } // ==================== 读取 VREF2 和 V3 ==================== bool readVref2AndV3(float* vref2, float* v3) { uint8_t buffer[8]; SPI.beginTransaction(spiSettings); digitalWrite(CS_PIN, LOW); SPI.transfer16(CMD_READ_VREF2_V3); for (int i = 0; i < 8; i++) { buffer[i] = SPI.transfer(0x00); } digitalWrite(CS_PIN, HIGH); SPI.endTransaction(); uint16_t raw_v3 = (buffer[0] << 8) | buffer[1]; uint16_t raw_vref2 = (buffer[2] << 8) | buffer[3]; *v3 = (raw_v3 - 10240) * 0.0001f; *vref2 = (raw_vref2 - 10240) * 0.0001f; return true; } // ==================== 读取 V5 / DieTemp / VSTK ==================== bool readV5DieTempVStk(float* v5, float* dieTemp, float* vstk) { uint8_t buffer[8]; SPI.beginTransaction(spiSettings); digitalWrite(CS_PIN, LOW); SPI.transfer16(CMD_READ_V5_DIETEMP_VSTK); for (int i = 0; i < 8; i++) { buffer[i] = SPI.transfer(0x00); } digitalWrite(CS_PIN, HIGH); SPI.endTransaction(); uint16_t raw_vstk = (buffer[0] << 8) | buffer[1]; uint16_t raw_dieTemp = (buffer[2] << 8) | buffer[3]; uint16_t raw_v5 = (buffer[4] << 8) | buffer[5]; *vstk = (raw_vstk - 10240) * 0.0016f; *v5 = (raw_v5 - 10240) * 0.0001f; if (raw_dieTemp >= 8000 && raw_dieTemp <= 30000) { *dieTemp = raw_dieTemp * 0.27662 - 3105.59; } else { *dieTemp = NAN; } return true; } // ==================== 更新 BLE 特征值(从 Core 1 调用)==================== void updateBLECharacteristic() { static char jsonBuffer[512]; // 300 足够容纳 cell[16] + temp int offset = 0; float cell[16]; float dieTemp; // 安全拷贝共享数据 portENTER_CRITICAL(&sensorMutex); memcpy(cell, sharedCellVoltages, sizeof(cell)); dieTemp = sharedDieTemp; portEXIT_CRITICAL(&sensorMutex); // 构造 JSON: {"cell":[...], "temp": ...} offset += snprintf(jsonBuffer + offset, sizeof(jsonBuffer) - offset, "{\"cell\":["); for (int i = 0; i < 16; i++) { offset += snprintf(jsonBuffer + offset, sizeof(jsonBuffer) - offset, "%.3f%s", cell[i], (i < 15) ? "," : ""); } if (!isnan(dieTemp)) { offset += snprintf(jsonBuffer + offset, sizeof(jsonBuffer) - offset, "],\"temp\":%.2f}", dieTemp); } else { offset += snprintf(jsonBuffer + offset, sizeof(jsonBuffer) - offset, "],\"temp\":null}"); } // 防溢出兜底 if (offset >= sizeof(jsonBuffer)) { strcpy(jsonBuffer, "{\"err\":\"long\"}"); } // 发送到 BLE if (pCharacteristic && deviceConnected) { pCharacteristic->setValue(jsonBuffer); pCharacteristic->notify(); // 触发通知 } #ifdef DEBUG_GMD1032 Serial.printf(“BLE Data: %s\n”, jsonBuffer); #endif } // ==================== 核心 0:传感器采集任务 ==================== void sensorTask(void *parameter) { for (;😉 { yield(); // 让出时间片 wakeUpGMD1032(); delay(2); sendCommand(CMD_START_MEASUREMENT); delay(10); float cellVoltages[16] = {0}; float disVoltages[16] = {0}; float vref2 = 0.0f, v3 = 0.0f, v5 = 0.0f, dieTemp = 0.0f, vstk = 0.0f; readAllCellVoltages(cellVoltages); readAllDisVoltages(disVoltages); readVref2AndV3(&vref2, &v3); readV5DieTempVStk(&v5, &dieTemp, &vstk); // 写入共享变量(加锁) portENTER_CRITICAL(&sensorMutex); memcpy(sharedCellVoltages, cellVoltages, sizeof(cellVoltages)); memcpy(sharedDisVoltages, disVoltages, sizeof(disVoltages)); sharedVref2 = vref2; sharedV3 = v3; sharedV5 = v5; sharedDieTemp = dieTemp; sharedVstk = vstk; portEXIT_CRITICAL(&sensorMutex); #ifdef DEBUG_GMD1032 Serial.println(============== GMD1032 全通道数据 ==============); for (int i = 0; i < 16; i++) { if (i % 4 == 0) { Serial.print(" "); } Serial.printf("CELL%2d:%5.3fV ", i + 1, cellVoltages[i]); if ((i + 1) % 4 == 0 || i == 15) { Serial.println(); } } if (!isnan(dieTemp)) { Serial.printf("芯片温度: %.2f°C\n", dieTemp); } else { Serial.println("芯片温度: 无效数据"); } Serial.println("---------------------------------------------"); #endif delay(1000); // 每秒采集一次 } } // ==================== 核心 1:BLE 服务任务 ==================== void bleTask(void *parameter) { BLEDevice::init(“GMD1032-BLE-Slave”); bool mtuSet = BLEDevice::setMTU(256); Serial.printf("MTU 设置 %s\n", mtuSet ? "成功" : "失败"); BLEServer* pServer = BLEDevice::createServer(); pServer->setCallbacks(new MyServerCallbacks()); BLEService* pService = pServer->createService(SERVICE_UUID); pCharacteristic = pService->createCharacteristic( CHARACTERISTIC_UUID, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE | BLECharacteristic::PROPERTY_NOTIFY ); pCharacteristic->addDescriptor(new BLE2902()); pCharacteristic->setCallbacks(new MyCharacteristicCallbacks()); pCharacteristic->setValue("GMD1032 Ready"); pService->start(); BLEAdvertising* pAdvertising = pServer->getAdvertising(); pAdvertising->addServiceUUID(SERVICE_UUID); pAdvertising->start(); Serial.println("BLE 广播已启动,等待连接... (运行于 Core 1)"); for (;;) { if (deviceConnected) { updateBLECharacteristic(); } delay(1000); // 每 1000ms 尝试推送一次 } } // ==================== setup() ==================== void setup() { Serial.begin(115200); while (!Serial && millis() < 3000); #ifdef DEBUG_GMD1032 Serial.println(“[DEBUG] GMD1032 调试模式已启用”); #endif // === 初始化 SPI === pinMode(CS_PIN, OUTPUT); digitalWrite(CS_PIN, HIGH); SPI.begin(SCK_PIN, MISO_PIN, MOSI_PIN, CS_PIN); // === 创建两个独立任务,分别绑定到不同核心 === xTaskCreatePinnedToCore(sensorTask, "SensorTask", 4096, NULL, 1, NULL, 0); // Core 0 xTaskCreatePinnedToCore(bleTask, "BleTask", 4096, NULL, 2, NULL, 1); // Core 1 } // ==================== loop() ==================== void loop() { // 所有功能由 FreeRTOS 任务接管,loop 留空 } 以上为从机代码, #include “spi_slave_server.h” // spi #include <SPI.h> #include <ESP32SPISlave.h> #include “helper.h” ESP32SPISlave slave; // 创建ESP32SPISlave对象 #define SPI_SPLAVE_ENABLE 1 // Adafruit_SPIDevice spi_dev = Adafruit_SPIDevice(SPIDEVICE_CS, 1000000, SPI_BITORDER_MSBFIRST, SPI_MODE0); #define MY_CS 12 #define MY_SCK 13 #define MY_MOSI 14 #define MY_MISO 11 // 自定义spi引脚 // SPIClass my_spi(HSPI); // 创建SPIClass对象my_spi #define SPI_READ_TIMEROUT 999999 // 10 // 50ms static constexpr size_t SPI_BUFFER_SIZE = 16 * 2; // 上位机 测试42 字节数据正常,模式1,定义SPI通信缓冲区大小为32字节 static constexpr size_t QUEUE_SIZE = 1; static uint8_t spi_tx_buf[SPI_BUFFER_SIZE]{1, 2, 3, 4, 5, 6, 7, 8};//发送缓冲区,预置测试数据1-8 static uint8_t spi_rx_buf[SPI_BUFFER_SIZE]{0, 0, 0, 0, 0, 0, 0, 0};//接收缓冲区,初始化为全0 static uint8_t spi_tx_cmd_buf[SPI_BUFFER_SIZE]{0, 0, 0, 0, 0, 0, 0, 0};//命令发送缓冲区,初始化为全0 #define SPI_TX_CACHE_BUF_LEN 1024//发送缓存总大小为1024字节 #define SPI_TX_PAGE_BUF_LEN (SPI_BUFFER_SIZE - 3) // spi 回复数据的最大长度,单次SPI通信有效数据长度 #define SPI_USER_CMD_NULL 0//空指令 #define SPI_USER_CMD_READ 1//读数据 #define SPI_USER_CMD_WRITE 2//写数据 static volatile uint8_t spi_tx_cache_buf[SPI_TX_CACHE_BUF_LEN]{0, 0, 0, 0, 0, 0, 0, 0}; static volatile int spi_tx_cache_buf_cnt = 0; // spi 发送缓存区数据长度 // static int spi_tx_page_cnt=0;//spi 发送缓存区数据长度 static volatile int spi_current_cmd = 0, spi_current_cmd_len = 0; static uint8_t spi_send_busy = 0;//发送忙标志位 static uint8_t spi_send_mode = 1; // 发送模式选择发串口 int spi_slave_data_cache_add(uint8_t data, uint16_t len) { if (len > SPI_TX_CACHE_BUF_LEN) { / code / len = SPI_TX_CACHE_BUF_LEN; } for (int i = 0; i < len; i++) { / code */ spi_tx_cache_buf[i] = data[i]; } spi_tx_cache_buf_cnt = len; return 0; } void IRAM_ATTR my_post_setup_cb(spi_slave_transaction_t *trans)//强制将函数编译到内部RAM,确保中断快速响应 { static uint8_t test_send = 0x11;//静态测试变量 int over_len = spi_tx_cache_buf_cnt - spi_current_cmd_len * SPI_TX_PAGE_BUF_LEN; // 剩余待发送数据量 = 总缓存量 - 已发送页数×单页容量 // Serial.printf("spi slave read over_len = %d cache= %d\r\n", over_len,spi_tx_cache_buf_cnt); if (over_len < 0)//数据已全部发送完成时的保护判断 { return; } if (over_len > SPI_TX_PAGE_BUF_LEN)//单包数据超限时截断处理 { over_len = SPI_TX_PAGE_BUF_LEN; } memset((void *)spi_tx_buf, 0, SPI_BUFFER_SIZE);//清空发送缓冲区(32字节全置0) spi_tx_buf[0] = 0xab; spi_tx_buf[1] = over_len; // 实际长度 spi_tx_buf[2] = spi_current_cmd_len; // 包号 for (int i = 0; i < over_len; i++)//将缓存数据拷贝到发送缓冲区(偏移3字节协议头) { /* code */ spi_tx_buf[3 + i] = spi_tx_cache_buf[spi_current_cmd_len * SPI_TX_PAGE_BUF_LEN + i]; } spi_current_cmd_len++;//包序号自增 if (spi_current_cmd_len >= 3)//包序号归零 { /* code */ spi_current_cmd_len = 0; } // Serial.printf("spi slave read cmd_len = %d ,%02x %02x %02x\n", over_len,spi_tx_buf[1], spi_tx_buf[2], spi_tx_buf[3]); } /*** spi slave 任务 模式设置4 字节 spi cmd: 0xaa 0x01 ,切换模式1,发串口1数据 数据读取 64字节,发aa 01 0xff … spi ack: 0xaa 0x01 ,剩余字节全部回复串口数据 */ static void spi_slave_task(void pvParameters)//FreeRTOS任务函数标准定义 { size_t received_bytes; // 记录接收到的数据字节数 while (1)//无限循环,保持任务持续运行 { / code */ // initializeBuffers(tx_buf, rx_buf, BUFFER_SIZE); int cs = digitalRead(MY_CS);//读取片选信号(CS)引脚状态,检测主设备是否选中本从设备 #if SPI_SPLAVE_ENABLE//条件编译开关,控制SPI从设备功能是否启用 if (cs == 0)//当片选信号为低电平时(表示被主设备选中)执行通信 { memset(spi_rx_buf, 0, SPI_BUFFER_SIZE);//清空接受缓存区 // start and wait to complete one BIG transaction (same data will be received from slave) received_bytes = slave.transfer(spi_tx_buf, spi_rx_buf, SPI_BUFFER_SIZE, SPI_READ_TIMEROUT); //tx_buf:发送缓冲区,rx_buf:接收缓冲区,BUFFER_SIZE:传输长度,READ_TIMEROUT:超时时间 // verify and dump difference with received data if (received_bytes > 0)//成功接收到数据时处理数据 { printf_log_hex(“slave”, spi_rx_buf, received_bytes); printf_log_hex(“slave”, spi_tx_buf, received_bytes); if (spi_rx_buf[1] == 0xaa && spi_rx_buf[2] == 0x01) // 当收到0xAA01时设置为读取模式,读取数据cmd { spi_current_cmd = SPI_USER_CMD_READ; // spi_current_cmd_len = spi_rx_buf[3]; } my_post_setup_cb(NULL);//调用之前分析的发送回调函数 } } #endif vTaskDelay(5 / portTICK_PERIOD_MS);//任务延时5ms } } /** spi slave 初始化 SPI_MODE1 sck 13 miso 14 mosi 11 ss 12 */ void spi_slave_server_init(void) { #if SPI_SPLAVE_ENABLE slave.setDataMode(SPI_MODE3); // default: SPI_MODE1 slave.setQueueSize(QUEUE_SIZE); // default: 1 slave.begin(HSPI, 13, 11, 14, 12); // default: HSPI (please refer README for pin assignments) // pinMode(10, INPUT); // slave.setPostSetupCb(my_post_setup_cb); xTaskCreate(spi_slave_task, “spi_slave_task”, 8 * 1024, NULL, 2, NULL); #endif } 以上为主机spi.cpp // BLEClient.cpp #include “BLEClient.h” #include <Arduino.h> BLEClientHandler::BLEClientHandler() doConnect(false), pServerAddress(nullptr), pClient(nullptr) { } BLEClientHandler::~BLEClientHandler() { if (pServerAddress != nullptr) { delete pServerAddress; pServerAddress = nullptr; } if (pClient != nullptr && !pClient->isConnected()) { delete pClient; pClient = nullptr; } } void BLEClientHandler::begin() { Serial.println(“初始化 BLE 主机…”); BLEDevice::init(""); BLEScan* pScan = BLEDevice::getScan(); pScan->setAdvertisedDeviceCallbacks(new AdvertisedDeviceCallbacks(this)); pScan->setActiveScan(true); pScan->start(0, false); // 0 表示无限期扫描,直到手动 stop } void BLEClientHandler::AdvertisedDeviceCallbacks::onResult(BLEAdvertisedDevice advertisedDevice) { Serial.printf(“发现设备: %s\n”, advertisedDevice.toString().c_str()); // 改为通过 Service UUID 判断是否为目标设备 if (advertisedDevice.isAdvertisingService(BLEUUID(SERVICE_UUID))) { Serial.println("发现目标服务 UUID,准备连接..."); pHandler->pServerAddress = new BLEAddress(advertisedDevice.getAddress()); pHandler->doConnect = true; BLEDevice::getScan()->stop(); // 停止扫描 } } void BLEClientHandler::ClientCallback::onConnect(BLEClient* pClient) { Serial.println(“BLE 连接成功”); } void BLEClientHandler::ClientCallback::onDisconnect(BLEClient* pClient) { Serial.println(“与从机断开连接,准备重连…”); pHandler->doConnect = true; } void BLEClientHandler::notifyCallback(BLERemoteCharacteristic* pChar, uint8_t* pData, size_t length, bool isNotify) { String jsonStr = String((char*)pData).substring(0, length); Serial.printf(“[JSON] 收到数据 (%d 字节): %s\n”, length, jsonStr.c_str()); } bool BLEClientHandler::isConnected() const { return pClient != nullptr && pClient->isConnected(); } void BLEClientHandler::disconnect() { if (pClient && pClient->isConnected()) { pClient->disconnect(); } } void BLEClientHandler::loop() { if (doConnect && pServerAddress != nullptr) { Serial.print("正在连接到: "); Serial.println(pServerAddress->toString().c_str()); pClient = BLEDevice::createClient(); pClient->setClientCallbacks(new ClientCallback(this)); if (pClient->connect(*pServerAddress)) { Serial.println("成功建立连接"); // =============== 关键:提升 MTU =============== if (pClient->setMTU(256)) { unsigned long timeout = millis() + 3000; while (pClient->getMTU() <= 23 && millis() < timeout) { delay(10); } Serial.printf("协商后的 MTU: %d\n", pClient->getMTU()); } else { Serial.println("setMTU(256) 调用失败"); } // 发现服务 BLERemoteService* pRemoteService = pClient->getService(SERVICE_UUID); if (pRemoteService == nullptr) { Serial.println("未发现指定服务"); doConnect = false; return; } BLERemoteCharacteristic* pRemoteCharacteristic = pRemoteService->getCharacteristic(CHARACTERISTIC_UUID); if (pRemoteCharacteristic == nullptr) { Serial.println("未发现指定特征值"); doConnect = false; return; } // 注册通知 if (pRemoteCharacteristic->canNotify()) { pRemoteCharacteristic->registerForNotify(notifyCallback); Serial.println("已启用 Notify"); } doConnect = false; // 防止重复连接 } else { Serial.println("连接失败"); delete pServerAddress; pServerAddress = nullptr; doConnect = false; } } } 以上为主机ble,现在要求按照当前spi的格式,esp32作为spi从机,将通过蓝牙收到的来自1032的数据传输给5606,5606会不间断的发送以AA为开头,后跟FF或00的一串数据,要求esp32在收到5606发出的数据时将1032的数据传输给5606
10-15
11-11
<think>嗯,用户之前问过如何将unsigned char转换为unsignedint,现在转向如何将unsignedchar转换为unsigned short,并且遇到了编译器报错。首先,我需要回顾之前的回答,看看有没有可以复用的内容,但也要注意两者的不同点。比如,unsignedint和unsigned short的范围可能不同,转换时是否会有数据丢失或者符号扩展的问题。用户提供的引用中,引用[1]提到char的符号性取决于编译器,这可能影响转换结果,但这里讨论的是unsignedchar,所以符号性可能不是问题,但需要注意不同平台下unsignedshort的大小。比如,unsignedchar是1字节,而unsigned short可能是2字节,所以在转换时需要考虑高位填充的问题。引用[2]提到函数名的修饰问题,这可能和转换无关,但用户的问题可能涉及到函数参数传递时的类型不匹配,比如传递unsignedchar给需要unsigned short的函数,导致链接错误或者编译错误。这时候可能需要显式转换,或者检查函数声明是否正确。引用[3]是关于打印数据类型大小的,这可能帮助用户确认不同类型的大小,确保转换时不会溢出。比如,如果unsignedshort是2字节,而unsigned char是1字节,直接赋值是安全的,但涉及到位操作或者大端小端的问题时可能需要更谨慎的处理。用户的问题可能出现在直接赋值时的类型不匹配,尤其是在函数调用或者赋值时没有进行显式转换,特别是在C++中,可能需要static_cast。或者在C中,如果编译器设置比较严格,可能需要强制转换。另外,用户之前的问题中提到指针类型转换的问题,这里可能同样适用,比如将unsigned char数组转换为unsigned short数组时,直接指针转换可能导致对齐问题或者违反严格别名规则,这时候应该使用memcpy或者逐字节处理。 可能的解决方案包括隐式转换、显式强制转换、处理指针时的正确方法,以及函数参数传递时的显式转换。同时,需要提醒用户注意不同平台下unsignedshort的大小,避免溢出或截断。还需要考虑用户是否有其他潜在的问题,比如符号扩展,但因为是unsigned到unsigned,可能不会有这个问题。但如果在转换过程中涉及到位运算,比如将两个unsigned char组合成一个unsigned short,可能需要移位和或操作,这时候需要确保操作的正确性。总结下来,回答的结构应该包括转换方法(隐式和显式)、常见错误场景(指针转换、函数参数)、位操作时的注意事项,以及编译器差异带来的影响。同时,生成相关问题,如如何处理多个字节拼接成short,跨平台类型大小差异,以及有符号和无符号转换的问题。</think>### 将$unsigned\ char$转换为$unsigned\ short$的正确方法 #### 基础转换方式 1. **隐式类型提升** C/C++中$unsigned\ char$到$unsigned\ short$的隐式转换是安全的,因为$unsigned\ char$范围($0$-$255$)完全包含在$unsigned\ short$的典型范围($0$-$65535$)内: ```c unsigned char uc = 200; unsigned short us = uc; // 自动完成类型提升[^1] ``` 2. **显式强制转换** 若编译器要求严格类型匹配(如C++中混合不同类型操作数时): ```c unsigned char uc = 200; unsigned short us = (unsigned short)uc; // C风格 unsigned short us2 = static_cast<unsigned short>(uc); // C++风格 ``` #### 常见错误场景及解决 1. **函数参数类型不匹配** 若函数参数声明为$unsigned\ short$,传入$unsigned\ char$需显式转换: ```c void process_data(unsigned short val); unsigned char uc = 150; process_data((unsigned short)uc); // 必须显式转换[^2] ``` 2. **多字节拼接场景** 将多个$unsigned\ char$合并为$unsigned\ short$时需用位运算: ```c unsigned char high_byte = 0x12; unsigned char low_byte = 0x34; unsigned short result = (high_byte << 8) | low_byte; // 正确拼接为0x1234 ``` **注意**:移位前需先将$unsigned\ char$提升为$unsigned\ short$,否则可能因溢出导致错误[^1]。 3. **指针类型错误** 直接指针强转违反严格别名规则: ```c unsigned char buffer[2] = {0xAB, 0xCD}; // 错误方式:unsigned short* p = (unsigned short*)buffer; // 对齐问题+别名规则冲突 // 正确方式:通过内存复制 unsigned short value; memcpy(&value, buffer, sizeof(value)); ``` #### 编译器/平台差异验证 1. 使用`sizeof()`验证类型长度: ```c printf("unsigned char size: %zu\n", sizeof(unsigned char)); // 输出1 printf("unsigned short size: %zu\n", sizeof(unsigned short));// 通常输出2[^3] ``` 2. 若目标平台$unsigned\ short$为1字节(罕见架构),需改用$unsigned\ int$[^3]。 ### 关键区别对比 | 操作 | $unsigned\ char$到$unsigned\ int$ | $unsigned\ char$到$unsigned\ short$ | |----------------|----------------------------------|-----------------------------------| | 隐式提升安全性 | 安全(4字节容纳1字节) | 安全(2字节容纳1字节) | | 移位操作风险 | 需显式提升避免截断 | 需显式提升避免截断(如`<<8`时) | | 指针强转风险 | 4字节对齐要求更高 | 2字节对齐要求 |
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Whitemeen太白

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值