UGamepad-V2 Getting Started Learning Manual

sequence

Welcome to the world of LDSCITECH evaluation boards, a field full of passion, innovation and unknown possibilities! Evaluation boards are the bridge between you and the future of technology. They are the key to exploring new concepts, validating ideas, and building innovative products. On this exciting journey, LDSCITECH will explore the magical world of microcontroller, sensor and motor evaluation boards with you.

Evaluation boards are the gateway to digital innovation, giving you a platform to experiment and test, giving you a deep understanding of various technologies without sacrificing complexity. Whether you are a beginner or an experienced engineer, the evaluation board is your right assistant in the design process, helping you quickly verify concepts, accelerate product development, and ultimately turn your ideas into reality.

At LDSCITECH, we understand the critical role of evaluation boards and are committed to providing you with the most advanced, high-performance microcontrollers, advanced sensor technology, and innovative motor evaluation boards. Through these tools, you will be able to push the boundaries of technology, challenge innovation peaks, empower your projects, and lead industry changes.

We believe that everyone engaged in technological innovation deserves a platform for creativity to fly. LDSCITECH will work with you to provide all-round support to help you find your unique path in the world of evaluation boards.

Are you ready to take on new technology challenges? Let’s embark on this exciting evaluation board journey together!

Thank you very much for your understanding and support! Learning and sharing in the technical field is an evolving process, and everyone is constantly improving their skills. Well-intentioned feedback and suggestions are the impetus for progress and provide authors with opportunities for improvement.

If you find any inaccuracies or unclear points in the documentation or videos, or have any suggestions for improvements, we sincerely welcome your suggestions. Your comments will help improve the quality, making the information more accurate and understandable, so that the documents and videos better meet the needs of readers and viewers.

LDSCITECH has always adhered to the spirit of openness, tolerance and learning. We look forward to making progress together with you and jointly creating better technical resources. Thank you again for your attention and support!

Example describe
Eg1_Joystick Implement a Joystick device
Eg2_WS2812B Light up the WS2812B lamp beads and achieve colorful gradients
Eg3_MultiTimer Port MultiTimer software timer module
Eg4_Mouse Implement simulated mouse function
Eg5_KeyBoard Implement simulated keyboard function
Eg6_DoubleJoystick Implement a USB dual rocker
Eg7_CompositeGMK Realize the combination of Joystick, MOUSE and Keyboard
Eg8_Gamepad Implement the function of Gamepad
Eg9_AbsoluteMouse Implement the function of absolute value mouse
Eg10_Xinput Implement Xbox controller function, Xinput (factory default)
Eg11_Xinput01 External joystick potentiometer realizes Xbox controller function
Eg12_MultiAxisButton Implement 8-axis 32-key joystick

Part One, Hardware Overview

1.1 3D diagram

Insert image description here

As shown in Figure 1.1, the Gamepad evaluation board is configured with eight 6*6 touch buttons, a joystick (Joystick), equipped with a WS2812B lamp bead, and connects the UART1 serial port, programming interface (SWD), and external Joystick interface, Type- C interface lead;

1.2 UGamepad-V2 schematic diagram

The schematic diagram of UGamepad-V2 is shown in Figure 1.2. If you can’t see clearly, you can open the PDF document in the Doc directory to view it.
Insert image description here

Part 2, Software Tools

2.1 Software Overview

In the /Software directory are commonly used tool software:

  1. Dt2_4: Tool for configuring USB device Report descriptor;

  2. USBHID debugging assistant/Yaya USB: USB debugging tool, equivalent to the serial port debugging assistant function;

  3. BUSHound: bus debugging tool;

  4. USBlyzer: a professional USB protocol analysis software

  5. MounRiver: compiler;

  6. Online testing tools:

    https://devicetests.com/

    https://key-test.com/cn/

    https://www.sqlsec.com/tools/mouse.html

    https://www.onlinemictest.com/zh/mouse-test/

2.2 Getting started with MounRiver software

MounRiver Studio is developed based on the Eclipse GNU version. While retaining the powerful code editing functions and convenient component framework of the original platform, it has made a series of interface, function, operation modifications and optimizations for embedded C/C++ development, as well as tool chain improvements. Add instructions and customize work. Strive to create an embedded integrated development environment based on RISC-V core that hardware engineers love. Please visit the following link to download: http://mounriver.com/help

Part Three, Practical Training

3.1 Example Eg01_ButtonDebug

As the first example, this section has two purposes: one is to test the usability of the buttons; the other is to introduce the component MultiButton; to show everyone the use of open source software. For our application engineers, the use of open source suites is often Can achieve twice the result with half the effort;

3.1.1Hardware design

Insert image description here

As shown in the picture above, there are 11 6x6 independent buttons;

The picture below is a five-way switch that supports up, down, left, right, and center directions;

Insert image description here

There are also 2 buttons, which are the middle buttons on the rocker potentiometer and are effective at high level.

Insert image description here

Therefore, we only need to configure 18 GPIOs as inputs to detect key signals;

3.1.2 Software design

3.1.2.1 Project tree

The first is the project tree. When we open the project, we can see the Gamepad directory under Project Explorer as shown below

Insert image description here

in

  • Binaries: Binaries;
  • Includes: Included header files;
  • __Core:__kernel file, stores core_riscv kernel file;
  • Debug: Stores files related to serial port printing and delay functions
  • **Ld:** Link file. Link files usually refer to various source code files and library files used to generate executable programs.
  • **Midleware:** Used to store open source software; such as MultiButton, MultiTimer
  • myBSP: Self-written peripheral driver file;
  • obj: Compiled and generated obj file;
  • Peripheral: This is the peripheral related driver provided by the MCU manufacturer;
  • Startup: startup file of ch32v203;
  • __User: __ch32v203 system configuration file, interrupt related files, main function, etc.;

The project directory will only be introduced once, and the subsequent sample directories will be similar.

3.1.2.2 System clock

We first open the startup_ch32v10x.S startup file, we see the following code

  jal  SystemInit
	la t0, main

Locate SystemInit

void SystemInit (void)
{
    
    
  RCC->CTLR |= (uint32_t)0x00000001;
  RCC->CFGR0 &= (uint32_t)0xF8FF0000;
  RCC->CTLR &= (uint32_t)0xFEF6FFFF;
  RCC->CTLR &= (uint32_t)0xFFFBFFFF;
  RCC->CFGR0 &= (uint32_t)0xFF80FFFF;
  RCC->INTR = 0x009F0000;    
  SetSysClock();
}

Regarding the configuration of the RCC register, please consult the user manual; we then open the SetSysClock function

static void SetSysClock(void)
{
#ifdef SYSCLK_FREQ_HSE
    SetSysClockToHSE();
#elif defined SYSCLK_FREQ_48MHz_HSE
    SetSysClockTo48_HSE();
#elif defined SYSCLK_FREQ_56MHz_HSE
    SetSysClockTo56_HSE();
#elif defined SYSCLK_FREQ_72MHz_HSE
    SetSysClockTo72_HSE();
#elif defined SYSCLK_FREQ_96MHz_HSE
    SetSysClockTo96_HSE();
#elif defined SYSCLK_FREQ_120MHz_HSE
    SetSysClockTo120_HSE();
#elif defined SYSCLK_FREQ_144MHz_HSE
    SetSysClockTo144_HSE();
#elif defined SYSCLK_FREQ_48MHz_HSI
    SetSysClockTo48_HSI();
#elif defined SYSCLK_FREQ_56MHz_HSI
    SetSysClockTo56_HSI();
#elif defined SYSCLK_FREQ_72MHz_HSI
    SetSysClockTo72_HSI();
#elif defined SYSCLK_FREQ_96MHz_HSI
    SetSysClockTo96_HSI();
#elif defined SYSCLK_FREQ_120MHz_HSI
    SetSysClockTo120_HSI();
#elif defined SYSCLK_FREQ_144MHz_HSI
    SetSysClockTo144_HSI();

#endif
 
 /* If none of the define above is enabled, the HSI is used as System clock
  * source (default after reset) 
	*/ 
}

Here we use the 96Mhz internal high-speed clock HSI: SYSCLK_FREQ_96MHz_HSI, because the USB peripheral requires 48Mhz, so for the convenience of frequency division, you need to choose an integer multiple of 48;

