基于凌鸥081ZYKFB开发板的编码器测转速算法学习

凌鸥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结果参考:

相关推荐
m0_3771081420 小时前
stm32-TIM
stm32·单片机·嵌入式硬件
潜创微科技20 小时前
潜创微科技|ITE 联阳官方授权代理商 & 方案商 高清高速接口一站式服务商
嵌入式硬件·音视频
东小东博客1 天前
STM32 WS2812 Proteus仿真 汉字显示 SPI控制
stm32·嵌入式硬件·proteus
眠りたいです1 天前
现代C++:C++14中的新语言特性和库特性
c语言·开发语言·c++
ytttr8731 天前
OPC UA 协议栈 C 语言实现
c语言·开发语言·mfc
song5011 天前
Ascend C 算子开发:从入门到上手
c语言·开发语言·图像处理·人工智能·分布式·flutter·交互
czhaii1 天前
STM32中的位带(bit-band)操作
单片机
小a杰.1 天前
Ascend C编程语言进阶:高性能算子开发技巧
android·c语言·开发语言
小a杰.2 天前
Ascend C算子开发实战 - 从零开始写算子
c语言·开发语言