这里记录了两种检测方式,通过中断处理如果旋转太快是有可能丢步的,这里异步方式是基于 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);
}
}