3.1.2.3 User code
3.1.2.3.1 Button part

The Button module is mainly about independent button scanning. The MultiButton event callback mechanism is used here. Regarding the use of MultiButton, you can find it on GitHub:

https://github.com/0x1abin/MultiButton

It also comes with usage methods, which I won’t go into details here.

#include "Button.h"


struct Button btn1, btn2, btn3, btn4, btn5, btn6, btn7, btn8, btn9, btn10,
        btn11, btn12, btn13,btn14,btn15,btn16,btn17,btn18;

u8 read_button_GPIO(u8 button_id) {
    
    
    // you can share the GPIO read function with multiple Buttons
    switch (button_id) {
    
    
    case btn1_id:
        return GPIO_ReadInputDataBit(LSW_PORT, LSW_PIN);
    case btn2_id:
        return GPIO_ReadInputDataBit(RSW_PORT, RSW_PIN);
    case btn3_id:
        return GPIO_ReadInputDataBit(LB_PORT, LB_PIN);
    case btn4_id:
        return GPIO_ReadInputDataBit(LT_PORT, LT_PIN);
    case btn5_id:
        return GPIO_ReadInputDataBit(RB_PORT, RB_PIN);
    case btn6_id:
        return GPIO_ReadInputDataBit(RT_PORT, RT_PIN);
    case btn7_id:
        return GPIO_ReadInputDataBit(BACK_PORT, BACK_PIN);
    case btn8_id:
        return GPIO_ReadInputDataBit(HOME_PORT, HOME_PIN);
    case btn9_id:
        return GPIO_ReadInputDataBit(START_PORT, START_PIN);
    case btn10_id:
        return GPIO_ReadInputDataBit(A_BT_PORT, A_BT_PIN);
    case btn11_id:
        return GPIO_ReadInputDataBit(B_BT_PORT, B_BT_PIN);
    case btn12_id:
        return GPIO_ReadInputDataBit(X_BT_PORT, X_BT_PIN);
    case btn13_id:
        return GPIO_ReadInputDataBit(Y_BT_PORT, Y_BT_PIN);
    case btn14_id:
        return GPIO_ReadInputDataBit(GTA_PORT, GTA_PIN);
    case btn15_id:
        return GPIO_ReadInputDataBit(GTB_PORT, GTB_PIN);
    case btn16_id:
        return GPIO_ReadInputDataBit(GTC_PORT, GTC_PIN);
    case btn17_id:
        return GPIO_ReadInputDataBit(GTD_PORT, GTD_PIN);
    case btn18_id:
        return GPIO_ReadInputDataBit(GTE_PORT, GTE_PIN);
    default:
        return 0;
    }
}

void LSW_SINGLE_Click_Handler(void* btn) {
    
    
    printf("LSW_SINGLE_Click_Handler\r\n");
}

void RSW_SINGLE_Click_Handler(void* btn) {
    
    
    printf("RSW_SINGLE_Click_Handler\r\n");

}
void LB_SINGLE_Click_Handler(void* btn) {
    
    
    printf("LB_SINGLE_Click_Handler\r\n");
}

void LT_SINGLE_Click_Handler(void* btn) {
    
    
    printf("LT_SINGLE_Click_Handler\r\n");

}

void RB_SINGLE_Click_Handler(void* btn) {
    
    
    printf("RB_SINGLE_Click_Handler\r\n");
}

void RT_SINGLE_Click_Handler(void* btn) {
    
    
    printf("RT_SINGLE_Click_Handler\r\n");

}
void BACK_SINGLE_Click_Handler(void* btn) {
    
    
    printf("BACK_SINGLE_Click_Handler\r\n");
}

