相机中有使用到旋转编码器,一个旋转编码器上面拥有上下左右Ok键再加上滚轮左滑右滑。现在我们通过模拟来实现旋转编码器输出的信号。
控制板怎么确定用户按下的是哪个键呢?其实是旋转编码器上的每一个按键输出的电压都不一样,一个范围的电压值就可以确定按下的是哪个按键。这样我们就可以通过DA转换来输出相应的电压值。选择DA芯片为AD5040,引脚定义如下:
此芯片是通过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模拟来实现这组信号,下面介绍这两种方式。
我们可以利用通道的翻转功能来实现相位差为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:
void wheel_OneLattice(ROTATE_T rotate)
{
TIM3_PWMShiftInit(rotate);
WHEEL_ENABLE(0);
delay_ms(5);
WHEEL_ENABLE(1);
TIM_Cmd(TIM3, DISABLE);
}
rotate代表左旋还是右旋。这里是发送5ms的脉冲数,可以计算这段时间内发送了多少脉冲,但一般的设计中,在信号接收那段通常是在如果某一段时间有多个脉冲就只算一个。
用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的逻辑分析仪去看输出的波形是不是与期望的一致。