【stm32】模拟旋转编码器AD按键和滚轮输出

        相机中有使用到旋转编码器,一个旋转编码器上面拥有上下左右Ok键再加上滚轮左滑右滑。现在我们通过模拟来实现旋转编码器输出的信号。

一 旋转编码器上的按键

       控制板怎么确定用户按下的是哪个键呢?其实是旋转编码器上的每一个按键输出的电压都不一样,一个范围的电压值就可以确定按下的是哪个按键。这样我们就可以通过DA转换来输出相应的电压值。选择DA芯片为AD5040,引脚定义如下:

 

                                                         【stm32】模拟旋转编码器AD按键和滚轮输出_第1张图片

    此芯片是通过3线spi来控制的,sync可以理解成CS,所以配置spi如下:

void SPI1_Init(void) 
{
	SPI_InitTypeDef  SPI_InitStructure;
	GPIO_InitTypeDef GPIO_InitStructure;
 
	//配置SPI2管脚
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_SPI1|RCC_APB2Periph_AFIO, ENABLE);
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 |GPIO_Pin_6| GPIO_Pin_7;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //复用推挽输出
	GPIO_Init(GPIOA, &GPIO_InitStructure);

	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	GPIO_SetBits(GPIOA, GPIO_Pin_4);
   
	SPI_InitStructure.SPI_Direction =SPI_Direction_1Line_Tx;				//SPI_Direction_2Lines_FullDuplex; 
	SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
	SPI_InitStructure.SPI_DataSize = SPI_DataSize_16b;
	SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;  							// 空闲状态为高电平
	SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; 							// 第二个边跳沿采样
	SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;	  							// 软件控制cs
	SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2;		// SPI_BaudRatePrescaler_16;	 // 16分频
	SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
	SPI_InitStructure.SPI_CRCPolynomial = 7;
	SPI_Init(SPI1, &SPI_InitStructure);

	//使能SPI1
	SPI_Cmd(SPI1, ENABLE);  
}

向AD5040写的程序如下:

void AD5040_ByWrite(int Data)
{
	 if(assert_direction(Data) == RESET){
	 	Usart_SendString(USART1, "direction input error!");
	 }

	 AD5040_SYNC(1);    // 下一次写数据时需要拉高  sync低有效
	 delay_ms(1);
	 AD5040_SYNC(0);
	 SPI1_SendData(Data);
}

    ad5040的14位用来放输入数据,所以最大值为16383,所以输出电压与对应值的公式为:

                                                               v = 输入值/16383 * 基准电压

但是测试的时候电压值总是期望电压值的一半,输入值乘以2就好了。并且注意的时输入值必须时偶数。

二 旋转编码器上的滚轮

    实际上滚轮就是旋转编码器,在网上介绍旋转编码器的很多,所以我们只要知道它的输出信号是什么就好了。旋转编码器的输出是一组相位差为90度的脉冲信号。我们可以利用定时器的PWM功能或者自己gpio模拟来实现这组信号,下面介绍这两种方式。

    1. 定时器的pwm功能

    我们可以利用通道的翻转功能来实现相位差为90度的脉冲信号,这里使用了定时器3的通道3和通道4:

void TIM3_PWMShiftInit(uint16_t direction)
{
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseInitStruct;
	GPIO_InitTypeDef  GPIO_InitStruct;
	TIM_OCInitTypeDef TIM_OCInitStruct;
 
	
	/**********************TIM3 GPIO配置*****************************/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
 
	GPIO_Init(GPIOB,&GPIO_InitStruct);
	
	/**********************初始化TimBase结构体*************************/
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
	
	TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;
	TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
	TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0;
	TIM_TimeBaseInitStruct.TIM_Period =  3;//3;  //一个周期翻转一次电平,Frequence = 36000000/PSC/(ARR+1);
	TIM_TimeBaseInitStruct.TIM_Prescaler = 7199;     
	
	TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStruct);
	
	/**********************初始化TIM3 OC结构体*************************/
	TIM_OCInitStruct.TIM_OCIdleState = TIM_OCIdleState_Set;
	TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_Toggle;
	TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_Low;//TIM_OCPolarity_High;
	TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable;
	TIM_OCInitStruct.TIM_Pulse = 0;   //第一个周期,CCR与CNT不会进行比较
 
 	if(direction == WHEEL_RIGHT)
		TIM_OC3Init(TIM3,&TIM_OCInitStruct);
	else
		TIM_OC4Init(TIM3,&TIM_OCInitStruct);
	
    TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_Low;//TIM_OCPolarity_High;   
    TIM_OCInitStruct.TIM_Pulse = 2;//2;   //移相度数 = 180*CCR/(ARR+1)	180*2/(3+1)
 
	if(direction == WHEEL_RIGHT)
		TIM_OC4Init(TIM3,&TIM_OCInitStruct);
	else
		TIM_OC3Init(TIM3,&TIM_OCInitStruct);

//	TIM_SelectOnePulseMode(TIM3,TIM_OPMode_Single);		  // 单脉冲
	TIM_OC3PreloadConfig(TIM3, TIM_OCPreload_Enable);    //使能预装载寄存器
	TIM_OC4PreloadConfig(TIM3, TIM_OCPreload_Enable);    //使能预装载寄存器
	TIM_Cmd(TIM3, ENABLE);
}

     这样我们就可以得到一组相位差为90度的脉冲信号了,但是信号是一直开启没办法控制,所以在硬件方面我们可以使用开关芯片,如TS5A23159DGSR,来控制需要发送的脉冲个数。WHEEL_ENABLE对应的引脚就是ST_WHEEL_IN:

【stm32】模拟旋转编码器AD按键和滚轮输出_第2张图片

void wheel_OneLattice(ROTATE_T rotate)
{
	TIM3_PWMShiftInit(rotate);
	WHEEL_ENABLE(0);
	delay_ms(5);
	WHEEL_ENABLE(1);
	TIM_Cmd(TIM3, DISABLE);	
}

rotate代表左旋还是右旋。这里是发送5ms的脉冲数,可以计算这段时间内发送了多少脉冲,但一般的设计中,在信号接收那段通常是在如果某一段时间有多个脉冲就只算一个。

2. gpio模拟

     用gpio模拟旋转编码器的信号,就是两个gpio引脚,到时间了反转就行:

void Send_SiglePulse(void)
{
	
        TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
		NVIC_InitTypeDef NVIC_InitStructure;
		GPIO_InitTypeDef  GPIO_InitStruct;

	//	direction = rotate;

        RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3 ,ENABLE);			//使能定时器3,APB1PeriphClock	        
        
		RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	
		GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
		GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1;
		GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
 
		GPIO_Init(GPIOB,&GPIO_InitStruct);
		GPIO_ResetBits(GPIOB, GPIO_Pin_0);
		GPIO_ResetBits(GPIOB, GPIO_Pin_1);

        NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; 			//TIM3 中断
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; 	//先占优先级 0 级
        NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; 			//从优先级1级
        NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; 			//IRQ 通道被使能
        NVIC_Init(&NVIC_InitStructure); 

        TIM_TimeBaseStructure.TIM_Period = 1000-1; 						//设置自动重装载寄存器周期的值 
        TIM_TimeBaseStructure.TIM_Prescaler = 72-1;						//设置时钟频率除数的预分频值
        TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; 		//设置时钟分割
        TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; 	//TIM 向上计数
        TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); 				//②初始化 TIM3
        TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE ); 						//③允许更新中断
        TIM_Cmd(TIM3, ENABLE); 											//⑤使能 TIM3                      
}

中断处理函数:

u8 t3_count = 0;
void TIM3_IRQHandler(void)
{
 	if (TIM_GetITStatus(TIM3, TIM_IT_Update)!=RESET)
	{
		TIM_ClearITPendingBit(TIM3,TIM_IT_Update);
		t3_count++;
		if(t3_count == 10)
		{
			if(direction == TURN_LEFT)
				GPIO_SetBits(GPIOB, GPIO_Pin_0);
			else
				GPIO_SetBits(GPIOB, GPIO_Pin_1);
		}
		if(t3_count == 15)
		{
			if(direction == TURN_LEFT)
				GPIO_SetBits(GPIOB, GPIO_Pin_1);
			else
				GPIO_SetBits(GPIOB, GPIO_Pin_0);
		}
		if(t3_count == 20)
		{
			if(direction == TURN_LEFT)
				GPIO_ResetBits(GPIOB, GPIO_Pin_0);
			else
				GPIO_ResetBits(GPIOB, GPIO_Pin_1);
		}
		if(t3_count == 25)
		{
			t3_count = 0;
			if(direction == TURN_LEFT)
				GPIO_ResetBits(GPIOB, GPIO_Pin_1);
			else
				GPIO_ResetBits(GPIOB, GPIO_Pin_0);
			TIM_Cmd(TIM3, DISABLE);
		}			
	}       

}

因为我们只需要发送一组脉冲就行了。没有示波器可以通过keil的逻辑分析仪去看输出的波形是不是与期望的一致。

你可能感兴趣的:(旋转编码器,模拟输出,AD按键)