void HOME_SINGLE_Click_Handler(void* btn) {
    
    
    printf("HOME_SINGLE_Click_Handler\r\n");
}
void START_SINGLE_Click_Handler(void* btn) {
    
    
    printf("START_SINGLE_Click_Handler\r\n");
}
void A_SINGLE_Click_Handler(void* btn) {
    
    
    printf("A_SINGLE_Click_Handler\r\n");
}
void B_SINGLE_Click_Handler(void* btn) {
    
    
    printf("B_SINGLE_Click_Handler\r\n");
}
void X_SINGLE_Click_Handler(void* btn) {
    
    
    printf("X_SINGLE_Click_Handler\r\n");
}
void Y_SINGLE_Click_Handler(void* btn) {
    
    
    printf("Y_SINGLE_Click_Handler\r\n");
}
void GTA_SINGLE_Click_Handler(void* btn) {
    
    
    printf("GTA_SINGLE_Click_Handler\r\n");
}
void GTB_SINGLE_Click_Handler(void* btn) {
    
    
    printf("GTB_SINGLE_Click_Handler\r\n");
}
void GTC_SINGLE_Click_Handler(void* btn) {
    
    
    printf("GTC_SINGLE_Click_Handler\r\n");
}
void GTD_SINGLE_Click_Handler(void* btn) {
    
    
    printf("GTD_SINGLE_Click_Handler\r\n");
}
void GTE_SINGLE_Click_Handler(void* btn) {
    
    
    printf("GTE_SINGLE_Click_Handler\r\n");
}

void ButtonInit(void) {
    
    
    GPIO_InitTypeDef GPIO_InitStructure;
    RCC_APB2PeriphClockCmd(
            RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOD |
            RCC_APB2Periph_AFIO, ENABLE); // 使能GPIOA时钟
    GPIO_PinRemapConfig(GPIO_Remap_PD01, ENABLE);
    // 配置GPIOA的Pin 0/3为输入下拉模式(IPD)
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_3;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    // 配置GPIOA的Pin 6/7/8/15为输入下拉模式(IPU)
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7 | GPIO_Pin_8
            | GPIO_Pin_15;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    // 配置GPIOB的Pin 6/7/8/15为输入下拉模式(IPU)
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2
            | GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_13
            | GPIO_Pin_14 | GPIO_Pin_15;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
    GPIO_Init(GPIOB, &GPIO_InitStructure);

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure);

    // 配置GPIOB的Pin 6/7/8/15为输入下拉模式(IPU)
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
    GPIO_Init(GPIOD, &GPIO_InitStructure);

    button_init(&btn1, read_button_GPIO, 1, btn1_id);
    button_init(&btn2, read_button_GPIO, 1, btn2_id);
    button_init(&btn3, read_button_GPIO, 0, btn3_id);
    button_init(&btn4, read_button_GPIO, 0, btn4_id);
    button_init(&btn5, read_button_GPIO, 0, btn5_id);
    button_init(&btn6, read_button_GPIO, 0, btn6_id);
    button_init(&btn7, read_button_GPIO, 0, btn7_id);
    button_init(&btn8, read_button_GPIO, 0, btn8_id);
    button_init(&btn9, read_button_GPIO, 0, btn9_id);
    button_init(&btn10, read_button_GPIO, 0, btn10_id);
    button_init(&btn11, read_button_GPIO, 0, btn11_id);
    button_init(&btn12, read_button_GPIO, 0, btn12_id);
    button_init(&btn13, read_button_GPIO, 0, btn13_id);
    button_init(&btn14, read_button_GPIO, 0, btn14_id);
    button_init(&btn15, read_button_GPIO, 0, btn15_id);
    button_init(&btn16, read_button_GPIO, 0, btn16_id);
    button_init(&btn17, read_button_GPIO, 0, btn17_id);
    button_init(&btn18, read_button_GPIO, 0, btn18_id);

    button_attach(&btn1, SINGLE_CLICK, LSW_SINGLE_Click_Handler);
    button_attach(&btn2, SINGLE_CLICK, RSW_SINGLE_Click_Handler);
    button_attach(&btn3, SINGLE_CLICK, LB_SINGLE_Click_Handler);
    button_attach(&btn4, SINGLE_CLICK, LT_SINGLE_Click_Handler);
    button_attach(&btn5, SINGLE_CLICK, RB_SINGLE_Click_Handler);
    button_attach(&btn6, SINGLE_CLICK, RT_SINGLE_Click_Handler);
    button_attach(&btn7, SINGLE_CLICK, BACK_SINGLE_Click_Handler);
    button_attach(&btn8, SINGLE_CLICK, HOME_SINGLE_Click_Handler);
    button_attach(&btn9, SINGLE_CLICK, START_SINGLE_Click_Handler);
    button_attach(&btn10, SINGLE_CLICK, A_SINGLE_Click_Handler);
    button_attach(&btn11, SINGLE_CLICK, B_SINGLE_Click_Handler);
    button_attach(&btn12, SINGLE_CLICK, X_SINGLE_Click_Handler);
    button_attach(&btn13, SINGLE_CLICK, Y_SINGLE_Click_Handler);
    button_attach(&btn14, SINGLE_CLICK, GTA_SINGLE_Click_Handler);
    button_attach(&btn15, SINGLE_CLICK, GTB_SINGLE_Click_Handler);
    button_attach(&btn16, SINGLE_CLICK, GTC_SINGLE_Click_Handler);
    button_attach(&btn17, SINGLE_CLICK, GTD_SINGLE_Click_Handler);
    button_attach(&btn18, SINGLE_CLICK, GTE_SINGLE_Click_Handler);


    button_start(&btn1);
    button_start(&btn2);
    button_start(&btn3);
    button_start(&btn4);
    button_start(&btn5);
    button_start(&btn6);
    button_start(&btn7);
    button_start(&btn8);
    button_start(&btn9);
    button_start(&btn10);
    button_start(&btn11);
    button_start(&btn12);
    button_start(&btn13);
    button_start(&btn14);
    button_start(&btn15);
    button_start(&btn16);
    button_start(&btn17);
    button_start(&btn18);

}

This code is the CH32 microcontroller code used to initialize and detect key presses. Notes and explanations are as follows:

  1. The ButtonInit function is used to initialize the IO pin of the button. It configures different IO pins as inputs, some pins use pull-up (IPU) mode, and other pins use pull-down (IPD) mode, depending on the button hardware connection and working principle; at the same time, the initialization of MultiTimer is called Function, add the required key callback function, here the click event is used.

3.1.3 Download verification

We can download the firmware program and open the serial port debugging assistant; connect the TX to USB to TTL module with the H3 pin header to print the Log information of these 18 button presses;

Insert image description here

3.2 Example Eg02_AnalogDebug

This section is used as an example mainly to test the rocker potentiometer;

3.2.1Hardware design

The schematic diagram of the rocker potentiometer is as follows:

Insert image description here

Therefore, we only need to configure 4 ADC inputs to detect the XY of the two potentiometers; the MCU configuration is as follows:
Insert image description here

3.2.2 Software design

3.2.2.1 ADC initialization configuration
/*
 * Analog.c
 *
 *  Created on: Dec 4, 2023
 *      Author: Administrator
 */

#include "Analog.h"


u16 ADC_ConvertedValue[LENGTH]={
    
    0};

//ADC对应GPIO初始化配置以及ADC初始化配置
void adc_Init(void)
{
    
    
    GPIO_InitTypeDef GPIO_InitStructure;
    ADC_InitTypeDef ADC_InitStructure;

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |RCC_APB2Periph_ADC1 , ENABLE ); //使能GPIOA时钟和ADC

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_4|GPIO_Pin_5; //PA1~5对应ADC通道1~5
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //GPIO模式为模拟输入
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;  //配置ADC为独立模式
    ADC_InitStructure.ADC_ScanConvMode = ENABLE;        //多通道模式下开启扫描模式
    ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;  //设置开启连续转换模式
    ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //设置转换不是由外部触发启动,软件触发启动
    ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //设置ADC数据右对齐
    ADC_InitStructure.ADC_NbrOfChannel = LENGTH;           //规则转换的ADC通道的数目
    ADC_Init(ADC1, &ADC_InitStructure);                    //根据ADC_InitStructure中指定的参数初始化ADC1寄存器

    RCC_ADCCLKConfig(RCC_PCLK2_Div6); //设置ADC时钟分频为6分频

    ADC_Cmd(ADC1, ENABLE);      //使能ADC1

    ADC_ResetCalibration(ADC1); //重置ADC1校准寄存器。

    while(ADC_GetResetCalibrationStatus(ADC1)); //等待复位校准结束

    ADC_StartCalibration(ADC1); //开启AD校准

    while(ADC_GetCalibrationStatus(ADC1));      //等待校准结束
}

//ADC DMA模式配置
void DMA_Tx_Init( void )
{
    
    
    DMA_InitTypeDef DMA_InitStructure;

    RCC_AHBPeriphClockCmd( RCC_AHBPeriph_DMA1, ENABLE ); //使能开启DMA时钟

    DMA_DeInit(DMA1_Channel1); //复位DMA控制器

    DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&ADC1->RDATAR;  //配置外设地址为ADC数据寄存器地址
    DMA_InitStructure.DMA_MemoryBaseAddr = (u32)ADC_ConvertedValue; //配置存储器地址为读取ADC值地址
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;              //配置数据源为外设,即DMA传输方式为外设到存储器
    DMA_InitStructure.DMA_BufferSize = LENGTH;                      //设置DMA数据缓冲区大小,此处设置为LENGTH
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//设置DMA外设递增模式关闭
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;         //设置DMA存储器递增模式开启
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; //设置外设数据大小为半字,即两个字节
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;         //设置存储器数据大小为半字,即两个字节
    DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;     //设置DMA模式为循环传输模式
    DMA_InitStructure.DMA_Priority = DMA_Priority_High; //设置DMA传输通道优先级为高,当使用一 DMA通道时,优先级设置不影响
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;        //因为此DMA传输方式为外设到存储器,因此禁用存储器到存储器传输方式
    DMA_Init( DMA1_Channel1, &DMA_InitStructure );      //初始化DMA

    DMA_Cmd(DMA1_Channel1 , ENABLE);  //使能DMA
}

void ADC_DMA_CONF(void)
{
    
    
    adc_Init();

    DMA_Tx_Init();

    // 配置 ADC 通道转换顺序为1,第一个转换,采样时间为55.5个时钟周期
    ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 1, ADC_SampleTime_239Cycles5);
    ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 2, ADC_SampleTime_239Cycles5);
    ADC_RegularChannelConfig(ADC1, ADC_Channel_4, 3, ADC_SampleTime_239Cycles5);
    ADC_RegularChannelConfig(ADC1, ADC_Channel_5, 4, ADC_SampleTime_239Cycles5);

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

    // 由于没有采用外部触发,所以使用软件触发ADC转换
    ADC_SoftwareStartConvCmd(ADC1, ENABLE);
}


The function of this code is to configure the ADC and DMA of STM32 to achieve continuous conversion of analog inputs from multiple channels and store the conversion results in the array ADC_ConvertedValue. This is typically used for reading analog signals such as sensors. When used, the ADC conversion result of the corresponding channel can be obtained in the ADC_ConvertedValue array.

3.2.2.2 User code
int main(void)
{
    
    
    u16 tick=0;
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    Delay_Init();
    USART_Printf_Init(115200);
    printf("SystemClk:%d;\r\n", SystemCoreClock);
    ButtonInit();
    ADC_DMA_CONF();
    printf("ADC Debug Demo;\r\n");
    while(1)
    {
    
    
        tick++;
        if((tick%100)==0)//500ms
        {
    
    
            tick=0;
            printf("\r\n The current ADCH1 value = %d \r\n", ADC_ConvertedValue[0]);
            printf("\r\n The current ADCH2 value = %d \r\n", ADC_ConvertedValue[1]);
            printf("\r\n The current ADCH3 value = %d \r\n", ADC_ConvertedValue[2]);
            printf("\r\n The current ADCH4 value = %d \r\n", ADC_ConvertedValue[3]);
        }
        button_ticks();
        Delay_Ms(5);
    }
}

In the test code, we directly print the AD value log once every 500ms;

3.2.3 Download verification

We can download the firmware program and open the serial port debugging assistant; connect the TX to USB to TTL module with the H3 pin header to print the Log information of the 4-channel ADC;

Insert image description here

3.2 Example Eg03_MultiTimer

This section uses MultiTimer, an open source software timer suite, to implement a software framework for realizing LED, ADC, and button multitasking. Regarding MultiTimer open source, you can visit the open source GitHub link to learn: https://github.com/0x1abin /MultiTimer.git

3.2.1Hardware design

This section uses the rocker potentiometer button and LED. The rocker potentiometer and button have been introduced in the previous two sections. The schematic diagram of the LED is as follows:
Insert image description here
The LED is connected to PB3 of the MCU :
Insert image description here

3.2.2 Software design

3.2.2.1 LED configuration
#include "LED.h"

/*********************************************************************
 * @fn      GPIO_Toggle_INIT
 *
 * @brief   Initializes GPIOA.0
 *
 * @return  none
 */
void LED_Init(void)
{
    
    
    GPIO_InitTypeDef GPIO_InitStructure = {
    
    0};

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
}

The function of this code is to configure LED PB3 as a push-pull output;

At the same time, LED.h uses a macro to pull IO high and low;

#ifndef MYBSP_LED_H_
#define MYBSP_LED_H_

#include "debug.h"

#define     LED(x)      (x?GPIO_ResetBits(GPIOB,GPIO_Pin_3):GPIO_SetBits(GPIOB,GPIO_Pin_3))

extern void LED_Init(void);

#endif /* MYBSP_LED_H_ */
3.2.2.2 SYStick configuration

Because MultiTimer needs Tick just like RTOS needs Tick, Systick is configured here as its time base;

void SysTick_Handler(void) __attribute__((interrupt("WCH-Interrupt-fast")));

/*********************************************************************
 * @fn      SYSTICK_Init_Config
 *
 * @brief   SYSTICK_Init_Config.
 *
 * @return  none
 */
void SYSTICK_Init_Config(u64 ticks)
{
    
    
    SysTick->SR = 0;
    SysTick->CNT = 0;
    SysTick->CMP = ticks;
    SysTick->CTLR =0xF;

    NVIC_SetPriority(SysTicK_IRQn, 15);
    NVIC_EnableIRQ(SysTicK_IRQn);
}

void SysTick_Handler(void)
{
    
    
    SysTick->SR = 0;
    uwTick++;
}
3.2.2.3 Application code

Finally, we create three timers to perform corresponding tasks respectively, as shown in the following code:

vu32 uwTick;

MultiTimer timer1;
MultiTimer timer2;
MultiTimer timer3;
uint64_t PlatformTicksGetFunc(void) {
    
    
    return uwTick;
}
void LEDTimer1Callback(MultiTimer* timer, void *userData)
{
    
    
    static FlagStatus LedSta=RESET;
    LED(LedSta);
    LedSta=~LedSta;
    printf("LED Status:%d\r\n",LedSta);
    MultiTimerStart(timer, 500, LEDTimer1Callback, userData);
}
void ADCTimer2Callback(MultiTimer* timer, void *userData)
{
    
    
    printf("\r\n The current ADCH1 value = %d \r\n", ADC_ConvertedValue[0]);
    printf("\r\n The current ADCH2 value = %d \r\n", ADC_ConvertedValue[1]);
    printf("\r\n The current ADCH3 value = %d \r\n", ADC_ConvertedValue[2]);
    printf("\r\n The current ADCH4 value = %d \r\n", ADC_ConvertedValue[3]);

    MultiTimerStart(timer, 1000, ADCTimer2Callback, userData);
}
void ButtonTimer3Callback(MultiTimer* timer, void *userData)
{
    
    
    button_ticks();;
    MultiTimerStart(timer, 5, ButtonTimer3Callback, userData);
}
void PollSystemInit(void) {
    
    
    MultiTimerInstall(PlatformTicksGetFunc);
    MultiTimerStart(&timer1, 500, LEDTimer1Callback, NULL);
    MultiTimerStart(&timer2, 1000, ADCTimer2Callback, NULL);
    MultiTimerStart(&timer3, 5, ButtonTimer3Callback, NULL);
    SYSTICK_Init_Config(SystemCoreClock/1000-1);
}

3.2.3 Download verification

We can download the firmware program and open the serial port debugging assistant; connect the TX to USB to TTL module with the H3 pin header to print three task Log information;
Insert image description here

Guess you like

Origin blog.csdn.net/qq_36250901/article/details/134931400