EV3100电梯专用变频器源代码
注意看那个modulation_index参数,相当于调制深度,超过1就会进入过调制区域,这时候波形会出现削顶,就像音响开太大破音似的。这种设计在资源受限的嵌入式系统中很常见,但要注意电源突然断开时的数据完整性。注意最后那个高低字节交换操作,Modbus协议要求CRC校验码是低位在前,而算法计算出来的是高位在前,所以需要手动调换顺序。注意到当门区打开时,如果电梯还在以0.2m/s以上的速度移动,就
EV3100电梯专用变频器源代码
翻开源码包看到motorcontrol.c文件里躺着的svpwmgenerate函数,这玩意儿绝对是整个变频器的灵魂。咱们电梯运行时那种丝滑的启停体验,八成是靠这个空间矢量脉宽调制算法撑起来的。看这段代码:
void SVPWM_Generate(MotorState* motor) {
// 计算扇区号
float theta = motor->electric_angle % (2*PI);
int sector = (int)(theta / (PI/3));
// 计算相邻矢量作用时间
float T1 = motor->modulation_index * sin(sector*PI/3 - theta);
float T2 = motor->modulation_index * sin(theta - (sector-1)*PI/3);
float T0 = 1 - T1 - T2;
// 设置PWM比较寄存器
PWM_SetCompare(sector_map[sector][0], T1 * PWM_PERIOD);
PWM_SetCompare(sector_map[sector][1], T2 * PWM_PERIOD);
PWM_SetDeadTime(T0 * 0.1 * PWM_PERIOD); // 死区时间补偿
}
这个扇区判断逻辑让我想起披萨切块——把整个电周期切成6块,每块60度。T1和T2这两个时间参数的计算其实就是矢量合成的数学魔术,用正弦函数把旋转磁场拆解成两个相邻矢量的线性组合。注意看那个modulation_index参数,相当于调制深度,超过1就会进入过调制区域,这时候波形会出现削顶,就像音响开太大破音似的。
安全回路处理部分的代码更有意思,在safety_monitor.c里藏着个状态机:
typedef enum {
SAFE,
BRAKE_ENGAGED,
OVERSPEED,
DOOR_OPEN
} SafetyState;
void update_safety_state(Elevator* elev) {
static SafetyState prev_state = SAFE;
if(elev->current_speed > elev->rated_speed * 1.15) {
trigger_emergency_brake();
prev_state = OVERSPEED;
log_error("超速125%触发安全钳");
}
else if(!door_sensor_get_status()) {
if(prev_state == BRAKE_ENGAGED && elev->current_speed > 0.2) {
cut_power_supply();
log_warning("门区异常移动");
}
prev_state = DOOR_OPEN;
}
// 其他状态判断...
}
这个状态机的跳转条件设计得很讲究,特别是门锁信号和移动速度的联合判断。注意到当门区打开时,如果电梯还在以0.2m/s以上的速度移动,就会直接切断电源——这个阈值比国标要求的0.8m/s还严格,看来厂家在安全余量上留了后手。
调试时最头疼的可能是楼层位置自学习功能。在homing.c里有段磁极对齐的代码:
void perform_homing() {
// 低速旋转寻找Z脉冲
set_motor_speed(30); // 30rpm低速
while(!z_pulse_detected()) {
feed_watchdog();
if(timeout_check(5000)) {
throw_exception(HOMING_TIMEOUT);
}
}
// 精确定位阶段
for(int i=0; i<3; i++) { // 三次平均减少误差
precise_adjustment();
save_encoder_position();
}
write_nvram(homing_data); // 掉电保存
}
这个自学习过程就像盲人摸象,先低速旋转找零位信号Z脉冲,找到后再做三次微调取平均。注意到喂狗操作和超时处理,这是工业代码的典型特征——永远要考虑最坏情况。write_nvram那行说明位置信息需要掉电保存,下次上电直接读取,避免每次都要重新寻址。
翻到通讯协议部分,modbus_rtu.c里的CRC校验函数写法很妖:
uint16_t crc16(uint8_t *data, uint8_t length) {
uint16_t crc = 0xFFFF;
while(length--) {
crc ^= *data++;
for(int i=0; i<8; i++) {
if(crc & 0x0001) {
crc = (crc >> 1) ^ 0xA001;
} else {
crc >>= 1;
}
}
}
return (crc << 8) | (crc >> 8); // 高低字节交换
}
这个位运算实现的CRC16查表法,比查表法省ROM空间,但牺牲了点速度。注意最后那个高低字节交换操作,Modbus协议要求CRC校验码是低位在前,而算法计算出来的是高位在前,所以需要手动调换顺序。这种细节就像吃鱼要挑刺,稍不注意就会被卡住。
最后在故障记录模块里发现个巧妙的设计:
#pragma pack(1)
typedef struct {
uint32_t timestamp;
uint16_t error_code;
float motor_current;
uint8_t drive_temp;
uint16_t reserved;
} ErrorLogEntry;
#pragma pack()
void save_error_log(ErrorLogEntry entry) {
uint8_t buffer[sizeof(ErrorLogEntry)];
memcpy(buffer, &entry, sizeof(entry));
flash_write(current_address, buffer);
current_address += sizeof(entry);
if(current_address >= MAX_LOG_SIZE) {
current_address = LOG_BASE_ADDRESS; // 循环覆盖
}
}
用#pragma pack(1)取消结构体对齐,保证存储时紧密排列,这样flash空间利用率最高。循环覆盖策略避免日志爆仓,老故障自动被新记录覆盖,类似行车记录仪的工作原理。这种设计在资源受限的嵌入式系统中很常见,但要注意电源突然断开时的数据完整性。

openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。
更多推荐


所有评论(0)