CH579 ADC collects analog joystick data

CH579 Introduction

Chip Overview:

CH579M is an ARM core 32-bit microcontroller integrated with BLE wireless communication. The chip integrates a low-power Bluetooth BLE communication module, Ethernet controller and transceiver, full-speed USB host and device controller and transceiver, segment LCD driver module, ADC, touch key detection module, RTC and other rich peripheral resources .

Chip block diagram:

Joystick principle

The dual-axis joystick sensor module is generally widely used in drones, video games, remote control cars and other equipment. Many devices with screens often use the joystick sensor as an input control for menu selection. The dual-axis joystick sensor module has 2-axis (X, Y) analog output and 1-way (Z) button digital output;

The dual-axis key joystick is mainly composed of two potentiometers and a key switch. The two potentiometers output the corresponding voltage values on the X and Y axes respectively with the twist angle of the joystick. Press the joystick in the direction of the Z axis to trigger Touch the button. Under the action of the matching mechanical structure, in the initial state of the rocker without external force twisting, the two potentiometers are in the middle of the range.

Generally, the joystick module has 5 pins, which are the standard voltage VCC and GND, the analog signal output pins in the X and Y directions, and the digital signal output pins in the Z direction. The principle of X and Y analog signal output is that there is a two-way cross 10K resistor inside the joystick module. The module uses 5V (or 3.3V) for power supply. In the original state (the joystick is in the middle and is not triggered), the X and Y analog pins The readout voltage is about 2.5V (or 1.7V). When the joystick is pushed in a certain direction, the output corresponding shaft voltage value will increase or decrease accordingly. The digital signal in the Z direction is used to confirm whether it is pressed (it can also be judged by analog quantity, and the pressed value is 0).

Example code

ADC.C

/********************************************** *******************************
CH579M supports 14 external signals, among them, it is necessary to ensure that the PA5 pin is configured as a floating input mode before calling during coarse adjustment data calibration
Pin No. ADC Channel
PA4 CH_EXTIN_0 = 0, // ADC external analog channel 0
PA5 CH_EXTIN_1, // ADC external analog channel 1
PA12 CH_EXTIN_2, // ADC external analog channel 2
PA13 CH_EXTIN_3, // ADC external analog channel 3
PA14 CH_EXTIN_4, // ADC external analog channel 4
PA15 CH_EXTIN_5, // ADC external analog channel 5
PA3 CH_EXTIN_6, // ADC external analog channel 6
PA2 CH_EXTIN_7, // ADC external analog channel 7
PA1 CH_EXTIN_8, // ADC external analog channel 8
PA0 CH_EXTIN_9, // ADC external analog channel 9
PA6 CH_EXTIN_10, // ADC external analog channel 10
PA7 CH_EXTIN_11, // ADC external analog channel 11
PA8 CH_EXTIN_12, // ADC external analog channel 12
PA9 CH_EXTIN_13, // ADC external analog channel 13
    
CH_INTE_VBAT, // ADC internal battery detection channel
CH_INTE_VTEMP, // ADC internal temperature sensor detection channel
    
CH579M supports 2 internal signals
***************************************************** ***************************/
#include "CH57x_common.h"
#include "adc.h"

#define ADC_CONV_NUM 20

UINT16 abcBuff[40];
signed short RoughCalib_Value=0; // ADC rough adjustment deviation value
float ADC_ConvertedValueLocal[3]; //Save the result of ADC conversion into floating point number
UINT16 ADC_ConvertedValue[3]={0,0,0}; //Save the last data collected by the ADC sampling channel (ADC_CONV_NUM acquisitions)
uint16_t Coordinate[2]={0}; //Save the changed voltage value and convert it to coordinate
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; //handle flag bit

