摇杆一般在航模中的无人机、电玩、遥控车、云台等设备上应用广泛,很多带有屏幕的设备也经常使用摇杆作为菜单选择的输入控制。
本篇介绍双轴按键摇杆的使用。
产品说明
PS2游戏双轴摇杆传感器模块由采用原装优质金属PS2摇杆电位器制作,具有2轴(X,Y)模拟输出,1路(Z)按钮数字输出;方便配合Arduino传感器扩展板使用。
产品特性
双轴按键摇杆主要由两个电位器和一个按键开关组成,两个电位器随着摇杆扭转角度分别输出X、Y轴上对应的电压值,在Z轴方向上按下摇杆可触发轻触按键。
在配套机械结构的作用下,无外力扭动的摇杆初始状态下,两个电位器都处在量程的中间位置。
它就是两个电位器和按键的组合体。
使用方法
PS2游戏摇杆可以被视为一个按钮和两个电位计的组合。
实现的结构类似下面图中所示:
这个图对于理解PS2的按键原理有帮助,但是图的质量不是很好,网上找不到更好的图片了,对付看吧。
电位器的两端,1脚和3脚之间接上电源,本设计中相当于接上3.3V和GND。
摆动PS2游戏摇杆相当于上图中可动臂转动,随着接触刷改变接触位置,滑动变阻器(电位器)的引脚2处的输出电压即发生变化。
X,Y轴为模拟输入信号而Z轴是数字输入信号,因此,x和y端口连接到ADC引脚,而z端口连接到数字端口。
所以我们一共需要使用STM32的三个GPIO引脚,其中两个模拟信号输入引脚和一个数字信号输入引脚。
PS2游戏摇杆正常状态(不受力状态)检测电压常态时为1.65V附近,最大值3.3V,最小值0V,用STM32自带ADC模数转换模块的两个通道分别检测电压值的变化就可以知道摇杆指向的位置了。
由于STM32单片机的ADC是12位精度,AD值在[0, 4095]之间,理论上X、Y轴输出中间值2048,但由于电位器及结构差异,原点值会有偏差,有些应用中需要进行校准。
程序中最主要的部分是按键扫描函数,我们分别采集摇杆的X轴和Y轴模拟输入,通过测试我们选取模拟量ADC值中的0~100、1900~2150、4850~4095三个区间,作为按键的三个状态,当扭动摇杆采集回的数据小于100或者大于4850,就认为这个轴向上进行了按键触发。
使用变量VRx(VRy)保存按键按下状态,只有当模拟量再次进入小于100或者大于4850时才会再次触发一次按键返回。
由上可知,要获得按键状态的难点就是如何获得VRx、VRy引脚的ADC值。
STM32自带ADC功能,查看芯片手册,手册上引脚中带有如下标识的引脚即有ADC功能。
对照下面的图:
我们可知,PC0、PC1、PC2三个引脚可以选择ADC1、ADC2或ADC3任意一个,初始化选中的ADC,然后获取通道11的值,即可得到PC1引脚的模拟量值,获取通道12的值,即可得到PC2引脚的模拟量值。
我们下面实现的代码选用的是ADC1。
硬件连接
ADC功能实现步骤:
1. ADC功能初始化
void Adc_Init(void)
{
ADC_InitTypeDef ADC_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC |RCC_APB2Periph_ADC1 , ENABLE ); //使能ADC1通道时钟
RCC_ADCCLKConfig(RCC_PCLK2_Div6); //设置ADC分频因子6 72M/6=12,ADC最大时间不能超过14M
//PC1 PC2
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1|GPIO_Pin_2;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //模拟输入引脚
GPIO_Init(GPIOC, &GPIO_InitStructure);
ADC_DeInit(ADC1); //复位ADC1,将外设 ADC1 的全部寄存器重设为缺省值
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //ADC工作模式:ADC1和ADC2工作在独立模式
ADC_InitStructure.ADC_ScanConvMode = DISABLE; //模数转换工作在单通道模式
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; //模数转换工作在单次转换模式
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //转换由软件而不是外部触发启动
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //ADC数据右对齐
ADC_InitStructure.ADC_NbrOfChannel = 1; //顺序进行规则转换的ADC通道的数目
ADC_Init(ADC1, &ADC_InitStructure); //根据ADC_InitStruct中指定的参数初始化外设ADCx的寄存器
ADC_Cmd(ADC1, ENABLE); //使能指定的ADC1
ADC_ResetCalibration(ADC1); //使能复位校准
while(ADC_GetResetCalibrationStatus(ADC1)); //等待复位校准结束
ADC_StartCalibration(ADC1); //开启AD校准
while(ADC_GetCalibrationStatus(ADC1)); //等待校准结束
}
2. 获取ADC值函数
//获得ADC值
//ch:通道值 0~3
u16 Get_Adc(u8 ch)
{
//设置指定ADC的规则组通道,一个序列,采样时间
ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_239Cycles5 ); //ADC1,ADC通道,采样时间为239.5周期
ADC_SoftwareStartConvCmd(ADC1, ENABLE); //使能指定的ADC1的软件转换启动功能
while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ));//等待转换结束
return ADC_GetConversionValue(ADC1); //返回最近一次ADC1规则组的转换结果
}
u16 Get_Adc_Average(u8 ch,u8 times)
{
u32 temp_val=0;
u8 t;
for(t=0;t<times;t++)
{
temp_val+=Get_Adc(ch);
delay_ms(5);
}
return temp_val/times;
}
3. main函数,while循环中,循环获得VRx、VRy引脚的电压值,并打印输出
#include "stm32f10x.h"
#include "delay.h"
#include "usart.h"
#include "common.h"
#include "adc.h"
#include "key.h"
int main(void)
{
int times = 0;
u8 i = 0;
u8 key=0XFF;
u16 VRx,VRy;
u8 dtbuf[50];
//初始化
//延时函数初始化
delay_init();
uart_init(115200); //串口1:Debug,初始化为115200
Adc_Init();
KEY_Init();
printf("System Init OK ...\r\n");
while(1)
{
times++;
VRx = Get_Adc_Average(11,10); //PC1
VRy = Get_Adc_Average(12,10); //PC2
if((VRx >= 2100 || VRx <= 1900) || (VRy >= 2100 || VRy <= 1900))
{
sprintf(dtbuf, "VRx:%04d--VRy:%04d \r\n", VRx,VRy);
printf(dtbuf);
}
key = KEY_Scan(0);
if(key)
{
switch(key)
{
case KEY_SW_PRES:
{
printf("KEY_SW_PRES...\r\n");
}
break;
default:
break;
}
}
delay_ms(10);
}
}
结果展示
由上我们可以看出,上下左右改变摇杆的位置,XY轴的AD值是变化的,按下SW按键,也能正常检测出按下的状态,所以一个PS2按键传感器可以当做多个按键使用。