CH579 ADC采集模拟摇杆数据
CH579介绍
芯片概述:
CH579M是集成BLE无线通讯的ARM内核32位微控制器。片上集成有低功耗蓝牙BLE通讯模块、以太网控制器及收发器、全速USB主机和设备控制器及收发器、段式LCD驱动模块、ADC、触摸按键检测模块、RTC等丰富的外设资源。
芯片框图:
摇杆原理
双轴摇杆传感器模块一般在无人机、电玩、遥控车等设备上应用广泛,很多带有屏幕的设备也经常使用摇杆传感器作为菜单选择的输入控制。双轴摇杆传感器模块具有2轴(X,Y)模拟输出,1路(Z)按钮数字输出;
双轴按键摇杆主要由两个电位器和一个按键开关组成,两个电位器随着摇杆扭转角度分别输出X、Y轴上对应的电压值,在Z轴方向上按下摇杆可触发轻触按键。在配套机械结构的作用下,无外力扭动的摇杆初始状态下,两个电位器都处在量程的中间位置。
一般摇杆模块摇杆有5个引脚,分别是标准电压VCC和GND,X、Y方向模拟信号输出脚,Z方向数字信号输出脚。X、Y模拟信号输出原理是摇杆模块内部有一个双向十字的10K电阻器,模块使用5V(或3.3V)供电,在原始状态(摇杆处于正中间未触发)时,X、Y模拟脚读出电压约为2.5V(或1.7V),当摇杆往某个方向推动,输出的相应轴电压值将会相应地增加或减小。Z方向数字信号用于确认是否按下(用模拟量来判断也行,按下为0)。
示例代码
ADC.C
/****************************************************************************
CH579M支持14个外部信号,其中粗调数据校准时调用前需保证PA5引脚配置浮空输入模式
引脚号 ADC通道
PA4 CH_EXTIN_0 = 0, // ADC 外部模拟通道 0
PA5 CH_EXTIN_1, // ADC 外部模拟通道 1
PA12 CH_EXTIN_2, // ADC 外部模拟通道 2
PA13 CH_EXTIN_3, // ADC 外部模拟通道 3
PA14 CH_EXTIN_4, // ADC 外部模拟通道 4
PA15 CH_EXTIN_5, // ADC 外部模拟通道 5
PA3 CH_EXTIN_6, // ADC 外部模拟通道 6
PA2 CH_EXTIN_7, // ADC 外部模拟通道 7
PA1 CH_EXTIN_8, // ADC 外部模拟通道 8
PA0 CH_EXTIN_9, // ADC 外部模拟通道 9
PA6 CH_EXTIN_10, // ADC 外部模拟通道 10
PA7 CH_EXTIN_11, // ADC 外部模拟通道 11
PA8 CH_EXTIN_12, // ADC 外部模拟通道 12
PA9 CH_EXTIN_13, // ADC 外部模拟通道 13
CH_INTE_VBAT, // ADC 内部电池检测通道
CH_INTE_VTEMP, // ADC 内部温度传感器检测通道
CH579M支持2个内部信号
****************************************************************************/
#include "CH57x_common.h"
#include "adc.h"
#define ADC_CONV_NUM 20
UINT16 abcBuff[40];
signed short RoughCalib_Value=0; // ADC粗调偏差值
float ADC_ConvertedValueLocal[3]; //保存ADC转换成浮点数后的结果
UINT16 ADC_ConvertedValue[3]={0,0,0}; //保存ADC采样通道最后采集到的数据(ADC_CONV_NUM次采集)
uint16_t Coordinate[2]={0}; //保存改变的电压值转换成坐标
volatile uint32_t Time;
float X_CHANGE,Y_CHANGE,K_CHANGE;
uint16_t LCD_X = 5,LCD_Y = 5,Trigger_Flag;
extern uint32_t Handle_Flag; //处理标志位
//ADC初始化 配置+采样一次
void ADC_Init(void)
{
/*
注意:数据校准包括 粗调和细调:
ADC_DataCalib_Rough() 是粗调函数,调用前需保证PA5引脚配置浮空输入模式,外部没有电压信号,在ADC初始化之后调用,一般调用一次,除非更改了ADC模式(增益改变)
ADC_DataCalib_Fine() 是细调函数,对于粗调过的数据进行拟合算法运算,得到更精确数据
*/
GPIOA_ModeCfg(GPIO_Pin_5, GPIO_ModeIN_Floating);
ADC_ExtSingleChSampInit( SampleFreq_3_2, ADC_PGA_1_2 );
RoughCalib_Value = ADC_DataCalib_Rough(); //用于计算ADC内部偏差,记录到全局变量 RoughCalib_Value中
//PRINT("=%d \r\n", RoughCalib_Value);
}
/*******************************************************************************
* Function Name : ADC_Get_AdcVal
外部单通道电压值采集
*******************************************************************************/
UINT16 ADC_Get_AdcVal(UINT32 GPIO_Pin_x, ADC_SingleChannelTypeDef Channel_x)
{
UINT32 sum = 0;
UINT8 i;
GPIOA_ModeCfg(GPIO_Pin_x, GPIO_ModeIN_Floating); //开启ADC引脚
// ADC_ExtSingleChSampInit(SampleFreq_3_2, ADC_PGA_1_2);
ADC_ChannelCfg( Channel_x );
for(i=0; i<(ADC_CONV_NUM); i++)
{
abcBuff[i] = ADC_ExcutSingleConver() + RoughCalib_Value; // 连续采样20次
ADC_DataCalib_Fine( &abcBuff[i], ADC_PGA_1_2);
sum += abcBuff[i];
}
// voltage = (float)((sum/ADC_CONV_NUM)/2048.0)*1.05; //PGA增益选择0db
// voltage = (float)((sum/ADC_CONV_NUM)/4096.0+0.5)*1.05; //PGA增益选择6db
// voltage = (float)((sum/ADC_CONV_NUM)/1024.0 - 1)*1.05; //PGA增益选择-6db
return sum / (ADC_CONV_NUM);
}
//获取摇杆变化的电压值
void Get_Adcvalue()
{
ADC_ConvertedValue[0] = ADC_Get_AdcVal(Rocker_X,CH_EXTIN_0);
ADC_ConvertedValue[1] = ADC_Get_AdcVal(Rocker_Y,CH_EXTIN_10);
ADC_ConvertedValue[2] = ADC_Get_AdcVal(Rocker_Z,CH_EXTIN_11);
ADC_ConvertedValueLocal[0] =(float) (ADC_ConvertedValue[0]/1024.0-1);
ADC_ConvertedValueLocal[1] =(float) (ADC_ConvertedValue[1]/1024.0-1);
ADC_ConvertedValueLocal[2] =(float) (ADC_ConvertedValue[2]/1024.0-1);
// PRINT("Rocker_X:%d\r\n",ADC_ConvertedValue[0]);
// PRINT("Rocker_Y:%d\r\n",ADC_ConvertedValue[1]);
// PRINT("Rocker_Z:%d\r\n",ADC_ConvertedValue[2]);
}
/**
* @brief 根据读取的来自摇杆按键的X轴和Y轴的ADC值来设置小黑点的坐标,Move_time宏定义为10,每10ms设置一次
* @param 无
* @retval 无
*/
void Coordinate_control(void)
{
static uint16_t Button_on_off=0;
if(Time==0)
{
Get_Adcvalue();
X_CHANGE=(ADC_ConvertedValueLocal[0]*10);
Y_CHANGE=(ADC_ConvertedValueLocal[1]*10);
K_CHANGE=(ADC_ConvertedValueLocal[2]*10);
if(K_CHANGE<=5)
{
if(Button_on_off == 0)
{
Button_on_off = 1;
if(K_CHANGE<=5) {Trigger_Flag = 1;}
}
}
else Button_on_off=0;
Set_X_Y(X_CHANGE,0);//设置X轴坐标
Set_X_Y(Y_CHANGE,1);//设置Y轴坐标
Time=Move_time;
// PRINT("X_CHANGE:%f\r\n",X_CHANGE);
// PRINT("Y_CHANGE:%f\r\n",Y_CHANGE);
// PRINT("K_CHANGE:%f\r\n",K_CHANGE);
}
}
/**
* @brief 根据ADC值来设置X坐标或Y坐标的值
* @param xy_adc:摇杆X轴或Y轴的AD值放大10倍
* bar:0:表示更新X轴 1:表示更新Y轴
* @retval 无
*/
void Set_X_Y(uint16_t xy_adc,uint8_t bar)
{
uint16_t _move,X_Rseult,Y_Rseult;
//X,Y轴未触发时采集到的数值为13—14
if((xy_adc>17)||(xy_adc<10))//摇杆被摇动
{
if(bar == 0)
{
_move=Calculate_move(xy_adc,bar); //计算出坐标的改变量
X_Rseult = Coordinate[bar]+_move;
if((X_Rseult>=W_MAX)||(X_Rseult<=W_MIN))
{
//异常数据,计算后的坐标值低于0后数字约为65535,远大于W_MAX*5
if(X_Rseult>W_MAX*5) Coordinate[bar]=W_MIN;
else
{
//限制范围,避免超出最大值
if(X_Rseult>=W_MAX) Coordinate[bar]=W_MAX;
//限制范围,避免低于最小值
if(X_Rseult<=W_MIN) Coordinate[bar]=W_MIN;
}
}
else Coordinate[bar]=X_Rseult;//根据改变量计算出坐标值
}
else
{
_move=Calculate_move(xy_adc,bar); //计算出坐标的改变量
Y_Rseult = Coordinate[bar]+_move;
if((Y_Rseult>=L_MAX)||(Y_Rseult<=L_MIN))
{
//异常数据,计算后的坐标值低于0后数字约为65535,远大于L_MAX*5
if(Y_Rseult>L_MAX*5) Coordinate[bar]=L_MIN;
else
{
//限制范围,避免超出最大值
if(Y_Rseult>=L_MAX) Coordinate[bar]=L_MAX;
//限制范围,避免低于最小值
if(Y_Rseult<=L_MIN) Coordinate[bar]=L_MIN;
}
}
else Coordinate[bar]=Y_Rseult;//根据改变量计算出坐标值
}
//保存本次坐标值
LCD_X = Coordinate[0];
LCD_Y = Coordinate[1];
PRINT("(LCD_X,LCD_Y) == (%3d,%3d)\r\n",LCD_X,LCD_Y);
}
else return; //摇杆不被摇动 不设置坐标,返回
}
/**
* @brief 根据ADC值来计算出坐标的移动量
* @param move_adc :摇杆X轴或Y轴的ADC值
* @retval move_unit :坐标的移动量
*/
int16_t Calculate_move(uint16_t move_adc,uint8_t bar)
{
int16_t move_unit;
if(bar==0)//x轴
{
/*由于存在线路上消耗电压,静止状态下摇杆ADC采集值为2900左右;最大值为3900左右;最小值1200左右;
根据实际情况更改位移量计算程序
*/
if(move_adc>15) {move_unit=(15-move_adc)*0.8*1.0;} //摇杆和屏幕黑点移动同向;*0.5——用于调节黑点的移动速度
if(move_adc<12) {move_unit=(12-move_adc)*0.8*1.0;} //摇杆和屏幕黑点移动同向;*0.5——用于调节黑点的移动速度
// move_unit=(20-move_adc)*0.8; //摇杆和屏幕黑点移动同向;*0.5——用于调节黑点的移动速度
}
if(bar==1)//Y轴
{
/*由于存在线路上消耗电压,静止状态下摇杆ADC采集值为2900左右;最大值为3900左右;最小值1200左右;
根据实际情况更改位移量计算程序
*/
if(move_adc>15) {move_unit=(15-move_adc)*0.8*1.0;} //摇杆和屏幕黑点移动同向;*0.5——用于调节黑点的移动速度
if(move_adc<12) {move_unit=(12-move_adc)*0.8*1.0;} //摇杆和屏幕黑点移动同向;*0.5——用于调节黑点的移动速度
// move_unit=(20-move_adc)*0.8; //摇杆和屏幕黑点移动同向;*0.5——用于调节黑点的移动速度
move_unit=(move_adc-16)*0.8; //摇杆和屏幕黑点移动反向;*0.5——用于调节黑点的移动速度
}
return move_unit;
}
ADC.h
#ifndef ADC_H_
#define ADC_H_
#ifdef __cplusplus
extern "C" {
#endif
#include "CH57x_gpio.h"
#include "CH57x_adc.h"
#include "CH579SFR.h"
#include "core_cm0.h"
#define Move_time 20
#define L_MAX (480-10)
#define W_MAX (800-10)
#define L_MIN 1
#define W_MIN 1
typedef u8 ad_channel_t;
//摇杆X轴电压检测引脚PA4
#define Rocker_X GPIO_Pin_4
//摇杆Y轴电压检测引脚PA6
#define Rocker_Y GPIO_Pin_6
//摇杆按下电压检测引脚PA7
#define Rocker_Z GPIO_Pin_7
void ADC_Init(void);
UINT16 ADC_Get_AdcVal(UINT32 GPIO_Pin_x, ADC_SingleChannelTypeDef Channel_x);
void Get_Adcvalue(void);
void Coordinate_control(void);
void Set_X_Y(uint16_t xy_adc,uint8_t bar);
int16_t Calculate_move(uint16_t move_adc,uint8_t bar);
#endif
主函数
这就是一个简单的ADC采集,把采集到的模拟数据处理一下,将模拟变化量转化成实际坐标信息通过串口打印出来。主函数在while循环里面调用“Coordinate_control();”即可。
串口测试
串口输出摇杆坐标信息如下: