STM32通用定时器-脉冲计数(编码器模式)及电机测速原理

一、知识点

1. 编码器
  两相编码器(正交编码器):两相编码器由 A 相和 B 相组成,相位差为 90 度。当旋转方向为顺时针时,A 相先变化,然后 B 相变化;当旋转方向为逆时针时,B 相先变化,然后 A 相变化。通过检测相位差变化,可以确定旋转的方向。STM32 单片机可以使用定时器的正交编码器模式(Encoder Mode)来实现两相编码器的接口。
在这里插入图片描述

2. 编码器接口
  编码器的两个输入引脚,就是每个定时器的CH1和CH2引脚,CH3和CH4不能接编码器。
  最终的实验现象,编码器有两个输出,一个是A相,一个是B相,然后接入到STM32,定时器的编码器接口,编码器的接口自动控制定时器时基单元中的CNT计数器,进行自增或自减。比如初始化之后,CNT初始值为0,然后编码器右转,CNT就++,右转产生一个脉冲,CNT就加一次,比如右转产生10个脉冲后,停下来,那么这个过程CNT就由0自增到10,停下来,编码器左转,CNT就–,左转产生一个脉冲,CNT就自减一次, 比如编码器再左转产生5个脉冲,那CNT就在原来10的基础上自减5,停下来。
  编码器接口,其实就相当于是一个带有方向控制的外部时钟,同时控制着CNT的计数时钟和计数方向,这样CNT的值就表示了编码器的位置。如果我们每隔一段时间取一次编码器的值,再把CNT清零,那么每次取出来的值就表示了编码器的速度。
  编码器测速实际上就是测频法测正交脉冲的频率,CNT计次,然后每隔一段时间取一次计次,这就是测频法的思路。编码器计次能根据旋转方向,不仅能自增计次还能自减计次,是一个带方向的测速。
每个高级定时器和通用定时器都拥有1个编码器接口。

二、框图

在这里插入图片描述
  
在这里插入图片描述
  

三、编码器计数操作

在这里插入图片描述
  解释:当Tl1先产生脉冲,Tl2后产生脉冲。(A相先变化,B相后变化)时电机正转,计数器开始计数CNT++。当产生毛刺时,CNT不计数。Tl2先产生脉冲,Tl1后产生脉冲。(B相先变化,A相后变化)时电机反转,计数器CNT- -。当产生毛刺时不计数。

  
在这里插入图片描述
  解释:在TL1和TL2上计数时:当Tl1FP1信号为上升沿,且相对信号(Tl2)为高电平状态,则为向下计数,即CNT- -。若Tl2FP2信号在上升沿,且相对信号(TL1)为高电平,则为向上计数,即CNT++。各类信号状态与计数关系请查看上表。

四、相关寄存器

1. 第①部分对应寄存器

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2. 第②部分对应寄存器

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

五、代码编写

1. 库函数编写

void Encoder_Init_TIM4(void)
{
	GPIO_InitTypeDef GPIO_InitStructure; //定义一个引脚初始化的结构体  
	TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;//定义一个定时器初始化的结构体
	TIM_ICInitTypeDef TIM_ICInitStructure; //定义一个定时器编码器模式初始化的结构体
	
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE); //使能TIM4时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //使能CPIOB时钟
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7;	//PB6、PB7
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入
	GPIO_Init(GPIOB, &GPIO_InitStructure);	//根据GPIO_InitStructure的参数初始化GPIOB0
	
	TIM_TimeBaseStructure.TIM_Period = 0xffff; //设定计数器自动重装值
	TIM_TimeBaseStructure.TIM_Prescaler = 0; // 预分频器 
	TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //选择时钟分频:不分频
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
	TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct的参数初始化定时器TIM4
	
	TIM_EncoderInterfaceConfig(TIM4, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);//使用编码器模式3:CH1、CH2同时计数,四分频
	TIM_ICStructInit(&TIM_ICInitStructure); //把TIM_ICInitStruct 中的每一个参数按缺省值填入
	TIM_ICInitStructure.TIM_ICFilter = 10;  //设置滤波器长度
	TIM_ICInit(TIM4, &TIM_ICInitStructure); //根TIM_ICInitStructure参数初始化定时器TIM4编码器模式
	
	TIM_Cmd(TIM4, ENABLE); //使能定时器4
}

