【应用】使用STM32单片机定时器的Encoder模式驱动数字旋转编码开关

 RZ-51 6合1扩展板原理图.pdf:https://download.csdn.net/download/ZLK1214/29016144icon-default.png?t=L892https://download.csdn.net/download/ZLK1214/29016144

/* 本程序测试时使用的是锐志电子六合一扩展板上的数字旋转编码开关 */
#include <stm32f10x.h>

int main(void)
{
	/* 打开外设时钟 */
	RCC->APB1ENR = RCC_APB1ENR_TIM2EN | RCC_APB1ENR_TIM3EN;
	RCC->APB2ENR = RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN | RCC_APB2ENR_IOPCEN;
	
	/* I/O端口配置 */
	GPIOA->CRL = 0x88000000; // PA6为通道1, PA7为通道2, 设为带电阻输入
	GPIOA->BSRR = GPIO_BSRR_BS6 | GPIO_BSRR_BS7; // PA6~7带上拉电阻输入
	GPIOA->CRH = 0x00080000; // PA12设为带电阻输入
	GPIOA->BSRR = GPIO_BSRR_BS12;
	GPIOB->CRH = 0x33333333; // 数码管段选
	GPIOC->ODR = 0x3000; // 熄灭数码管
	GPIOC->CRH = 0x00333333; // 数码管位选
	
	/* 数码管扫描中断配置 */
	// 每隔一定时间点亮一个数码管
	// 从低位向高位扫描
	TIM2->ARR = 29; // 0~29有30个数字, 定时时间30*0.1ms=3ms, 向上计数, 每当TIM2->CNT跳变到0时触发一次中断
	TIM2->PSC = 7199; // 对72MHz进行7200分频, 得到的频率是10kHz, 也就是0.1ms
	TIM2->EGR = TIM_EGR_UG; // 立即触发TIM2中断, 点亮数码管, 同时刷新寄存器
	TIM2->DIER = TIM_DIER_UIE; // 允许触发定时器溢出中断
	NVIC_EnableIRQ(TIM2_IRQn); // 允许执行中断服务函数, 该函数位于core_cm3.h, 是MDK自带的函数, 不是库函数
	TIM2->CR1 = TIM_CR1_CEN; // 开定时器
	
	/* 数字编码开关计数器配置 */
	// 将数字编码开关的1、3脚分别接到TIM3定时器的通道1~2输入端口PA6~7上
	TIM3->ARR = TIM_ARR_ARR; // 计数的最大值为65535
	TIM3->CCMR1 = TIM_CCMR1_CC1S_0; // CC1S=01(IC1映射到TI1)
	TIM3->CCMR1 |= TIM_CCMR1_CC2S_0; // CC2S=01(IC2映射到TI2)
	
	// 以下三种模式任选一种即可
	TIM3->SMCR = TIM_SMCR_SMS_0; // SMS=001, 对通道1计数
	//TIM3->SMCR = TIM_SMCR_SMS_1; // SMS=010, 对通道2计数
	//TIM3->SMCR = TIM_SMCR_SMS_1 | TIM_SMCR_SMS_0; // SMS=011, 同时对通道1,2计数(计数速度更快)
	
	TIM3->CCER = TIM_CCER_CC1P; // 反转TI1的电平, 顺时针旋转时使计数值增加而不是减少(默认是减少)
	// 也可以改为反转TI2的电平, 这个和上面SMS的选择无关
	
	TIM3->EGR = TIM_EGR_UG; // 刷新寄存器
	TIM3->CR1 = TIM_CR1_CEN; // 开始计数
	
	/* 开PA12外中断 */
	EXTI->IMR = EXTI_IMR_MR12;
	EXTI->FTSR = EXTI_FTSR_TR12; // 下降沿触发
	NVIC_EnableIRQ(EXTI15_10_IRQn);
	NVIC_SetPriority(EXTI15_10_IRQn, 1); // 该中断优先级比数码管扫描中断的优先级低, 不会影响数码管扫描
	
	while (1);
}

// 数码管动态扫描
void TIM2_IRQHandler(void)
{
	static uint8_t pos = 5;
	static uint32_t numbuf;
	const uint8_t seg8[] = {0xc0, 0xf9, 0xa4, 0xb0, 0x99, 0x92, 0x82, 0xf8, 0x80, 0x90};
	if (TIM2->SR & TIM_SR_UIF) // 定时器2溢出中断
	{
		TIM2->SR &= ~TIM_SR_UIF; // 清中断标志
		if (pos == 5)
			numbuf = TIM3->CNT; // 取定时器3的计数值
		else
			numbuf /= 10;
		
		GPIOC->ODR = (GPIOC->ODR & 0xf0ff) | 0x3000; // 关闭位选
		GPIOB->ODR = (GPIOB->ODR & 0xff) | (seg8[numbuf % 10] << 8); // 设置显示字符
		if (pos <= 3)
			GPIOC->ODR |= 1 << (pos + 8); // 数码管1~4的位选直连I/O端
		else
			GPIOC->ODR &= ~(1 << (pos + 8)); // 数码管5~6的位选是由PNP三极管驱动
		if (pos == 0)
			pos = 5;
		else
			pos--;
	}
}

// 编码开关按键按下中断
void EXTI15_10_IRQHandler(void)
{
	EXTI->PR |= EXTI_PR_PR12; // 清中断标志
	TIM3->EGR = TIM_EGR_UG; // 重置定时器3, 使计数值归0
}

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
好的,对于这个问题,我可以给你一些指导。 使用 STM32F405 的定时器可以更精确地读取磁编码器的位置,因为定时器可以提供更高的计数精度。在 STM32F405 中,每个定时器都有一个计数器,可以用来计数定时器的时钟周期数。你可以将定时器连接到磁编码器的输出信号上,当定时器的计数器达到一定的值时,就可以触发一个中断来读取磁编码器的位置。 下面是一个使用 STM32F405 标准库函数的示例代码,可以读取 TLB5012B 磁编码器的位置: ```c #include "stm32f4xx.h" // 定义 GPIO 接口和引脚号 #define ENCODER_PORT GPIOA #define ENCODER_PIN GPIO_Pin_0 // 定义定时器和中断号 #define TIMER TIM2 #define IRQ TIM2_IRQn // 定义计数器 static int32_t counter = 0; // 初始化 GPIO 接口和定时器 void encoder_init(void) { GPIO_InitTypeDef GPIO_InitStructure; TIM_TimeBaseInitTypeDef TIM_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; // 使能 GPIO 时钟 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); // 配置 GPIO 为输入模式 GPIO_InitStructure.GPIO_Pin = ENCODER_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; GPIO_Init(ENCODER_PORT, &GPIO_InitStructure); // 连接 GPIO 到定时器 GPIO_PinAFConfig(ENCODER_PORT, GPIO_PinSource0, GPIO_AF_TIM2); // 使能定时器时钟 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); // 配置定时器为计数模式 TIM_InitStructure.TIM_Period = 0xFFFF; TIM_InitStructure.TIM_Prescaler = 0; TIM_InitStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_InitStructure.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseInit(TIMER, &TIM_InitStructure); // 配置定时器输入捕获模式 TIM_ICInitTypeDef TIM_ICInitStructure; TIM_ICInitStructure.TIM_Channel = TIM_Channel_1; TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; TIM_ICInitStructure.TIM_ICFilter = 0; TIM_ICInit(TIMER, &TIM_ICInitStructure); // 使能定时器输入捕获中断 NVIC_InitStructure.NVIC_IRQChannel = IRQ; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); // 初始化计数器 counter = 0; // 启动定时器 TIM_Cmd(TIMER, ENABLE); } // 中断处理函数 void IRQHandler(void) { // 读取定时器计数器的值 uint16_t value = TIM_GetCapture1(TIMER); // 计数器加上脉冲数量 if (value >= 0x8000) { counter--; } else { counter++; } // 清除中断标志 TIM_ClearITPendingBit(TIMER, TIM_IT_CC1); } // 读取磁编码器的位置 int32_t encoder_get_position(void) { // 返回计数器的值 return counter; } ``` 这段代码使用STM32F405 的定时器 TIM2 和输入捕获模式来读取磁编码器的位置。你可以使用 `encoder_get_position()` 函数来获取磁编码器的位置。请注意,这段代码只是一个参考实现,实际的实现方法可能会因应用场景的不同而有所不同。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

巨大八爪鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值