资讯详情

基于STM32F407 ADC双通道 PS2游戏机摇杆ADC采集

基于STM32F407 ADC双通道 PS2游戏机摇杆ADC采集

  • 一、PS2游戏机摇杆概述
    • 1.1PS2游戏机摇杆概述
    • 1.2PS2游戏机摇杆图
  • 二、硬件连接分析
  • 三、实现代码
    • 3.1 ADC代码
    • 3.1 定时器
    • 3.2 主函数
  • 四、实验效果分析

一、PS2游戏机摇杆概述

1.1PS2游戏机摇杆概述

摇杆一般广泛应用于无人机、电子游戏、遥控车、云台等设备,许多带屏幕的设备经常使用摇杆作为菜单选择的输入控制。

PS游戏双轴摇杆传感器模块采用原装优质金属PS22轴摇杆电位器制成(X,Y)模拟输出,1路(Z)按钮数字输出。

原理:摇杆为双向十字10K电阻器。使用5个模块V在原始状态下供电X、Y读出电压约为2.5V,当摇杆向某个方向推动时,相应的轴电压值增加或减小,大值5V,小值0V。

工作电压:5V 2.输出电压范围:0~5V 3.接口:两个模拟信号代表X、Y偏移量、数字信号SW代表Z轴是否按下

在这里插入图片描述

1.2PS2游戏机摇杆图

(1)SW:按下摇杆时触发标识符,引脚电平为高低电平变化,万用表测量时,没有电平变化,引脚电平不通过。我不知道我买的模块是否有问题,其实在编程过程中并不难,加个中断就行了。VRY:输出Y轴电压变化范围:0~3.3V 或 0~5V ;电压范围取决于 5V标识的连接方式 (3)VRX:输出Y轴电压变化范围:0~3.3V 或 0~5V;电压范围取决于 5V标识的连接方式 (4)GND:接电源地 (5) 5V:这是接电压,

X、Y如图所示

二、硬件连接分析

由于X、Y轴输出电压可变量,有两个ADC需要同时采集,所以会采用ADC设计双通道,通过搜索手册确定使用 因为需要快速响应采集速度,所以使用DMA通过搜索手册过搜索手册知道ADC1与DMA的关系为

三、代码实现

3.1 ADC代码

adc.c

#include "adc.h"  //用于存储PA2 PA3的ADC值 __IO uint16_t aADCDualConvertedValue[2];    /************************************* 硬件说明 PA2 -- ADC123_IN2 PA3 -- ADC123_IN3 选用ADC1 *************************************/  void ADC_PA2_PA3_Init(void) { 
           GPIO_InitTypeDef    GPIO_InitStructure;  ADC_CommonInitTypeDef  ADC_CommonInitStructure;  ADC_InitTypeDef        ADC_InitStructure;   RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);//使能GPIOA时钟  RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); //使能ADC1时钟       GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_2|GPIO_Pin_3;  //引脚2 3  GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AN;    ///模拟模式  GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_NOPULL ;  //浮空  GPIO_Init(GPIOA, &GPIO_InitStructure);   复位ADC寄存器  RCC_APB2PeriphResetCmd(RCC_APB2Periph_ADC1,ENABLE);	  	//reset adc
	RCC_APB2PeriphResetCmd(RCC_APB2Periph_ADC1,DISABLE);	//end reset adc


	ADC_CommonInitStructure.ADC_Mode 				= ADC_Mode_Independent;				//独立模式
	ADC_CommonInitStructure.ADC_TwoSamplingDelay 	= ADC_TwoSamplingDelay_20Cycles;
	ADC_CommonInitStructure.ADC_DMAAccessMode 		= ADC_DMAAccessMode_Disabled; 		//这是混合ADC才使用的模式,这里由于是独立模式,选择参数ADC_DMAAccessMode_Disabled
	ADC_CommonInitStructure.ADC_Prescaler 			= ADC_Prescaler_Div4;//预分频4分频。ADCCLK=PCLK2/4=84/4=21Mhz,ADC时钟最好不要超过36Mhz 
	ADC_CommonInit(&ADC_CommonInitStructure);//初始化

	ADC_InitStructure.ADC_Resolution 			= ADC_Resolution_12b;	//12位模式
	ADC_InitStructure.ADC_ScanConvMode 			= ENABLE;				//扫描模式 多通道采集需要
	ADC_InitStructure.ADC_ContinuousConvMode 	= ENABLE;				//连续转换
	ADC_InitStructure.ADC_ExternalTrigConvEdge 	= ADC_ExternalTrigConvEdge_None;//禁止触发检测,使用软件触发
// ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T3_CC1; //禁止硬件触发,些值可以不用填写
	ADC_InitStructure.ADC_DataAlign 			= ADC_DataAlign_Right;//右对齐 
	ADC_InitStructure.ADC_NbrOfConversion 		= 2;//2个转换在规则序列中

	ADC_Init(ADC1, &ADC_InitStructure);//ADC初始化

	// 配置 ADC 通道转换顺序和采样时间周期
	ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 1, ADC_SampleTime_15Cycles );	//ADC1,ADC通道,480个周期,提高采样时间可以提高精确度 
	ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 2, ADC_SampleTime_28Cycles );	//ADC1,ADC通道,480个周期,提高采样时间可以提高精确度 
	 

	// 使能 DMA 请求 after last transfer (Single-ADC mode)
	ADC_DMARequestAfterLastTransferCmd(ADC1, ENABLE);

	//使能 ADC DMA
	ADC_DMACmd(ADC1, ENABLE);

	//ADC_ExternalTrigConvCmd(ADC1, ENABLE); 
	ADC_Cmd(ADC1, ENABLE);//开启AD转换器 

}



void DMA_Config(void)
{ 
        
	DMA_InitTypeDef DMA_InitStructure;

	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);

	DMA_InitStructure.DMA_Channel 				= DMA_Channel_0;				//选择 DMA 通道,通道存在于流中0中
	DMA_InitStructure.DMA_PeripheralBaseAddr 	= (uint32_t)&(ADC1->DR);		//数据源在ADC1->DR中
	DMA_InitStructure.DMA_Memory0BaseAddr 		= (uint32_t)aADCDualConvertedValue;//数据目的地在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 传输通道优先级为高,当使用一个 DMA 通道时,优先级设置不影响
	DMA_InitStructure.DMA_FIFOMode 				= DMA_FIFOMode_Disable;            // 禁止 DMA FIFO ,使用直连模式
	// FIFO 大小, FIFO 模式禁止时,这个不用配置
	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 流
	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"

/********************************************* 功能说明: TIM3 -- APB1 -- 16位定时器(65535) TIM3时钟频率: APB1*2 = 42*2 = 84MHZ **********************************************/

void Tim3_Init(void)
{ 
        
	NVIC_InitTypeDef  NVIC_InitStruct;
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseInitStruct;
	
	// 1、使能定时器时钟。
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
	
	

	TIM_TimeBaseInitStruct.TIM_Prescaler		= 8400-1;   			//8400分频 84000 000HZ/84000 = 10000HZ
	TIM_TimeBaseInitStruct.TIM_Period			= 10000-1;   			//计10000个数,用时1s
	TIM_TimeBaseInitStruct.TIM_CounterMode		= TIM_CounterMode_Up; 	//向上计数
	TIM_TimeBaseInitStruct.TIM_ClockDivision	= TIM_CKD_DIV1;			//分频因子
	// 2、初始化定时器,配置ARR,PSC。
	TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStruct);	
	
	NVIC_InitStruct.NVIC_IRQChannel						= TIM3_IRQn;		//中断通道 ,在stm32f4xx.h
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority	= 2;				//抢占优先级
 	NVIC_InitStruct.NVIC_IRQChannelSubPriority			= 2;				//响应优先级
	NVIC_InitStruct.NVIC_IRQChannelCmd					= ENABLE;			//中断通道使能 
	// 3、启定时器中断,配置NVIC。
    NVIC_Init(&NVIC_InitStruct);
	
	// 4、设置 TIM3_DIER 允许更新中断
	TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);
	// 5、使能定时器。
	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"


//用于存储PA2 PA3的ADC值
extern __IO uint16_t aADCDualConvertedValue[2];
unsigned int adcVal,adcVal01,adcVal02;
char msgstr[64];

int main(void)
{ 
        
	//设置系统中断优先级分组2
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	
	Delay_Init();
	Usart1_Init(115200);
	ADC_PA2_PA3_Init();
	DMA_Config();	

	//1S产生一次中断,在中断获取ADC数据
	Tim3_Init();
	//开始 adc 转换,软件触发
	ADC_SoftwareStartConv(ADC1);
	while(1)
	{ 
        
		delay_s(1);//延时200ms

	};
	
	return 0;
}


// 6、编写中断服务函数。
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");
	
	}
	//清除中断线0标志位
    TIM_ClearITPendingBit(TIM3, TIM_IT_Update);
}

四、实验效果分析

将程序下载到开发板后,打开串口,通过摇杆,即可改变不同的电压 单个的PS游戏摇杆就写到这里了。两个摇杆的话,写成四通道即可。源码链接:https://download.csdn.net/download/wwwqqq2014/85309506?spm=1001.2014.3001.5503

标签: 300pa2pa传感器单数字控制电位器single

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台