int16_t Encoder_Get(void)
{
	int16_t Temp;
	Temp = TIM_GetCounter(TIM4); //获取编码器当前值
	TIM_SetCounter(TIM4, 0);  //将编码器计数器清0
	return Temp;
}

六、电机测速原理

1. 基本信息

  1. 需要知道电机的一些基本信息,如减速比、多少线的编码器(这些参数需要问电机的的商家,或者查看手册获得)。
  2. 知道在代码中使用的是编码器模式几。如编码器模式1、2、3。使用编码器模式1和2则为2倍频,使用编码器模式3则为4倍频。频数高的精度会更高。
    在这里插入图片描述

2. 计算电机一圈的脉冲数

假设电机减速比为30,500线的编码器,使用编码器模式3。
电机一圈脉冲数:30×500×4=60000

3. 计算电机的速度

   这是我们需要再使用一个定时器用于定时,在定时器中断中获取编码器的脉冲。如:我们将定时器2设置为10ms触发一次中断,则每过10ms我们获取一次脉冲,假设10ms测得脉冲数为600。则1ms的脉冲数为60(600/10),1s的脉冲数为60000(60*1000)。又因为电机一圈脉冲为60000,所以电机的速度为:60000/60000=1 圈/s 。代码如下所示:

float cnt=0;
float speed=0;
void Time2_Init(u16 arr,u16 psc)
{
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
	TIM_InternalClockConfig(TIM2);
	
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
	TIM_TimeBaseInitStructure.TIM_Period = arr; //电机PWM频率要和定时器采样频率一致
	TIM_TimeBaseInitStructure.TIM_Prescaler = psc;
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
	TIM_ClearFlag(TIM2, TIM_FLAG_Update);
	TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
	NVIC_Init(&NVIC_InitStructure);

	TIM_Cmd(TIM2, ENABLE);
}

void TIM2_IRQHandler(void)
{
	if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
	{     
		  cnt=Encoder_Get();
		  speed=cnt/10*1000/60000;
		  TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
	}
}
//------------------------mian.c------------

