帝森永大艾默生三菱电梯dsp读写方案
最近在电梯控制板逆向工程里折腾帝森永大和艾默生三菱的DSP方案,发现它们的通信协议设计得挺有意思。今天就手撕一下DSP数据读写的核心逻辑,顺便分享几个实战中总结的骚操作。
先看个典型的DSP寄存器读取代码片段:
uint16_t read_dsp_register(uint8_t reg_addr) { SPI_CS_LOW(); // 发送读命令+寄存器地址 uint8_t cmd = 0x80 | (reg_addr & 0x3F); HAL_SPI_Transmit(&hspi1, &cmd, 1, 100); // 接收双字节数据(注意时钟相位) uint8_t rx_buf[2]; HAL_SPI_Receive(&hspi1, rx_buf, 2, 100); SPI_CS_HIGH(); // 数据校验(奇偶位在第15位) uint16_t data = (rx_buf[0] << 8) | rx_buf[1]; if((data >> 15) != (reg_addr & 0x01)) { throw校验异常; } return data & 0x7FFF; }这代码里藏着几个关键点:
- 命令字最高位固定为1表示读操作(0x80),寄存器地址只取低6位
- SPI时钟在下降沿采样,和常规配置相反(实测发现用默认相位会丢数)
- 校验位复用寄存器地址的最低位,这设计真是省到家了
写操作更有意思,厂家做了个双缓冲机制。直接写会进影子寄存器,需要发特定脉冲才能生效:
def write_parameter(slot, value): spi.xfer([0x00, slot >> 8, slot & 0xFF]) spi.xfer([0x01, (value >> 8) & 0xFF, value & 0xFF]) # 拍板脚触发(需要精准时序) gpio.set(FLASH_PIN, 1) time.sleep(12e-6) # 精确到微秒级 gpio.set(FLASH_PIN, 0) # 防手抖机制(500ms内禁止重复操作) last_write_time = time.time()遇到过最坑爹的是某型号的CRC校验算法,手册里压根没提。后来用逻辑分析仪抓了200多组数据,反推出是变种CRC-8:
uint8_t weird_crc(uint8_t *data, int len) { uint8_t crc = 0x5A; // 初始值诡异 for(int i=0; i<len; ++i){ crc ^= data[i]; for(int j=0; j<8; ++j){ if(crc & 0x80) { crc = (crc << 1) ^ 0x7D; // 多项式不标准 } else { crc <<= 1; } } } return ~crc; // 还取反! }实战中建议做好超时重试机制。某次现场升级遇到电磁干扰,后来在读写函数里加了指数退避重试:
def safe_dsp_write(data, retries=5): for attempt in range(retries): try: return raw_write(data) except TimeoutError: sleep_time = 0.1 * (2 ** attempt) time.sleep(sleep_time) raise DSPCommError("写入失败,检查接线或干扰源")最后提醒下,不同批次的板子可能存在协议差异。去年遇到过V2.3版突然把SPI时钟极性反转的情况,建议在初始化时先发0xAA探测设备特征码。搞电梯DSP这玩意儿,永远要做好面对惊喜的心理准备。