#include #include "bsp/bsp.h" #include "bsp/mc_hall_gpio.h" #include "os/co_task.h" #include "os/timer.h" #include "libs/utils.h" #include "libs/logger.h" #include "math/fast_math.h" #include "hall_sensor.h" #include "foc/foc_api.h" #include "app/nv_storage.h" #include "bsp/timer_count32.h" #define HALL_READ_TIMES 3 static void _hall_detect_task(void *args); /* * 测量HALL_PLACE_OFFSET通用方式就是ST推荐的通过外力带动电机, * 测量电机U相反电动势和hall 1的上升沿之间的差值 * 这里使用的是先通过hall_sensor_calibrate测量hall1,2,3,4,5,6 * 对应的角度(偏差比较大),然后启动电机,让HALL_PLACE_OFFSET * 从0开始增加,每增加1度观察电机电流(看直流电源), * 找到一个电机平稳转动并且电流最小的角度作为HALL_PLACE_OFFSET */ #define HALL_PLACE_OFFSET 210 /* 100 101 001 011 010 110 4,5,1,3,2,6,4 */ //static u16 _hall_table[] = {0xFFFF, 292/*1*/, 54/*2*/, 1/*3*/, 180/*4*/, 229/*5*/, 115/*6*/, 0xFFFF}; static u16 _hall_table[] = {0xFFFF, 121/*1*/, 240/*2*/, 190/*3*/, 13/*4*/, 58/*5*/, 306/*6*/, 0xFFFF}; static hall_t _hall; static hall_sample_t h_samples; measure_time_t g_meas_hall = {.exec_max_time = 6,}; #define read_hall(h,t) {h = get_hall_stat(HALL_READ_TIMES); t = _hall_table[h];} #define us_2_s(tick) ((float)tick / 1000000.0f) static float __inline _delta_seconds(u32 prev) { return (float)timer_count32_delta_us(prev, NULL)/1000000.0f; } static void __inline _hall_put_sample(u32 ticks) { hall_sample_t *s = &h_samples; s->index += 1; if (s->index >= SAMPLE_MAX_COUNT) { s->full = true; s->index = 0; } s->ticks[s->index] = ticks; } static float __inline _hall_avg_speed(void){ hall_sample_t *s = &h_samples; float t_angle = (float)(PHASE_60_DEGREE * SAMPLE_MAX_COUNT); u32 t_ticks = 0; for (int i = 0; i < SAMPLE_MAX_COUNT; i++) { t_ticks += s->ticks[i]; } if (t_ticks == 0.0f) { return 0.0f; } return (t_angle / us_2_s(t_ticks)); } void hall_sensor_init(void) { mc_hall_init(); memset(&_hall, 0, sizeof(_hall)); read_hall(_hall.state, _hall.theta); _hall.phase_offset = HALL_PLACE_OFFSET;//mc_config_get()->hall_offset; co_task_create(_hall_detect_task, NULL, 512); } void hall_sensor_clear(void) { memset(&_hall, 0, sizeof(_hall)); read_hall(_hall.state, _hall.theta); _hall.phase_offset = HALL_PLACE_OFFSET;//mc_config_get()->hall_offset; } static void _hall_detect_task(void *args) { while(1) { if (_hall.working) { u32 ticks_now = timer_count32_get(); u32 delta_us = timer_count32_getus(ticks_now, _hall.ticks); if (delta_us >= (1200*1000)) { hall_sensor_clear(); } } co_task_delay(100); } } u16 *hall_phase_angle(void) { return _hall_table; } float hall_sensor_get_theta(void){ if (_hall.is_override_theta) { return _hall.override_theta; } if (!_hall.working) { read_hall(_hall.state, _hall.theta); return _hall.theta; } _hall.est_theta = _delta_seconds(_hall.ticks) * _hall.angle_speed * _hall.direction + _hall.theta; float est_delta = _hall.est_theta - _hall.theta; if (est_delta > PHASE_60_DEGREE) { _hall.est_theta = _hall.theta + PHASE_60_DEGREE; if (_hall.est_theta >= PHASE_360_DEGREE){ _hall.est_theta -= PHASE_360_DEGREE; } }else if (est_delta < -PHASE_60_DEGREE){ _hall.est_theta = _hall.theta - PHASE_60_DEGREE; if (_hall.est_theta < 0/*= -PHASE_360_DEGREE*/) { _hall.est_theta += PHASE_360_DEGREE; } } return _hall.est_theta; } void hall_sensor_set_theta(bool override, float theta){ _hall.is_override_theta = override; _hall.override_theta = theta; } float hall_sensor_get_speed(void) { return _hall.rpm; } float hall_sensor_avg_speed(void) { return _hall.angle_speed_avg / 360 * 60.0f; } int hall_offset_increase(int inc) { if (_hall.phase_offset + inc >= 360) { _hall.phase_offset = _hall.phase_offset + inc - 360; }else { _hall.phase_offset += inc; } return _hall.phase_offset; } int hall_sensor_calibrate(float voltage, u16 *hall_table){ if (hall_table == NULL) { hall_table = _hall_table; } foc_set_controller_mode(FOC_MODE_OPEN_LOOP); hall_sensor_set_theta(true, 0.0f); foc_set_dq_command(0.0f, 0.0f); foc_pwm_start(true); for (int i = 0;i < 1000;i++) { foc_set_dq_command((float)i * voltage / 1000.0f, 0.0f); delay_ms(1); } float sin_hall[8]; float cos_hall[8]; int hall_iterations[8]; memset(sin_hall, 0, sizeof(sin_hall)); memset(cos_hall, 0, sizeof(cos_hall)); memset(hall_iterations, 0, sizeof(hall_iterations)); delay_ms(2 * 1000); // Forwards for (int i = 0;i < 5;i++) { for (int j = 0;j < 360;j++) { hall_sensor_set_theta(true, j); delay_ms(5); int hall = get_hall_stat(7); float s, c; normal_sincosf(degree_2_pi(j), &s, &c); sin_hall[hall] += s; cos_hall[hall] += c; hall_iterations[hall]++; } } delay_ms(2 * 1000); // Reverse for (int i = 0;i < 5;i++) { for (int j = 360;j >= 0;j--) { hall_sensor_set_theta(true, j); delay_ms(5); int hall = get_hall_stat(7); float s, c; normal_sincosf(degree_2_pi(j), &s, &c); sin_hall[hall] += s; cos_hall[hall] += c; hall_iterations[hall]++; } } foc_pwm_start(false); hall_sensor_set_theta(false, 0.0f); foc_set_dq_command(0.0f, 0.0f); int fails = 0; for(int i = 0;i < 8;i++) { if (hall_iterations[i] > 30) { float ang = pi_2_degree(atan2f(sin_hall[i], cos_hall[i])); fast_norm_angle(&ang); hall_table[i] = (u16)ang; } else { hall_table[i] = 0xFFFF; fails++; } } return fails == 2; } static s32 _hall_position(u8 state_now, u8 state_prev) { s32 theta_now = 0xFFFFFFFF; switch (state_now) { case STATE_1: if (state_prev == STATE_5) { _hall.direction = POSITIVE; theta_now = _hall.phase_offset + PHASE_60_DEGREE; }else if (state_prev == STATE_3) { _hall.direction = NEGATIVE; theta_now = _hall.phase_offset + PHASE_120_DEGREE; } break; case STATE_2: if (state_prev == STATE_3) { _hall.direction = POSITIVE; theta_now = _hall.phase_offset + PHASE_180_DEGREE; }else if (state_prev == STATE_6) { _hall.direction = NEGATIVE; theta_now = _hall.phase_offset + PHASE_240_DEGREE; } break; case STATE_3: if (state_prev == STATE_1) { _hall.direction = POSITIVE; theta_now = _hall.phase_offset + PHASE_120_DEGREE; }else if (state_prev == STATE_2) { _hall.direction = NEGATIVE; theta_now = _hall.phase_offset + PHASE_180_DEGREE; } break; case STATE_4: if (state_prev == STATE_6) { _hall.direction = POSITIVE; theta_now = _hall.phase_offset + PHASE_300_DEGREE; }else if (state_prev == STATE_5) { _hall.direction = NEGATIVE; theta_now = _hall.phase_offset; } break; case STATE_5: if (state_prev == STATE_4) { _hall.direction = POSITIVE; theta_now = _hall.phase_offset; }else if (state_prev == STATE_1) { _hall.direction = NEGATIVE; theta_now = _hall.phase_offset + PHASE_60_DEGREE; } break; case STATE_6: if (state_prev == STATE_2) { _hall.direction = POSITIVE; theta_now = _hall.phase_offset + PHASE_240_DEGREE; }else if (state_prev == STATE_4) { _hall.direction = NEGATIVE; theta_now = _hall.phase_offset + PHASE_300_DEGREE; } break; default: return 0xFFFFFFFF; } if (theta_now >= 360) { theta_now -= 360; } return theta_now; } void hall_sensor_handler(void) { time_measure_start(&g_meas_hall); u8 state_now = get_hall_stat(HALL_READ_TIMES); u8 state_prev = _hall.state; u32 ticks_now = timer_count32_get(); _hall.state = get_hall_stat(HALL_READ_TIMES); if (!_hall.working) { if(_hall_table[state_now] != 0xFFFF) { _hall.working = true; _hall.state = state_now; _hall.theta = _hall_table[state_now]; _hall.ticks = ticks_now; } return; } s32 theta_now = _hall_position(_hall.state, state_prev); if (theta_now == 0xFFFFFFFF) { _hall.state = state_prev; return; } u32 delta_us = timer_count32_getus(ticks_now, _hall.ticks); if (delta_us == 0) { return; } os_disable_irq(); float delta_time = us_2_s(delta_us); _hall_put_sample(delta_us); _hall.angle_speed = (float)PHASE_60_DEGREE / delta_time; if (!h_samples.full) { _hall.angle_speed_avg = _hall.angle_speed; }else { _hall.angle_speed_avg = _hall_avg_speed(); } _hall.theta = theta_now; _hall.state = state_now; _hall.ticks = ticks_now; os_enable_irq(); _hall.rpm = _hall.angle_speed / 360.0f * 60.0f; time_measure_end(&g_meas_hall); }