int main()
{
	Time2_Init(7199,99);  //定时时间为7200*100/72 = 10000us =10ms
	while(1){}
}
<think>好的,用户之前问过如何实现基于LCD显示的码制转换电路,现在他们的新问题是“利用keil”。我需要理解他们具体想了解什么。Keil是一个广泛使用的嵌入式开发工具,主要用于ARM等微控制器的编程。可能他们想了解如何使用Keil来开发之前提到的码制转换电路,或者更基础的使用方法。 首先,用户的身份可能是学生或者刚入门的工程师,正在学习嵌入式系统开发,需要指导如何使用Keil进行项目创建、编码、调试等。他们可能对Keil的基本操作不熟悉,需要从安装开始,或者更深入的调试技巧。 考虑到之前的回答是关于硬件设计和代码实现的,用户可能在完成硬件部分后,现在需要软件开发的工具指导。所以,我需要结构清晰地介绍Keil的主要功能,包括项目创建、编码、编译、调试,以及可能的问题排查。 需要检查用户的问题是否涉及特定功能,比如Keil中如何配置LCD驱动,或者如何与之前的码制转换代码结合。但当前问题比较笼统,可能需要先给出Keil的概述和基本使用步骤,再结合实际例子,比如如何在Keil中编写和调试之前提到的ASCII转十六进制的代码。 另外,用户可能遇到常见错误,比如头文件缺失、编译错误,或者调试时无法连接硬件,这些都需要在回答中提及解决方案。同时,要确保语言简洁,步骤明确,适合初学者跟随操作。 最后,可能需要提供扩展建议,比如结合Proteus进行仿真,或者使用版本控制,这样用户能进一步优化开发流程。确保回答结构清晰,分步骤讲解,帮助用户逐步掌握Keil的使用,从而应用到他们的项目中。</think>好的!以下是一个关于 **Keil MDK(Microcontroller Development Kit)** 的详细介绍,涵盖其核心功能、基本操作流程和典型应用场景,帮助您快速上手开发嵌入式系统。 --- ### **1. Keil MDK 是什么?** Keil MDK 是 ARM 微控制器开发的集成开发环境(IDE),支持从代码编写、编译、调试到烧录的全流程。 - **适用芯片**:STMicroelectronics(STM32)、NXP(LPC系列)、TI(Tiva)等基于 ARM Cortex-M 的微控制器。 - **核心组件**: - **µVision**:可视化开发界面 - **ARM Compiler**:C/C++ 编译器 - **Debugger**:支持硬件仿真(JTAG/SWD)和软件模拟 - **CMSIS**:ARM 官方提供的标准化硬件抽象层 --- ### **2. 基本开发流程** #### **(1) 新建工程** 1. 点击菜单栏 **Project → New µVision Project** 2. 选择保存路径并命名工程(如 `LCD_CodeConverter`) 3. 选择目标芯片型号(例如 STM32F103C8) #### **(2) 配置工程** 1. **添加启动文件**:从芯片厂商提供的库中导入 `startup_stm32f10x.s`(汇编启动代码) 2. **设置头文件路径**: - 右键工程 → **Options for Target → C/C++** → 在 `Include Paths` 中添加库文件目录(如 `Drivers/STM32F1xx_HAL_Driver/Inc`) 3. **选择调试工具**: - **Options for Target → Debug** → 选择 ST-Link、J-Link 或其他调试器 #### **(3) 编写代码** 1. 新建 `main.c` 文件,编写主程序: ```c #include "stm32f1xx.h" #include "lcd1602.h" // 假设已自定义LCD驱动 int main(void) { HAL_Init(); // 初始化HAL库 LCD_Init(); // 初始化LCD LCD_DisplayString("Code: 0x5A"); // 显示码制转换结果 while(1); } ``` #### **(4) 编译与下载** 1. 点击 **Build(F7)** 编译代码,检查输出窗口是否显示 `0 Error(s)` 2. 连接开发板,点击 **Load(F8)** 将程序烧录到芯片 #### **(5) 调试** 1. 点击 **Start/Stop Debug Session(Ctrl+F5)** 进入调试模式 2. 使用单步执行(F11)、断点(F9)、变量监视窗口分析程序行为 --- ### **3. 关键功能与技巧** #### **(1) 外设库的使用** - **标准外设库(SPL)**:ST 早期的寄存器级操作库 - **HAL/LL 库**:ST 官方推出的硬件抽象层库,简化外设配置(推荐新手使用) ```c // 使用HAL库配置GPIO GPIO_InitTypeDef GPIO_InitStruct = {0}; GPIO_InitStruct.Pin = GPIO_PIN_13; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); ``` #### **(2) 调试技巧** - **逻辑分析仪(Logic Analyzer)**: 在调试模式下,右键变量 → **Add to Watch Window**,可实时观察变量值变化。 - **内存查看**: 点击菜单栏 **View → Memory Window**,输入地址(如 `0x20000000`)查看RAM数据。 #### **(3) 常见问题解决** | 问题现象 | 可能原因 | 解决方案 | |---------------------------|----------------------------|------------------------------| | 编译时报错 `undefined symbol` | 头文件未包含或库未链接 | 检查头文件路径,添加 `.c` 文件到工程 | | 程序下载失败 | 调试器驱动未安装 | 安装 ST-Link/V2 驱动 | | LCD无显示 | 时序配置错误 | 使用示波器检查EN、RS信号波形 | --- ### **4. 结合码制转换的实例** 假设需要在 STM32 上实现 **二进制转BCD并在LCD显示**: 1. 在 Keil 中创建工程,添加 LCD 驱动代码和转换算法: ```c // 二进制转BCD函数 uint8_t bin2bcd(uint8_t binary) { return ((binary / 10) << 4) | (binary % 10); } ``` 2. 主函数中调用并显示: ```c uint8_t value = 25; // 二进制: 00011001 uint8_t bcd = bin2bcd(value); LCD_WriteData(0x30 + (bcd >> 4)); // 显示十位 '2' LCD_WriteData(0x30 + (bcd & 0x0F)); // 显示个位 '5' ``` --- ### **5. 扩展工具与资源** - **Pack Installer**:通过 **Pack Installer**(菜单栏)安装芯片支持包(如 STM32F1xx_DFP) - **CMSIS-RTOS**:用于实时操作系统开发(如 FreeRTOS 集成) - **第三方插件**: - **CooCox IDE**:轻量级替代方案 - **VS Code 扩展**:通过 `Keil Assistant` 插件在 VS Code 中编辑工程 --- ### **6. 推荐学习路径** 1. **入门**:完成 LED 闪烁、按键检测等基础实验 2. **进阶**:结合 ADC、PWM、UART 实现传感器数据采集与显示 3. **实战**:移植 FatFS 文件系统或 LVGL 图形库到 LCD 如果需要具体的工程模板(如 LCD1602 驱动代码),可以进一步说明需求!
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值