#include #include "bsp/bsp_driver.h" #include "os/os_task.h" #include "libs/utils.h" #include "libs/logger.h" #include "math/fast_math.h" #include "foc/motor/hall.h" #include "foc/mc_config.h" #include "app/nv_storage.h" #include "libs/logger.h" #define HALL_READ_TIMES 5 #define SMOOTH_COUNT 0 #define HALL_DETECT_OFFSET 0 /* 1,5,4,6,2,3 0,1,2,3,4,5 ////// 2,6,4,5,1,3 0,1,2,3,4,5 //// 2,3,1,5,4,6 0,1,2,3,4,5 */ static s8 hall_2_pos[] = {7,2,0,1,4,3,5,7}; static hall_t g_hall; #define us_2_s(tick) ((float)tick / 1000000.0f) //s32q14 #define HALL_MAX_TIME 1000000UL static u32 g_delta_cnt = 0; static u32 g_trns_cnt = 0; static u32 g_min_delta; #define COUNT_2_US(c) (c/(SYSTEM_CLOCK/1000000)) #if HALL_DETECT_OFFSET > 0 static float hall_off[6]; static u32 hall_off_cnt[6]; static void hall_offset_dec_init(void) { for (int i = 0; i < 6; i++) { hall_off[i] = 0; hall_off_cnt[i] = 0; } } float mc_get_mot_angle(void); static __INLINE void hall_offset_update(void) { int pos_int = (int)g_hall.low_res_pos; float hall_pos = g_hall.low_res_pos * PHASE_60_DEGREE; float delta_pos = hall_pos - (mc_get_mot_angle() + 90.0f); norm_angle_deg(delta_pos); hall_off[pos_int] = delta_pos; hall_off_cnt[pos_int] += 1; } static void hall_offset_log(void) { if (hall_off_cnt[0] < 20) { return; } s16 value[6]; u32 mask = cpu_enter_critical(); for (int i = 0; i < 6; i++) { value[i] = (s16)(hall_off[i]/(float)hall_off_cnt[i]); hall_off_cnt[i] = 0; } cpu_exit_critical(mask); sys_debug("off:%d,%d,%d,%d,%d,%d\n", value[0], value[1], value[2], value[3], value[4], value[5]); } #else static void hall_offset_dec_init(void) { } static __INLINE void hall_offset_update(void) { } static void hall_offset_log(void) { } #endif static __INLINE u32 hall_delta_us(u32 count) { u32 now = task_ticks_abs(); u32 delta = now - count; if (now < count) { //wrap delta = 0xFFFFFFFF - count + now + 1; } return COUNT_2_US(delta); } static u8 __INLINE hall_read_state(void) { u8 hall_a = 0, hall_b = 0, hall_c = 0; for (int i = 0; i < HALL_READ_TIMES; i++) { hall_a += gpio_hall_a_value(); hall_b += gpio_hall_b_value(); hall_c += gpio_hall_c_value(); } u8 state = 0; if (hall_a > (HALL_READ_TIMES/2 + 1)) { state = 1; } if (hall_b > (HALL_READ_TIMES/2 + 1)) { state = state | (1<<1); } if (hall_c > (HALL_READ_TIMES/2 + 1)) { state = state | (1 << 2); } return state; } static void hall_init_low_pos(void) { u8 state = hall_read_state(); s16 pos = hall_2_pos[state]; if (pos == 7) { g_hall.sig_errors ++; return; } g_hall.state = state; if (g_hall.dir == POSITIVE) { g_hall.low_res_pos = pos + 0.5f; }else { g_hall.low_res_pos = pos - 0.5f; } } static void __INLINE hall_put_sample(u32 ticks) { hsample_t *s = &g_hall.samples; g_hall.last_delta_ticks = ticks; s->ticks_sum -= s->ticks[s->index]; s->ticks[s->index] = ticks; s->ticks_sum += s->ticks[s->index]; s->index += 1; if (s->index >= SAMPLE_MAX_COUNT) { s->index = 0; } if (s->filled < SAMPLE_MAX_COUNT) { s->filled++; } } static float __INLINE hall_elec_angle_vel(void){ hsample_t *s = &g_hall.samples; if (s->filled < SAMPLE_MAX_COUNT) { return (PHASE_60_DEGREE) / us_2_s(g_hall.last_delta_ticks); } if (s->ticks_sum == 0) { return 0.0f; } return (PHASE_60_DEGREE * SAMPLE_MAX_COUNT) / us_2_s(s->ticks_sum); } void hall_debug_log(void) { sys_debug("angle dir %d, stat %d, lowres %f, err %d,%d\n", g_hall.dir, g_hall.state, g_hall.low_res_pos, g_hall.sig_errors, g_hall.noise_errors); sys_debug("D: %d,%d,%d\n", g_delta_cnt, g_trns_cnt, g_min_delta); sys_debug("RPM: %f\n", hall_get_velocity()); hall_offset_log(); } void hall_init(void) { g_hall.phase_offset = mc_conf()->m.encoder_offset; g_hall.mot_poles = mc_conf()->m.poles; g_hall.b_trns_det = false; g_hall.angle_smooth_cnt = SMOOTH_COUNT + 1; g_hall.angle_smooth_step = 0; g_hall.samples.ticks_sum = 0; g_hall.position = 0; g_hall.samples.index = 0; g_hall.samples.filled = 0; g_hall.elec_angle_vel = 0; g_hall.edge_ticks = 0; g_hall.dir_set = g_hall.prev_dir = g_hall.dir = POSITIVE; g_min_delta = 1000000 * 100; for (int i = 0; i < SAMPLE_MAX_COUNT; i++) { g_hall.samples.ticks[i] = HALL_MAX_TIME; } g_hall.samples.ticks_sum = HALL_MAX_TIME * SAMPLE_MAX_COUNT; if (!g_hall.inited) { g_hall.inited = true; #ifdef HALL_TIMER hall_tmr_init(); #else gpio_hall_init(); #endif } hall_offset_dec_init(); hall_init_low_pos(); } void hall_set_direction(s8 dir) { if (dir == g_hall.dir_set) { return; } g_hall.dir_set = g_hall.dir = g_hall.prev_dir = dir; hall_init_low_pos(); } #if SMOOTH_COUNT > 0 static float get_angle_diff(float a1, float a2) { float diff = a1 - a2; float abs_diff = ABS(diff); if (abs_diff >= PHASE_180_DEGREE) { return (PHASE_360_DEGREE - abs_diff); }else { return diff; } } #endif static __INLINE bool hall_update_low_pos(void) { u8 state = hall_read_state(); s16 pos = hall_2_pos[state]; if (pos == 7) { g_hall.sig_errors ++; return false; } s16 pos_prev = hall_2_pos[g_hall.state]; s16 delta_pos = pos - pos_prev; if (delta_pos != 0) { s8 prev_dir = g_hall.dir; if (delta_pos == 1 || delta_pos == -5) { g_hall.dir = POSITIVE; g_hall.prev_dir = prev_dir; }else if (delta_pos == -1 || delta_pos == 5){ g_hall.dir = NEGATIVE; g_hall.prev_dir = prev_dir; }else { if (g_hall.samples.filled > 0) { g_hall.sig_errors ++; return false; } } g_hall.edge_ticks = task_ticks_abs(); }else { g_hall.sig_errors ++; return false; } g_hall.state = state; g_hall.low_res_pos = pos; return true; } hall_t *hall_get(void) { return &g_hall; } float hall_get_elec_angle(void) { float phase_offset = g_hall.phase_offset; if (g_hall.dir == NEGATIVE) { phase_offset = PHASE_60_DEGREE + phase_offset; } float angle = g_hall.elec_angle + phase_offset; norm_angle_deg(angle); return angle; } float hall_update_elec_angle(void) { float delta_ticks = (float)hall_delta_us(g_hall.edge_ticks);//上次hall变换到目前的时间 float low_res = g_hall.low_res_pos; float delta_pos = g_hall.elec_angle_vel / PHASE_60_DEGREE * us_2_s(delta_ticks) * g_hall.dir;//上次hall变换到目前走过的角度(对60度的比值,小于1),通过速度插值 if (delta_pos > 1.0f) { delta_pos = 1.0f; }else if (delta_pos < -1.0f) { delta_pos = -1.0f; } float high_res_pos = delta_pos + low_res; float elec_angle = high_res_pos * PHASE_60_DEGREE; float elec_smooth_angle; #if SMOOTH_COUNT>0 float delta_angle = delta_pos * PHASE_60_DEGREE; if (g_hall.angle_smooth_cnt < (SMOOTH_COUNT + 1)) { elec_smooth_angle = g_hall.elec_angle_edge + g_hall.angle_smooth_step * g_hall.angle_smooth_cnt + delta_angle; g_hall.angle_smooth_cnt++; if (g_hall.angle_smooth_step >= 0) { elec_smooth_angle = min(elec_smooth_angle, elec_angle); }else { elec_smooth_angle = MAX(elec_smooth_angle, elec_angle); } }else { elec_smooth_angle = elec_angle; } #else elec_smooth_angle = elec_angle; #endif norm_angle_deg(elec_smooth_angle); g_hall.elec_angle = elec_smooth_angle; g_hall.position += g_hall.elec_angle_vel * FOC_CTRL_US / g_hall.mot_poles; if ((g_hall.elec_angle_vel > 0.000001f) && (delta_ticks >= HALL_MAX_TIME)) { g_hall.elec_angle_vel = 0; g_hall.samples.filled = 0; g_hall.samples.index = 0; g_hall.velocity_filted = 0; g_hall.dir = g_hall.prev_dir = g_hall.dir_set; hall_init_low_pos(); } return hall_get_elec_angle(); } float hall_get_vel_counts(void) { return g_hall.elec_angle_vel/PHASE_60_DEGREE * g_hall.dir; } float hall_get_velocity(void) { return g_hall.velocity_filted; } float hall_get_position(void) { return g_hall.position; } static __INLINE void hall_calc_mot_velocity(u32 delta_cnt) { if (g_hall.samples.filled == 0) { hall_put_sample(HALL_MAX_TIME/10); return; } if (delta_cnt < g_min_delta) { g_min_delta = delta_cnt; } hall_put_sample(delta_cnt); g_hall.elec_angle_vel = hall_elec_angle_vel(); float velocity_raw = g_hall.elec_angle_vel/PHASE_360_DEGREE/g_hall.mot_poles * 60.0f * g_hall.dir; LowPass_Filter(g_hall.velocity_filted, velocity_raw, 0.5F); } float hall_offset_detect(float *off) { return 0.0f; } void HALL_IRQHandler(void) { g_delta_cnt++; u32 mask = cpu_enter_critical(); u32 delta_cnt = hall_delta_us(g_hall.edge_ticks); if (delta_cnt < 200) { //1200rpm, 30 poles g_hall.noise_errors++; goto hall_end; } if (!hall_update_low_pos()) { goto hall_end; } hall_offset_update(); g_hall.elec_angle_edge = g_hall.elec_angle; #if SMOOTH_COUNT>0 g_hall.delta_angle_edge = get_angle_diff(g_hall.low_res_pos * PHASE_60_DEGREE, g_hall.elec_angle_edge); if (ABS(g_hall.delta_angle_edge) >= 2.0f) { g_hall.angle_smooth_step = g_hall.delta_angle_edge/SMOOTH_COUNT; g_hall.angle_smooth_cnt = 1; }else { g_hall.angle_smooth_step = 0; g_hall.angle_smooth_cnt = SMOOTH_COUNT + 1; } #endif hall_calc_mot_velocity(delta_cnt); hall_end: cpu_exit_critical(mask); return; }