//ADC initialization configuration + sampling once
void ADC_Init(void)
{
    /*
    Note: Data calibration includes coarse and fine adjustments:
    ADC_DataCalib_Rough() is a rough adjustment function. Before calling it, it is necessary to ensure that the PA5 pin is configured as a floating input mode. There is no external voltage signal. It is called after the ADC is initialized. Generally, it is called once unless the ADC mode is changed (gain change)
    ADC_DataCalib_Fine() is a fine-tuning function, which performs a fitting algorithm operation on the coarse-tuned data to obtain more accurate data
    */
    GPIOA_ModeCfg(GPIO_Pin_5, GPIO_ModeIN_Floating);
    ADC_ExtSingleChSampInit( SampleFreq_3_2, ADC_PGA_1_2 );
    RoughCalib_Value = ADC_DataCalib_Rough(); //Used to calculate the internal deviation of ADC and record it into the global variable RoughCalib_Value
    //PRINT("=%d \r\\
", RoughCalib_Value);
}


/**************************************************** *******************************
* Function Name : ADC_Get_AdcVal
External single-channel voltage value acquisition
***************************************************** *******************************/
UINT16 ADC_Get_AdcVal(UINT32 GPIO_Pin_x, ADC_SingleChannelTypeDef Channel_x)
{
    UINT32 sum = 0;
    UINT8i;
        
    GPIOA_ModeCfg(GPIO_Pin_x, GPIO_ModeIN_Floating); //Open ADC pin
// 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; // continuous sampling 20 times
        ADC_DataCalib_Fine( & abcBuff[i], ADC_PGA_1_2);
        sum + = abcBuff[i];
    }
    
// voltage = (float)((sum/ADC_CONV_NUM)/2048.0)*1.05; //PGA gain selection 0db
// voltage = (float)((sum/ADC_CONV_NUM)/4096.0 + 0.5)*1.05; //PGA gain selection 6db
// voltage = (float)((sum/ADC_CONV_NUM)/1024.0 - 1)*1.05; //PGA gain selection -6db
    return sum / (ADC_CONV_NUM);
}


/ / Get the voltage value of the joystick change
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\\
",ADC_ConvertedValue[0]);
// PRINT("Rocker_Y: %d\r\\
",ADC_ConvertedValue[1]);
// PRINT("Rocker_Z: %d\r\\
",ADC_ConvertedValue[2]);
}


/**
  * @brief Set the coordinates of the small black dot according to the ADC values read from the X-axis and Y-axis of the joystick button, the Move_time macro is defined as 10, and it is set every 10ms
  * @param None
  * @retval None
  */
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);//Set X-axis coordinates
        Set_X_Y(Y_CHANGE,1);//Set Y-axis coordinates
            
        Time=Move_time;
        
// PRINT("X_CHANGE: %f\r\\
",X_CHANGE);
// PRINT("Y_CHANGE: %f\r\\
",Y_CHANGE);
// PRINT("K_CHANGE: %f\r\\
",K_CHANGE);
    }
}


/**
  * @brief Set the value of X coordinate or Y coordinate according to the ADC value
  * @param xy_adc: The AD value of the X-axis or Y-axis of the joystick is enlarged by 10 times
  * bar:0: Indicates to update the X axis 1: Indicates to update the Y axis
  * @retval None
  */
