ec11 旋转编码器 一定位一脉冲检测程序

这里记录了两种检测方式,通过中断处理如果旋转太快是有可能丢步的,这里异步方式是基于 embassy 框架的,所有实际效果还与其他任务占用时间有关系。

  • 同步检测方式:

正反转检测逻辑如下:

状态对应为 AL: a 低 AH: a 高 BL: b 低 BH: b 高

逻辑判断,以反转为例,(正反转只是当a 或 b 拉高时,当前的a b电位状态相反):

1.未触发时 AH BH 不进入逻辑

2.开始反转 AL BH 进入逻辑,上次状态为 AH BH, 更新后上次状态为 AL BH

3.一段时间的无状态变化不进入逻辑,此时状态为 AL BH

4.b 进入拉低状态 当前状态为 AL BL,上次状态为 AL BH , 更新后上次状态为 AL BL

5.一段时间的无状态变化不进入逻辑,此时状态为 AL BL

6.a 进入拉高状态 当前状态为 AH BL, 上次状态 为 AL BL 进入方向判断逻辑 ,

如果是正转,此时 状态为 AL BH

如果是反转,此时 状态为 AH BL

    let a_point = io.pins.gpio5.into_pull_up_input();
    let b_point = io.pins.gpio4.into_pull_up_input();
// 初始化编码器状态
    let mut last_a_state = a_point.is_low();
    let mut last_b_state = b_point.is_low();

    // 开始监听编码器状态变化
    let mut num = 0;
    loop {
        let current_a_state = a_point.is_low();
        let current_b_state = b_point.is_low();
        let ticks = Instant::now().as_millis();
        //println!("ticks:{}",ticks);


        // 检查状态变化,确定旋转方向
        /***
        AL a 低   AH  a 高   BL b 低  BH b 高
        逻辑判断,以反转为例,(正反转只是当a 或 b 拉高时,当前的a b电位状态相反):
        1.未触发时 AH BH 不进入逻辑
        2.开始反转 AL BH 进入逻辑,上次状态为 AH BH, 更新后上次状态为 AL BH
        3.一段时间的无状态变化不进入逻辑,此时状态为 AL BH
        4.b 进入拉低状态 当前状态为 AL BL,上次状态为 AL BH , 更新后上次状态为 AL BL
        5.一段时间的无状态变化不进入逻辑,此时状态为 AL BL
        6.a 进入拉高状态 当前状态为 AH BL, 上次状态 为 AL BL  进入方向判断逻辑 ,
            如果是正转,此时 状态为 AL BH
            如果是反转,此时 状态为 AH BL
         */
        if current_a_state != last_a_state || current_b_state != last_b_state {
            if last_a_state.unwrap()  && last_b_state.unwrap() {
                if current_a_state.unwrap() && !current_b_state.unwrap() {
                    // 正转
                    num = num + 1;
                    println!("正转");
                    println!("num :{}",num);
                } else if !current_a_state.unwrap() && current_b_state.unwrap(){
                    // 反转
                    num = num - 1;
                    println!("反转");
                    println!("num :{}",num);
                }
            }
            // 更新状态
            last_a_state = current_a_state;
            last_b_state = current_b_state;
        }
    }
  • 通过中断异步检测方式:

正反转检测逻辑如下:

通过a 的下降沿记录开始方向,再通过a的上升沿判断结束时的方向,如果两个方向一致则触发。使用多次采样消抖。

可以考虑在触发结束待几ms后再去触发刷新绘制可能会好一点,因为是单核cpu在绘制时,cpu无法处理编码器导致丢步。

#[derive(Eq, PartialEq)]
enum BeginDirection {
    FRONT,
    BACK,
    NO_STATE,
}
const SAMPLE_TIMES:u32 = 10;
const JUDGE_TIMES:u32 = 8;

#[embassy_executor::task]
pub async fn task(mut a_point :Gpio1<Input<PullUp>>,mut b_point :Gpio0<Input<PullUp>>){
    // 初始化编码器状态

    let mut begin_state = BeginDirection::NO_STATE;
    //let renderSender = display::RENDER_CHANNEL.sender();//发送刷新屏幕通知
    // 初始化编码器状态
    let mut last_a_state = a_point.is_low();
    let mut last_b_state = b_point.is_low();

    // 开始监听编码器状态变化
    let mut num:i32 = 0;
    loop {
        a_point.wait_for_any_edge().await;

        //多次采样去抖
        let mut a_is_low_times = 0;
        let mut b_is_low_times = 0;
        for i in 0..SAMPLE_TIMES {
            if a_point.is_low().unwrap() {
                a_is_low_times += 1;
            }
            if b_point.is_low().unwrap() {
                b_is_low_times += 1;
            }
        }
        println!("a_is_low_times:{}",a_is_low_times);
        println!("b_is_low_times:{}",b_is_low_times);
        let mut a_is_down = false;
        let mut b_is_down = false;
        if(a_is_low_times > JUDGE_TIMES){
            println!("下降沿");
            a_is_down = true;
        }else if a_is_low_times < SAMPLE_TIMES - JUDGE_TIMES {
            println!("上升沿");
            a_is_down = false;
        }else {
            continue;
        }
        if b_is_low_times > JUDGE_TIMES {
            b_is_down = true;
        }else if b_is_low_times < SAMPLE_TIMES - JUDGE_TIMES {
            b_is_down = false;
        }else {
            continue;
        }

        let ticks = Instant::now().as_millis();
        println!("ticks:{}",ticks);

        //下降沿开始
        if(a_is_down){
            if(b_is_down){
                begin_state = FRONT;
                println!("开始正转");
                continue;
            }else if(!b_is_down){
                begin_state =  BACK;
                println!("开始反转");
                continue;
            }
            begin_state = NO_STATE;

        }else{
            //上升沿判断结束
            if(!b_is_down){
                if begin_state == FRONT  {
                    println!("正转");
                    num +=10;
                    //renderSender.send(RenderInfo{num}).await;
                }
            }else if(b_is_down){
                if begin_state == BACK {
                    println!("反转");
                    num -=10;
                    //renderSender.send(RenderInfo{num}).await;
                }
            }
            begin_state = NO_STATE;
        }

        println!("num:{}",num);

    }

}