STM32-编码器的软件解码

it2023-07-20  66

编码器的输出AB两相的波形如下图所示,其原理不赘述。

软件解码方案有两种

1.查表法

有波形可以知道,波形有四种状态组成:

A相0110B相0011

当AB相波形经历00—10—11—01的状态就表示一个正向步进。反之则表示反向步进。

读引脚电平的代码就不贴上了,只贴波形处理代码:

#define FILTER_MAX 3 // 电平滤波次数 //AB电平状态表 const unsigned char TrueTabA[4] = {1,0,0,1} ; const unsigned char TrueTabB[4] = {1,1,0,0} ; typedef struct { unsigned char AVal; // A电平 unsigned char BVal; // B电平 unsigned char PreAVal; // 前次A电平 unsigned char PreBVal; // 前次B电平 unsigned char CmpNum; // 滤波次数 unsigned char Index; // 真值表中位置 (这是什么鬼?) unsigned char CWW ; // 方向 1-正向 0-反向 unsigned char Initbut; // 上电后初始化标志 signed short PNum; // 累计脉冲数 signed short PrePNum; // 前次累计脉冲数 GPIO_TypeDef port; // unsigned short pin; // }T_TubeButInfo; // 获取在真值表中的位置 static unsigned char GetIndex(unsigned char tbindex) //当AB不同电平的时候返回相应的值 { if(TubeButInfo[tbindex].AVal) { if(TubeButInfo[tbindex].BVal) return 0 ;//A高B高 return 3 ; //A高 B低 } else { if(TubeButInfo[tbindex].BVal) return 1 ;//A低B高 return 2 ; //A低 B低 } } // 计算脉冲个数 // asf : 1表示累加 0-累减 static void SetPNum(unsigned char tbindex, unsigned char asf) { //1表示正向转 0表示反向转 if((asf != 0) & (TubeButInfo[tbindex].PNum < 8000)) //防止溢出 { TubeButInfo[tbindex].PNum++; } else if((asf == 0) & (TubeButInfo[tbindex].PNum > -8000)) { TubeButInfo[tbindex].PNum--; } } void ProcessTubeButton(void) //按键数据处理 { unsigned int i ; unsigned char index ; for(i=0; i<TUBE_MAX; i++) { if( (TubeButInfo[i].PreAVal == TubeButInfo[i].AVal) && (TubeButInfo[i].PreBVal == TubeButInfo[i].BVal) ) //A B 电平都没有发生变化 { TubeButInfo[i].CmpNum = TubeButInfo[i].CmpNum + 1 ; if( TubeButInfo[i].CmpNum >= FILTER_MAX )//滤波,等稳定下来 { TubeButInfo[i].CmpNum = 0 ; index = GetIndex(i) ;// 返回值: A高B高0 A高B低3 A低B高1 A低B低2 即对应查表TrueTabA/B的状态 if( TubeButInfo[i].Initbut == 1) //开机刷新 { TubeButInfo[i].Index = index ; TubeButInfo[i].Initbut = 0 ; } else { if( TubeButInfo[i].Index == index) // 旋钮没动 { return; } if( TubeButInfo[i].CWW == 1) // 当前为正转状态 { if( TubeButInfo[i].Index == 3) //前状态A高B低 { if(index == 0) // 3到0 为正转 { SetPNum(i,1); } else // 3到其他(只能是3到2),为反转 { SetPNum(i,0) ; TubeButInfo[i].CWW = 0 ; } } else { if( (TubeButInfo[i].Index + 1) == index) // 为正转 { SetPNum(i,1); } else // 为反转 { SetPNum(i,0) ; TubeButInfo[i].CWW = 0; } } TubeButInfo[i].Index = index; } else // 当前为反转状态 { if( TubeButInfo[i].Index == 0 ) { if(index == 3) // 0到3 为反转 { SetPNum(i,0) ; } else // 0到其他 为正转 { SetPNum(i,1) ; TubeButInfo[i].CWW = 1 ; } } else { if( (TubeButInfo[i].Index - 1)== index) // 为反转 { SetPNum(i,0); } else // 正转 { SetPNum(i,1) ; TubeButInfo[i].CWW = 1 ; } } TubeButInfo[i].Index = index; } } } } else { TubeButInfo[i].PreAVal = TubeButInfo[i].AVal ; TubeButInfo[i].PreBVal = TubeButInfo[i].BVal ; TubeButInfo[i].CmpNum = 0 ; } } }

这段代码将变换的状态数量记录在TubeButInfo[i].PNum(一个步进是4个PNum),并将方向记录在TubeButInfo[i].CWW。

2.鉴相法

与触发器鉴相(点击查看相关文章)原理相同,只不过这里使用软件的方式鉴相。核心思想是:检测A相的边沿发生时B相的电平状态。比如上图A相的上升沿发生的时候,如果B相是低电平,则表示一个正向步进;如果B相是高电平,则表示一个反向步进。

上代码(注意下面这个代码的读取的波形是上面图的反相的,检A下降沿时B相电平): 代码里涉及到消抖滤波的部分如果看不懂请下方留言,可以单独写一下...

static void ReadPin(void) //读取引脚值,在定时器中断中调用 { u8 i = 0; for(i = 0;i<TUBE_MAX;i++) {TubeButInfo[i].AVal <<= 1;TubeButInfo[i].BVal <<= 1;} //左移一位 TubeButInfo[0].AVal |= GPIO_ReadInputDataBit(TB1to7A_GPIO_Port,TB1A_Pin); TubeButInfo[0].BVal |= GPIO_ReadInputDataBit(TBnB_GPIO_Port,TB1B_Pin); TubeButInfo[1].AVal |= GPIO_ReadInputDataBit(TB1to7A_GPIO_Port,TB2A_Pin); TubeButInfo[1].BVal |= GPIO_ReadInputDataBit(TBnB_GPIO_Port,TB2B_Pin); TubeButInfo[2].AVal |= GPIO_ReadInputDataBit(TB1to7A_GPIO_Port,TB3A_Pin); TubeButInfo[2].BVal |= GPIO_ReadInputDataBit(TBnB_GPIO_Port,TB3B_Pin); TubeButInfo[3].AVal |= GPIO_ReadInputDataBit(TB1to7A_GPIO_Port,TB4A_Pin); TubeButInfo[3].BVal |= GPIO_ReadInputDataBit(TBnB_GPIO_Port,TB4B_Pin); TubeButInfo[4].AVal |= GPIO_ReadInputDataBit(TB1to7A_GPIO_Port,TB5A_Pin); TubeButInfo[4].BVal |= GPIO_ReadInputDataBit(TBnB_GPIO_Port,TB5B_Pin); TubeButInfo[5].AVal |= GPIO_ReadInputDataBit(TB1to7A_GPIO_Port,TB6A_Pin); TubeButInfo[5].BVal |= GPIO_ReadInputDataBit(TBnB_GPIO_Port,TB6B_Pin); TubeButInfo[6].AVal |= GPIO_ReadInputDataBit(TB1to7A_GPIO_Port,TB7A_Pin); TubeButInfo[6].BVal |= GPIO_ReadInputDataBit(TBnB_GPIO_Port,TB7B_Pin); TubeButInfo[7].AVal |= GPIO_ReadInputDataBit(TB8to11A_GPIO_Port,TB8A_Pin); TubeButInfo[7].BVal |= GPIO_ReadInputDataBit(TBnB_GPIO_Port,TB8B_Pin); TubeButInfo[8].AVal |= GPIO_ReadInputDataBit(TB8to11A_GPIO_Port,TB9A_Pin); TubeButInfo[8].BVal |= GPIO_ReadInputDataBit(TBnB_GPIO_Port,TB9B_Pin); TubeButInfo[9].AVal |= GPIO_ReadInputDataBit(TB8to11A_GPIO_Port,TB10A_Pin); TubeButInfo[9].BVal |= GPIO_ReadInputDataBit(TBnB_GPIO_Port,TB10B_Pin); TubeButInfo[10].AVal |= GPIO_ReadInputDataBit(TB8to11A_GPIO_Port,TB11A_Pin); TubeButInfo[10].BVal |= GPIO_ReadInputDataBit(TBnB_GPIO_Port,TB11B_Pin); for(i=0;i<11;i++) { if((TubeButInfo[i].AVal & 0x0f) == 0x00) //A通道滤波检测: 为低电平,表示有旋动 { But_Sta[i] = 0x00; //低电平标志 } else { But_Sta[i] = 0xff; //高电平标志 } if((TubeButInfo[i].BVal & 0x0f) == 0x00) //B通道滤波检测,只记录B通道状态,全部为0表示低电平 { But_StaChB[i] = 0x00; } else { But_StaChB[i] = 0xff; } } } void But_Process() { u8 i = 0; if(ClearFlag != 0) { for(i = 0;i < TUBE_MAX;i++) { TubeButReg[i]=0; } TubeButReg[11] = 0; //按键不包含在TUBE_MAX中,需要单独处理 ClearFlag = 0; //清除完成 } for(i=0;i<11;i++) { if(But_Sta[i] != But_FormSta[i]) //But i 状态发生变化 { if(!But_Sta[i]) //旋钮发生变化,下降沿 { if(But_StaChB[i] != 0) //高电平,正向 { TubeButReg[i] += 1; TubeRegChangeFlag = 1; } else //低电平,反向 { TubeButReg[i] -= 1; TubeRegChangeFlag = 1; } } // else // { // //上升沿 // } But_FormSta[i] = But_Sta[i]; } } }

 

 

最新回复(0)