

凌鸥081ZYKFB官方关于正交编码器的例程太过简单,main函数里仅用一个value变量读取编码器计数寄存器的值:
/*******************************************************************************
函数名称: int main(void)
功能描述: 主程序入口
输入参数: 无
输出参数: 无
返 回 值: 无
其它说明: 实验将P2.8和P1.3分别复用为TIM3_CH0和TIM3_CH1模式,其中TIM3_CH0对应编码器
模式的T0信号输入口,TIM3_CH1对应编码器模式的T1信号输入口。实验配置编码器工
作模式为编码器模式,计数周期为4096。
运行效果为: 将编码器的A B两个相线分别接入P2.8和P1.3口,转动编码器通过debug查看Value查
看编码器脉冲计数值。
修改日期 版本号 修改人 修改内容
-----------------------------------------------------------------------------
*******************************************************************************/
u32 Value = 0;
int main(void)
{
Hardware_init(); /* 硬件初始化 */
while (1)
{
Value = UTIMER_ECD1_CNT;//读取编码器计数值
}
}
测试用编码器介绍:

该编码器从开发板5V取电,A端连接P1.3,B端连接P2.8:
硬件初始化程序:
cpp
/*******************************************************************************
函数名称: void Hardware_init(void)
功能描述: 硬件部分初始化
输入参数: 无
输出参数: 无
返 回 值: 无
其它说明:
修改日期 版本号 修改人 修改内容
-----------------------------------------------------------------------------
2015/11/5 V1.0 Howlet Li 创建
*******************************************************************************/
void Hardware_init(void)
{
__disable_irq(); /* 关闭中断 中断总开关 */
SYS_WR_PROTECT = 0x7a83; /*使能系统寄存器写操作*/
FLASH_CFG |= 0x00080000; /* enable prefetch ,FLASH预取加速使能*/
GPIO_init(); /* GPIO初始化 */
UTimer_init(); /* UTimer初始化 */
SoftDelay(100); /* 等待硬件初始化完毕*/
NVIC_EnableIRQ(ENCODER1_IRQn); /*使能编码器1中断*/
NVIC_EnableIRQ(TIMER3_IRQn); /*使能TIME3中断X*/
SYS_WR_PROTECT = 0x0; /*关闭系统寄存器写操作*/
#if POWER_MODE
SYS_VolSelModuleEnableIRQ(MCU_POWER_5v0);/*MCU电源中断使能函数*/
#else
SYS_VolSelModuleEnableIRQ(MCU_POWER_3v3);/*MCU电源中断使能函数*/
#endif
__enable_irq(); /* 开启中断 */
}
/*******************************************************************************
函数名称: void GPIO_init(void)
功能描述: GPIO硬件初始化
输入参数: 无
输出参数: 无
返 回 值: 无
其它说明:
修改日期 版本号 修改人 修改内容
-----------------------------------------------------------------------------
2015/11/5 V1.0 Howlet Li 创建
*******************************************************************************/
void GPIO_init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_StructInit(&GPIO_InitStruct); //初始化结构体
//配置按键 start:P2.11 stop:P2.11
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN; //GPIO输入模式
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_11 | GPIO_Pin_12;
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIO2, &GPIO_InitStruct);
//配置LED1:P0.6 LED2:P0.7 LED3: P0.3
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT; //GPIO输出模式
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7 | GPIO_Pin_3;
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIO0, &GPIO_InitStruct);
/* 配置UTimer3 TIM3_CH0: P2.8 TIM3_CH1: P1.3*/
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_8;
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIO2, &GPIO_InitStruct);
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_3;
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIO1, &GPIO_InitStruct);
GPIO_PinAFConfig(GPIO1, GPIO_PinSource_3, AF8_TIMER23); //复用
GPIO_PinAFConfig(GPIO2, GPIO_PinSource_8, AF8_TIMER23); //复用
}
/*******************************************************************************
函数名称: void Clock_Init(void)
功能描述: 时钟配置
输入参数: 无
输出参数: 无
返 回 值: 无
其它说明:
修改日期 版本号 修改人 修改内容
-----------------------------------------------------------------------------
2023/10/23 V2.0 HuangMG 增加MCU电源检测功能
*******************************************************************************/
void Clock_Init(void)
{
SYS_WR_PROTECT = 0x7a83; /* 解除系统寄存器写保护 */
SYS_AFE_REG5 |= BIT15; /* BIT15:PLLPDN */
#if POWER_MODE
SYS_VolSelModule(MCU_POWER_5v0); /* MCU供电电压:1:5V,0;3.3V*/
#else
SYS_VolSelModule(MCU_POWER_3v3); /* MCU供电电压:1:5V,0;3.3V*/
#endif
SoftDelay(100); /* 等待PLL稳定*/
SYS_WR_PROTECT = 0x7a83; /* 解除系统寄存器写保护 */
SYS_CLK_CFG = 0x000001ff; /* BIT8:0: CLK_HS,1:PLL | BIT[7:0]CLK_DIV | 1ff对应96M时钟 */
SYS_WR_PROTECT = 0x0; /*关闭系统寄存器写操作*/
}
/*******************************************************************************
函数名称: void SystemInit(void)
功能描述: 硬件系统初始化,调用时钟初始化函数
输入参数: 无
输出参数: 无
返 回 值: 无
其它说明:
修改日期 版本号 修改人 修改内容
-----------------------------------------------------------------------------
2016/3/14 V1.0 Howlet Li 创建
*******************************************************************************/
void SystemInit(void)
{
Clock_Init(); /* 时钟初始化 */
}
/*******************************************************************************
函数名称: void UTimer_init(void)
功能描述: UTimer硬件初始化
输入参数: 无
输出参数: 无
返 回 值: 无
其它说明:
修改日期 版本号 修改人 修改内容
-----------------------------------------------------------------------------
2015/11/5 V1.0 Howlet Li 创建
*******************************************************************************/
void UTimer_init(void)
{
TIM_TimerInitTypeDef TIM_InitStruct;
TIM_TimerStrutInit(&TIM_InitStruct); //Timer结构体初始化
TIM_InitStruct.Timer_TH = 12000; /* 定时器1ms中断一次*/
TIM_InitStruct.Timer_Filter0 = 1; /*CH0滤波,滤波宽度为Timer_Filter0*8个时钟周期*/
TIM_InitStruct.Timer_Filter1 = 1;
TIM_InitStruct.Timer_ClockDiv = TIM_Clk_Div8; //12MHz
TIM_TimerInit(TIMER3, &TIM_InitStruct);
TIM_TimerCmd(TIMER3, ENABLE); /* Timer3 模块使能 */// time3不使能对编码器采集无影响;
UTIMER_ECD1_TH = 800; //计数门限,10圈溢出一次;
UTIMER_ECD1_CFG = (0x1 << 8); //编码器模式选择正交编码模式,T1/T2都计数,脉冲数是实际的4倍;
UTIMER_IE = (0x3 << 14);//ENC1_OF_IE=1 ENC1_UF_IE=1,上溢 下溢中断均使能;---ZDX
UTIMER_IE|=BIT9;//T3_ZC_IE Timer3 计数器过 0 中断使能,高电平有效
UTIMER_CFG |= (1 << 9); //启动编码器
}
中断程序:
/*******************************************************************************
函数名称: void TIMER3_IRQHandler(void)
功能描述: TIMER3中断处理函数
输入参数: 无
输出参数: 无
返 回 值: 无
其它说明:
修改日期 版本号 修改人 修改内容
-----------------------------------------------------------------------------
2020/8/5 V1.0 Howlet Li 创建
*******************************************************************************/
u16 time3_1ms_cnt=0;
u16 run_1ms_cnt=0;
u16 second_flag=0;
u8 Run_start_flag=0;//编码器开始转动标志
void UTIMER3_IRQHandler(void)
{
if (UTIMER_IF & BIT9)//1MS 过零一次
{
UTIMER_IF=BIT9;
time3_1ms_cnt++;
if(time3_1ms_cnt>=200)//200ms
{
second_flag++;
Invers_GPIO(GPIO0, GPIO_Pin_7); //LED2反转
time3_1ms_cnt=0;
}
if(Run_start_flag)
{
run_1ms_cnt++;
}
}
}
/*******************************************************************************
函数名称: void ENCODER0_IRQHandler(void)
功能描述: ENCODER1中断处理函数
输入参数: 无
输出参数: 无
返 回 值: 无
其它说明:
修改日期 版本号 修改人 修改内容
-----------------------------------------------------------------------------
2020/8/5 V1.0 Howlet Li 创建
*******************************************************************************/
int16_t ENCODE_cnt = 0;
void ENCODER1_IRQHandler(void)
{
if (UTIMER_IF & BIT14)//下溢出
{
UTIMER_IF=BIT14;
Invers_GPIO(GPIO0, GPIO_Pin_6); //LED1反转
ENCODE_cnt--;
}
if (UTIMER_IF & BIT15)//上溢出
{
UTIMER_IF=BIT15;
Invers_GPIO(GPIO0, GPIO_Pin_3); //LED3反转
ENCODE_cnt++;
}
}
MAIN函数程序:
u32 Value = 0;
int32_t ROUND_Value;//采集到的总脉冲数
int16_t RPM_value;//
int16_t RPM_value_ave;//转速单位:RPM
u8 zero_flag=0;//value初始是否为0;
int16_t RPM_Temp[6]={0};
int main(void)
{
u8 i=0;
Hardware_init(); /* 硬件初始化 */
GPIO_SetBits(GPIO0,GPIO_Pin_3);
GPIO_SetBits(GPIO0,GPIO_Pin_6);
UTIMER_IF=0x00ff;
Value = UTIMER_ECD1_CNT;
if(Value) zero_flag=1;
while (1)
{
Value = UTIMER_ECD1_CNT;//读取编码器计数值;;编码器转一圈实际发出20个脉冲
if(zero_flag) {Value=0;zero_flag=0;}
if(Value)
{
Run_start_flag=1;
}
if(ENCODE_cnt>=0)//上溢出,10圈溢出一次 表示正转
{
ROUND_Value=ENCODE_cnt*800+Value;
}
else
{
ROUND_Value=ENCODE_cnt*800-Value;
}
if(second_flag)//每200ms进入一次
{
second_flag=0;
RPM_Temp[i]=ROUND_Value;
i++;
if(i>=6)//1s
{
i=0;
RPM_value=RPM_Temp[5]-RPM_Temp[0];
RPM_value_ave=(RPM_value*60/80);//1分钟圈数 RPM
ENCODE_cnt=0;ROUND_Value=0;run_1ms_cnt=0;Run_start_flag=0;
}
}
}
}
计算编码器旋钮转速思路:该编码器旋钮旋转1圈可使A、B接口均发出20个脉冲(PWM)信号,UTimer模块配置为上下边沿均计数,即:旋钮每转1圈,UTIMER_ECD1_CNT增加80个数值;UTIMER_ECD1_TH=800,说明每旋转10圈,UTIMER则进入溢出中断一次,正转则进入上溢中断,反转则进入下溢中断,上溢一次ENCODE_cnt+1,下溢一次ENCODE_cnt-1。
TImer3定时器配置为12Mhz频率,门限值为12000,则每1ms进入一次过零中断。
程序运行时,使用变量Value实时读取UTIMER_ECD1_CNT,并通过:
ROUND_Value=ENCODE_cnt*800+Value;(正转)
ROUND_Value=ENCODE_cnt*800-Value; (反转)
计算出持续的脉冲数;
Timer3定时器每1ms进入一次过零中断,期间每200ms second_flag自加一次,每当second_flag=1时,主函数里将ROUND_Value值存入RPM_Temp[i]数组内,共存6次,存满第6个数据,即RPM_Temp[5] 时,将 RPM_Temp[5]-RPM_Temp[0] ,计算出1秒内ROUND_Value总增加数,该数字除以4,再除以20得到1秒内编码器转过的总圈数 ,乘以60秒表示1分钟转过的总圈数,单位:RPM。Debug结果参考:
