基于STM32F407 ADC双通道 PS2游戏机摇杆ADC采集
一、PS2游戏机摇杆概述
1.1PS2游戏机摇杆概述
摇杆一般在航模中的无人机、电玩、遥控车、云台等设备上应用广泛,很多带有屏幕的设备也经常使用摇杆作为菜单选择的输入控制。
PS2游戏双轴摇杆传感器模块由采用原装优质金属PS2摇杆电位器制作,具有2轴(X,Y)模拟输出,1路(Z)按钮数字输出。
原理:摇杆为一个双向十字的10K电阻器。模块使用5V供电,原始状态下X、Y读出电压约为2.5V,当摇杆往某个方向推动,输出的相应轴电压值增加或减小,大值5V,小值0V。
1、工作电压:5V 2、输出电压范围:0~5V 3、接口:两路模拟信号代表X、Y偏移量,一路数字信号SW代表Z轴是否按下
1.2PS2游戏机摇杆图解
(1)SW:标识符为摇杆按下时触发,引脚电平为高低电平变化,但是我通过万用表进行测量时,没有电平变化,且引脚电平是不通过的,不知道是不是买的这个模块有问题,在编程过程中其实也不难,加个中断即可。(2)VRY:输出Y轴电压变化范围:0~3.3V 或 0~5V ;电压范围取决于+5V标识的连接方式 (3)VRX:输出Y轴电压变化范围:0~3.3V 或 0~5V;电压范围取决于+5V标识的连接方式 (4)GND:接电源地即可 (5)+5V:这个是接电压的,虽然写着+5V,但是也可以接3.3V(如果接3.3V, X轴与Y轴的电压变化范围:0~3.3V),由于STM32F407 ADC的参考电压为3.3V,所以此引脚接3.3V
X、Y转电压增量变化如图
二、硬件连接分析
由于X、Y轴输出的是电压可变的量,且有两个ADC需要同时进行采集,故将采用ADC的双通道进行设计,通过查找手册,确定使用ADC1 通道2及通道3 由于需要进行快速的响应采集速度,故使用DMA的方式进行转换的方式,通过查找手册知道ADC1与DMA的关系为
三、代码实现
3.1 ADC代码
adc.c
#include "adc.h"
__IO uint16_t aADCDualConvertedValue[2];
void ADC_PA2_PA3_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
ADC_CommonInitTypeDef ADC_CommonInitStructure;
ADC_InitTypeDef ADC_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2|GPIO_Pin_3;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ;
GPIO_Init(GPIOA, &GPIO_InitStructure);
RCC_APB2PeriphResetCmd(RCC_APB2Periph_ADC1,ENABLE);
RCC_APB2PeriphResetCmd(RCC_APB2Periph_ADC1,DISABLE);
ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent;
ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_20Cycles;
ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;
ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div4;
ADC_CommonInit(&ADC_CommonInitStructure);
ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;
ADC_InitStructure.ADC_ScanConvMode = ENABLE;
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfConversion = 2;
ADC_Init(ADC1, &ADC_InitStructure);
ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 1, ADC_SampleTime_15Cycles );
ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 2, ADC_SampleTime_28Cycles );
ADC_DMARequestAfterLastTransferCmd(ADC1, ENABLE);
ADC_DMACmd(ADC1, ENABLE);
ADC_Cmd(ADC1, ENABLE);
}
void DMA_Config(void)
{
DMA_InitTypeDef DMA_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);
DMA_InitStructure.DMA_Channel = DMA_Channel_0;
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&(ADC1->DR);
DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)aADCDualConvertedValue;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
DMA_InitStructure.DMA_BufferSize = 2;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
DMA_InitStructure.DMA_MemoryDataSize = DMA_PeripheralDataSize_HalfWord;
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;
DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
DMA_Init(DMA2_Stream0, &DMA_InitStructure);
DMA_Cmd(DMA2_Stream0, ENABLE);
}
adc.h
#ifndef __ADC_H
#define __ADC_H
#include "stm32f4xx.h"
void ADC_PA2_PA3_Init(void);
void DMA_Config(void);
#endif
3.1 定时器
在这里,我选择每隔1S采集ADC的值,如果在项目中需要快速采集,则减少定时器中断即可 tim.c
#include "tim.h"
void Tim3_Init(void)
{
NVIC_InitTypeDef NVIC_InitStruct;
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
TIM_TimeBaseInitStruct.TIM_Prescaler = 8400-1;
TIM_TimeBaseInitStruct.TIM_Period = 10000-1;
TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStruct);
NVIC_InitStruct.NVIC_IRQChannel = TIM3_IRQn;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 2;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStruct);
TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);
TIM_Cmd(TIM3, ENABLE);
}
tim.h
#ifndef __TIM_H
#define __TIM_H
#include "stm32f4xx.h"
void Tim3_Init(void);
#endif
3.2 主函数
#include "stm32f4xx.h"
#include "adc.h"
#include "delay.h"
#include "usart.h"
#include "tim.h"
extern __IO uint16_t aADCDualConvertedValue[2];
unsigned int adcVal,adcVal01,adcVal02;
char msgstr[64];
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
Delay_Init();
Usart1_Init(115200);
ADC_PA2_PA3_Init();
DMA_Config();
Tim3_Init();
ADC_SoftwareStartConv(ADC1);
while(1)
{
delay_s(1);
};
return 0;
}
void TIM3_IRQHandler(void)
{
if(TIM_GetITStatus(TIM3, TIM_IT_Update) == SET)
{
printf("While Run...\r\n");
adcVal01= aADCDualConvertedValue[0];
adcVal02= aADCDualConvertedValue[1];
printf("X轴电压:%f, Y轴电压:%f\r\n", (adcVal01/4095.0)*3.3 , (adcVal02/4095.0)*3.3);
sprintf(msgstr,"PA2 ch2=%d PA3 ch3=%d\r\n",adcVal01,adcVal02);
printf(msgstr);
printf("\r\n");
}
TIM_ClearITPendingBit(TIM3, TIM_IT_Update);
}
四、实验效果分析
将程序下载到开发板后,打开串口,通过摇杆,即可改变不同的电压 单个的PS游戏摇杆就写到这里了。两个摇杆的话,写成四通道即可。源码链接:https://download.csdn.net/download/wwwqqq2014/85309506?spm=1001.2014.3001.5503
|