void Set_X_Y(uint16_t xy_adc, uint8_t bar)
{
    uint16_t _move, X_Rseult, Y_Rseult;
    //The value collected when the X and Y axes are not triggered is 13-14
    if((xy_adc>17)||(xy_adc<10))//The rocker is shaken
    {
        if(bar == 0)
        {
            _move=Calculate_move(xy_adc,bar); //Calculate the change of coordinates
            X_Rseult = Coordinate[bar] + _move;
            if((X_Rseult>=W_MAX)||(X_Rseult<=W_MIN))
            {
                // Abnormal data, the calculated coordinate value is lower than 0 and the number is about 65535, which is much larger than W_MAX*5
                if(X_Rseult>W_MAX*5) Coordinate[bar]=W_MIN;
                else
                {
                    // Limit the range to avoid exceeding the maximum value
                    if(X_Rseult>=W_MAX) Coordinate[bar]=W_MAX;
                    //Limit the range to avoid going below the minimum
                    if(X_Rseult<=W_MIN) Coordinate[bar]=W_MIN;
                }
            }
            else Coordinate[bar]=X_Rseult;//Calculate the coordinate value according to the change amount
    
        }
        else
        {
            _move=Calculate_move(xy_adc,bar); //Calculate the change of coordinates
            Y_Rseult = Coordinate[bar] + _move;
            if((Y_Rseult>=L_MAX)||(Y_Rseult<=L_MIN))
            {
                // Abnormal data, the calculated coordinate value is lower than 0 and the number is about 65535, which is much larger than L_MAX*5
                if(Y_Rseult>L_MAX*5) Coordinate[bar]=L_MIN;
                else
                {
                    // Limit the range to avoid exceeding the maximum value
                    if(Y_Rseult>=L_MAX) Coordinate[bar]=L_MAX;
                    //Limit the range to avoid going below the minimum
                    if(Y_Rseult<=L_MIN) Coordinate[bar]=L_MIN;
                }
            }
            else Coordinate[bar]=Y_Rseult;//Calculate the coordinate value according to the change amount
            
        }
        
        //Save the current coordinate value
        LCD_X = Coordinate[0];
        LCD_Y = Coordinate[1];
        PRINT("(LCD_X, LCD_Y) == (=, =)\r\\
",LCD_X,LCD_Y);
    }
    else return; //The joystick is not shaken, no coordinates are set, return
}



/**
  * @brief Calculate the movement of the coordinates according to the ADC value
  * @param move_adc : The ADC value of the X-axis or Y-axis of the joystick
  * @retval move_unit: the amount of movement of the coordinates
  */
int16_t Calculate_move(uint16_t move_adc,uint8_t bar)
{
     int16_t move_unit;
    if(bar==0)//x axis
    {
        /* Due to the voltage consumption on the line, the ADC acquisition value of the joystick in the static state is about 2900; the maximum value is about 3900; the minimum value is about 1200;
        Change the displacement calculation program according to the actual situation
        */
        if(move_adc>15) {move_unit=(15-move_adc)*0.8*1.0;} //The joystick and the screen black dot move in the same direction; *0.5--used to adjust the moving speed of the black dot
        if(move_adc<12) {move_unit=(12-move_adc)*0.8*1.0;} //The joystick and the screen black dot move in the same direction; *0.5--used to adjust the moving speed of the black dot
        
    // move_unit=(20-move_adc)*0.8; //The joystick and the screen black dot move in the same direction; *0.5--used to adjust the moving speed of the black dot
    }
    if(bar==1)//Y axis
    {
        /* Due to the voltage consumption on the line, the ADC acquisition value of the joystick in the static state is about 2900; the maximum value is about 3900; the minimum value is about 1200;
        Change the displacement calculation program according to the actual situation
        */
        if(move_adc>15) {move_unit=(15-move_adc)*0.8*1.0;} //The joystick and the screen black dot move in the same direction; *0.5--used to adjust the moving speed of the black dot
        if(move_adc<12) {move_unit=(12-move_adc)*0.8*1.0;} //The joystick and the screen black dot move in the same direction; *0.5--used to adjust the moving speed of the black dot
// move_unit=(20-move_adc)*0.8; //The joystick and the screen black dot move in the same direction; *0.5--used to adjust the moving speed of the black dot
     move_unit=(move_adc-16)*0.8; //The joystick and the screen black point move in reverse; *0.5--used to adjust the moving speed of the black point
    }
     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;
// Joystick X-axis voltage detection pin PA4
#define Rocker_X GPIO_Pin_4
// Joystick Y-axis voltage detection pin PA6
#define Rocker_Y GPIO_Pin_6
//The joystick presses the voltage detection pin 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

Main function

This is a simple ADC acquisition, process the collected analog data, convert the analog variation into actual coordinate information and print it out through the serial port. The main function calls “Coordinate_control();” in the while loop.

Serial port test

The joystick coordinate information output by the serial port is as follows: