Joystick 3 Click with Clicker 4 for STM32F4

Published 4月 10, 2025

点击板

Joystick 3 Click
Flash 12 Click
Joystick 3 Click

开发板

Clicker 4 for STM32F4

编译器

NECTO Studio

微控制器单元

STM32F407VGT6

Dual Joystick Position Tracking Solution

实时追踪并存储双摇杆动作,结合持久内存记录与精确方向检测功能

你将学到和构建的内容

简介

Dual Joystick Position Tracking 解决方案集成了两个 Joystick 3 Click 板和一个 Flash 12 Click 板,实现了双摇杆输入的实时同步追踪与持久存储。该方案专为游戏控制器、机器人系统以及多轴输入设备而设计,通过采集两个摇杆的原始 ADC 值,判断各自的方向状态,并在发生移动变化时将其记录到非易失性 Flash 存储器中。该系统提供清晰且响应迅速的输入追踪机制,通过 UART 输出反馈信息,具备双通道输入能力,为需要精确用户输入历史记录的交互式系统提供了可靠的平台。

mikroBUS 1

Joystick 3 Click

Joystick 3 Click 是一款紧凑型扩展板,可满足各类方向性模拟输入需求。该板搭载 Adafruit Industries 出品的 2765 高品质微型双轴模拟摇杆。该摇杆为“自动回中”模拟型结构,配有黑色摇杆帽,外形类似于 PSP 游戏摇杆。它内部集成两个 10kΩ 电位器,分别用于上下(Y 轴)与左右(X 轴)方向的控制。由于该摇杆为模拟信号输出类型,Joystick 3 Click 通过 MCP3204 12 位 A/D 转换器,将模拟信号转换为数字信号,并通过 SPI 接口与 mikroBUS™ 进行连接与通信。这款 Click 板非常适合用作人机交互设备(HMI)、机器人控制系统及其他控制接口场景,是构建交互式控制系统的理想组件。

Joystick 3 Click top side image

mikroBUS 2

Flash 12 Click

Flash 12 Click 是一款紧凑型扩展板,提供高度可靠的存储解决方案。该板搭载 Renesas 出品的 AT25EU0041A,这是一款 4Mbit 串行 Flash 存储器,以超低功耗著称。此 Click 板专为运行于 IoT 网络边缘的系统设计,特别适合作为程序代码的存储与直接从 NOR Flash 执行的解决方案。该器件采用创新的擦除架构,在读写擦除各操作中均实现了低功耗和快速响应,擦除时间短,是性能与效率兼具的闪存存储器。Flash 12 Click 特别适用于新一代低功耗 IoT 设备的开发,满足快速启动、代码缓存、事件记录和数据日志等应用场景下对低功耗、高性能存储的需求。

Flash 12 Click front-background image

mikroBUS 4

Joystick 3 Click

Joystick 3 Click 是一款紧凑型扩展板,可满足各类方向性模拟输入需求。该板搭载 Adafruit Industries 出品的 2765 高品质微型双轴模拟摇杆。该摇杆为“自动回中”模拟型结构,配有黑色摇杆帽,外形类似于 PSP 游戏摇杆。它内部集成两个 10kΩ 电位器,分别用于上下(Y 轴)与左右(X 轴)方向的控制。由于该摇杆为模拟信号输出类型,Joystick 3 Click 通过 MCP3204 12 位 A/D 转换器,将模拟信号转换为数字信号,并通过 SPI 接口与 mikroBUS™ 进行连接与通信。这款 Click 板非常适合用作人机交互设备(HMI)、机器人控制系统及其他控制接口场景,是构建交互式控制系统的理想组件。

Joystick 3 Click top side image

功能概述

开发板

Clicker 4 for STM32F4 是一款紧凑型开发板,专为快速构建自定义设备而设计。它配备 STM32F407VGT6 MCU、四个 mikroBUS™ 插座(用于 Click board™ 连接)、电源管理等功能,使其成为快速应用开发的理想选择。核心采用 STM32F407VGT6 MCU,该芯片基于 Arm® Cortex®-M4 32 位处理器,运行频率高达 168 MHz,提供充足的计算能力以满足高负载任务需求。除了两个 1x20 排针接口外,四个 mikroBUS™ 插座可支持庞大且不断增长的 Click boards™ 生态系统。开发板上清晰标记的功能区域提供直观、易用的界面,加快开发进程。Clicker 4 不仅能加速原型设计,还可直接集成到项目中,无需额外的硬件修改。四个 4.2mm(0.165”)的安装孔位于角落,便于使用螺丝固定,实现简便安装。

Clicker 4 for STM32F4 front image

微控制器概述 

MCU卡片 / MCU

default

建筑

ARM Cortex-M4

MCU 内存 (KB)

10

硅供应商

STMicroelectronics

引脚数

100

RAM (字节)

100

一步一步来

项目组装

Clicker 4 for STM32F4 front image hardware assembly

首先,选择您的开发板 - Clicker 4 for STM32F4

Clicker 4 for STM32F4 front image hardware assembly
LTE IoT 5 Click front-background image hardware assembly
Calypso Click front-background image hardware assembly
Button PLAY Click top side image hardware assembly
Board mapper by product7 hardware assembly
Necto image step 2 hardware assembly
Necto image step 3 hardware assembly
Necto image step 4 hardware assembly
Necto image step 5 hardware assembly
NECTO Output Selection Step Image hardware assembly
Necto image step 6 hardware assembly
Clicker 4 for STM32F4 HA MCU Step hardware assembly
Necto image step 8 hardware assembly
Necto image step 9 hardware assembly
Necto image step 10 hardware assembly
Necto PreFlash Image hardware assembly

实时跟踪您的结果

应用程序输出

1. 应用程序输出 - 在调试模式下,“应用程序输出”窗口支持实时数据监控,直接提供执行结果的可视化。请按照提供的教程正确配置环境,以确保数据正确显示。

2. UART 终端 - 使用UART Terminal通过USB to UART converter监视数据传输,实现Click board™与开发系统之间的直接通信。请根据项目需求配置波特率和其他串行设置,以确保正常运行。有关分步设置说明,请参考提供的教程

3. Plot 输出 - Plot功能提供了一种强大的方式来可视化实时传感器数据,使趋势分析、调试和多个数据点的对比变得更加直观。要正确设置,请按照提供的教程,其中包含使用Plot功能显示Click board™读数的分步示例。在代码中使用Plot功能时,请使用以下函数:plot(insert_graph_name, variable_name);。这是一个通用格式,用户需要将“insert_graph_name”替换为实际图表名称,并将“variable_name”替换为要显示的参数。

软件支持

库描述

Dual Joystick Position Tracking Solution 使用 NECTO Studio 开发,确保兼容 mikroSDK 的开源库和工具。该解决方案采用即插即用的设计,支持快速实施和测试,并与所有配备 mikroBUS™ 插座的开发板、入门套件和 mikromedia 板完全兼容。

示例描述
Dual Joystick Position Tracking 解决方案集成了两个 Joystick 3 Click 和一个 Flash 12 Click,用于同时追踪和存储两个摇杆的位置。Joystick 3 Click 捕捉两个摇杆输入的原始 ADC 值,并检测每个摇杆的移动方向。Flash 12 Click 将这些位置存储在非易失性内存中,实现持久记录。该系统适用于需要实时追踪和位置历史存储的交互式应用,如游戏控制器、机器人系统和具有双输入的用户操作跟踪。

关键功能:

  • joystick3_init - 初始化 Joystick 3 Click,用于读取两个摇杆的移动状态和 ADC 值。

  • joystick3_read_raw_adc - 读取两个摇杆 X 和 Y 轴的原始 ADC 数值。

  • joystick3_get_position - 将原始 ADC 数值转换为每个摇杆的具体方向状态(例如:上、下、左、右、中立)。

  • flash12_init - 初始化 Flash 12 Click,用于将摇杆位置数据存储到非易失性内存中。

  • flash12_memory_write - 将两个摇杆的位置数据写入指定地址的 Flash 存储器中。

  • flash12_memory_read - 从 Flash 存储器中读取已存储的摇杆位置数据以供验证。

应用初始化
初始化序列用于配置系统以实现双摇杆位置追踪功能:

1. 初始化 UART 日志记录器以用于调试。

2. 通过 SPI 接口配置 Joystick 3 Click 以读取摇杆位置。

3. 初始化 Flash 12 Click,用于将摇杆位置持久存储至 Flash 内存中。

如果任一初始化步骤失败,系统将记录错误并停止运行以防止异常行为。

应用任务
主循环执行以下操作:

1. 读取两个摇杆的 ADC 数值以检测其移动情况。

2. 将两个摇杆的 ADC 数值映射为各自的位置状态(例如:上、下、左、右)。

3. 比较两个摇杆当前的位置与之前的位置,以检测是否发生变化。

4. 如果检测到位置变化,则将两个摇杆的位置记录到 Flash 存储器中。

5. 读取并验证 Flash 存储中的数据,以确保记录准确。

6. 等待 2 秒后再检测下一次位置变化。

开源

代码示例

完整的应用程序代码和一个现成的项目可以通过NECTO Studio包管理器直接安装到NECTO Studio 应用程序代码也可以在MIKROE的GitHub账户中找到。

/*
 * Solution Name: Dual Joystick Real-Time Position Tracking Solution
 *
 * Description:
 * This embedded application continuously tracks joystick positions 
 * using two Joystick 3 Click boards and logs the detected positions 
 * to Flash memory via the Flash 12 Click.
 *
 * The system utilizes the following Click boards:
 *   - Joystick 3 Click (x2): Captures joystick positions in raw ADC values and detects 
 *     movement directions for both joysticks.
 *   - Flash 12 Click: Stores detected joystick positions in non-volatile memory.
 *
 * The `application_init` function initializes both Joystick 3 Click boards and the Flash 12 Click,
 * ensuring proper configuration and communication, and sets up the UART logger for debugging.
 *
 * The `application_task` function monitors position changes for both joysticks and logs the detected 
 * positions to Flash memory when a new position is detected. It also verifies stored data 
 * by reading from memory.
 *
 * Hardware Setup:
 *   - MIKROBUS_1: Joystick 3 Click #1 (Joystick position tracking)
 *   - MIKROBUS_2: Flash 12 Click (Persistent storage for joystick positions)
 *   - MIKROBUS_4: Joystick 3 Click #2 (Second joystick position tracking)
 *
 * Key Features:
 *   - Real-time detection of joystick position changes for both joysticks.
 *   - Logging of joystick positions to Flash memory.
 *   - Verification of stored data through memory read-back.
 *   - UART-based logging for displaying position updates.
 *
 * Development Environment:
 *   - [NECTO Studio](https://www.mikroe.com/necto)
 *   - [mikroSDK v2.0](https://www.mikroe.com/mikrosdk) framework
 *   - MIKROE [Click boards](https://www.mikroe.com/click-boards) Add-ons
 *
 * Author: Branko Jaksic
 * Date: March, 2025
 */

// ------------------------------------------------------------------- INCLUDES
#include "board.h"
#include "log.h"
#include "flash12.h"
#include "joystick3.h"

// ------------------------------------------------------------- PRIVATE MACROS
// Starting memory address
#define STARTING_ADDRESS 0x012345

// ------------------------------------------------------------------ VARIABLES
static log_t logger;
static flash12_t flash12;
static joystick3_t joystick3_1;  // First Joystick (mikroBUS_1)
static joystick3_t joystick3_2;  // Second Joystick (mikroBUS_4)

typedef uint8_t joystick3_position_t;

// ------------------------------------------------------ APPLICATION FUNCTIONS
void application_init ( void )
{
    log_cfg_t log_cfg;  /**< Logger config object. */
    joystick3_cfg_t joystick3_cfg_1;  /**< Click config object. */
    joystick3_cfg_t joystick3_cfg_2;  /**< Click config object. */
    flash12_cfg_t flash12_cfg;  /**< Click config object. */

    /** 
     * Logger initialization.
     * Default baud rate: 115200
     * Default log level: LOG_LEVEL_DEBUG
     * @note If USB_UART_RX and USB_UART_TX 
     * are defined as HAL_PIN_NC, you will 
     * need to define them manually for log to work. 
     * See @b LOG_MAP_USB_UART macro definition for detailed explanation.
     */
    LOG_MAP_USB_UART( log_cfg );
    log_init( &logger, &log_cfg );
    log_info( &logger, " Application Init " );

    // Joystick 3 (1st) Click initialization.
    joystick3_cfg_setup( &joystick3_cfg_1 );
    JOYSTICK3_MAP_MIKROBUS( joystick3_cfg_1, MIKROBUS_1 );
    if ( SPI_MASTER_ERROR == joystick3_init( &joystick3_1, &joystick3_cfg_1 ) )
    {
        log_error( &logger, " Communication init." );
        for ( ; ; );
    }

    // Joystick 3 (2nd) Click initialization.
    joystick3_cfg_setup( &joystick3_cfg_2 );
    JOYSTICK3_MAP_MIKROBUS( joystick3_cfg_2, MIKROBUS_4 );
    if ( SPI_MASTER_ERROR == joystick3_init( &joystick3_2, &joystick3_cfg_2 ) )
    {
        log_error( &logger, " Communication init." );
        for ( ; ; );
    }

    // Flash 12 Click initialization.
    flash12_cfg_setup( &flash12_cfg );
    FLASH12_MAP_MIKROBUS( flash12_cfg, MIKROBUS_2 );
    if ( SPI_MASTER_ERROR == flash12_init( &flash12, &flash12_cfg ) )
    {
        log_error( &logger, " Communication init." );
        for ( ; ; );
    }
    
    if ( FLASH12_ERROR == flash12_default_cfg ( &flash12 ) )
    {
        log_error( &logger, " Default configuration." );
        for ( ; ; );
    }

    log_info( &logger, " Application Task " );
}

void read_logged_positions ( void ) 
{
    uint32_t mem_address_1 = STARTING_ADDRESS;
    uint32_t mem_address_2 = STARTING_ADDRESS + 0x100;
    uint8_t data_buf[32] = { 0 };

    log_printf( &logger, "\r\n--- Reading Stored Joystick Positions ---\r\n" );

    // Read Joystick 1 logged positions
    log_printf( &logger, "Joystick 1 Positions:\r\n" );
    while ( 1 ) 
    {
        memset( data_buf, 0, sizeof( data_buf ) );

        if ( FLASH12_OK != flash12_memory_read( &flash12, mem_address_1, data_buf, sizeof( data_buf ) ) )
        {
            log_printf( &logger, "Error reading memory at 0x%.6lX\r\n", mem_address_1 );
            break;
        }

        // Stop if we reach an empty space (default erased flash value is 0xFF)
        if ( data_buf[0] == 0xFF || data_buf[0] == '\0' ) 
        {
            log_printf( &logger, "End of Joystick 1 logged data.\r\n" );
            break;
        }

        log_printf( &logger, "Memory 0x%.6lX: %s\r\n", mem_address_1, data_buf );

        mem_address_1 += strlen( (char*)data_buf ) + 1;  // Move to the next stored entry
    }

    // Read Joystick 2 logged positions
    log_printf( &logger, "Joystick 2 Positions:\r\n" );
    while ( 1 ) 
    {
        memset( data_buf, 0, sizeof( data_buf ) );

        if ( FLASH12_OK != flash12_memory_read( &flash12, mem_address_2, data_buf, sizeof( data_buf ) ) )
        {
            log_printf( &logger, "Error reading memory at 0x%.6lX\r\n", mem_address_2 );
            break;
        }

        // Stop if we reach an empty space (default erased flash value is 0xFF)
        if ( data_buf[0] == 0xFF || data_buf[0] == '\0' ) 
        {
            log_printf( &logger, "End of Joystick 2 logged data.\r\n" );
            break;
        }

        log_printf( &logger, "Memory 0x%.6lX: %s\r\n", mem_address_2, data_buf );

        mem_address_2 += strlen( (char*)data_buf ) + 1;  // Move to the next stored entry
    }

    log_printf( &logger, "-----------------------------------------\r\n" );
}

const char* joystick_position_to_string( joystick3_position_t position ) 
{
    switch ( position ) 
    {
        case JOYSTICK3_POSITION_NEUTRAL:      return "NEUTRAL";
        case JOYSTICK3_POSITION_UP:           return "UP";
        case JOYSTICK3_POSITION_DOWN:         return "DOWN";
        case JOYSTICK3_POSITION_LEFT:         return "LEFT";
        case JOYSTICK3_POSITION_RIGHT:        return "RIGHT";
        case JOYSTICK3_POSITION_UPPER_LEFT:   return "UPPER-LEFT";
        case JOYSTICK3_POSITION_UPPER_RIGHT:  return "UPPER-RIGHT";
        case JOYSTICK3_POSITION_LOWER_LEFT:   return "LOWER-LEFT";
        case JOYSTICK3_POSITION_LOWER_RIGHT:  return "LOWER-RIGHT";
        default:                              return "UNKNOWN";
    }
}

void application_task ( void ) 
{
    static uint32_t mem_address_1 = STARTING_ADDRESS; // Rolling memory address
    static uint32_t mem_address_2 = STARTING_ADDRESS + 0x100;  // Offset for second joystick
    static joystick3_position_t last_position_1 = JOYSTICK3_POSITION_NEUTRAL;
    static joystick3_position_t last_position_2 = JOYSTICK3_POSITION_NEUTRAL;
    
    uint16_t raw_x1, raw_y1, raw_x2, raw_y2;
    uint8_t data_buf[32] = { 0 };  // Buffer for logging position
    
    // Read Joystick 1 raw ADC values
    if ( JOYSTICK3_OK == joystick3_read_raw_adc ( &joystick3_1, &raw_x1, &raw_y1 ) ) 
    {
        joystick3_position_t current_position_1 = joystick3_get_position( raw_x1, raw_y1 );

        // Log position if it changes
        if ( current_position_1 != last_position_1 ) 
        {
            const char* position_str_1 = joystick_position_to_string( current_position_1 );
            log_printf( &logger, "Joystick 1 position: %s\r\n", position_str_1 );

            // Write position to Flash memory
            memset( data_buf, 0, sizeof( data_buf ) );
            strncpy( (char*)data_buf, position_str_1, sizeof( data_buf ) - 1 );

            if ( FLASH12_OK == flash12_memory_write( &flash12,
                                                     mem_address_1,
                                                     data_buf,
                                                     strlen( position_str_1 ) + 1 ) )
            {
                log_printf( &logger, "Stored JoyStick 1 in Flash at 0x%.6lX: %s\r\n",
                            mem_address_1,
                            data_buf );
                mem_address_1 += strlen( position_str_1 ) + 1;  // Move address forward
            }

            last_position_1 = current_position_1;  // Update last position
        }
        read_logged_positions();  // Call to read and verify stored positions
    }

    // Read Joystick 2 raw ADC values
    if ( JOYSTICK3_OK == joystick3_read_raw_adc ( &joystick3_2, &raw_x1, &raw_y2 ) ) 
    {
        joystick3_position_t current_position_2 = joystick3_get_position( raw_x2, raw_y2 );

        // Log position if it changes
        if ( current_position_2 != last_position_2 ) 
        {
            const char* position_str_2 = joystick_position_to_string( current_position_2 );
            log_printf( &logger, "Joystick 2 position: %s\r\n", position_str_2 );

            // Write position to Flash memory
            memset( data_buf, 0, sizeof( data_buf ) );
            strncpy( (char*)data_buf, position_str_2, sizeof( data_buf ) - 1 );

            if ( FLASH12_OK == flash12_memory_write( &flash12,
                                                     mem_address_2,
                                                     data_buf,
                                                     strlen( position_str_2 ) + 1 ) )
            {
                log_printf( &logger, "Stored JoyStick 2 in Flash at 0x%.6lX: %s\r\n",
                            mem_address_2,
                            data_buf );
                mem_address_2 += strlen( position_str_2 ) + 1;  // Move address forward
            }

            last_position_2 = current_position_2;  // Update last position
        }
        read_logged_positions();  // Call to read and verify stored positions
    }

    Delay_ms( 100 );
}


int main ( void ) 
{
    /* Do not remove this line or clock might not be set correctly. */
    #ifdef PREINIT_SUPPORTED
    preinit();
    #endif

    application_init( );

    for ( ; ; ) 
    {
        application_task( );
    }

    return 0;
}

// ------------------------------------------------------------------------ END

额外支持

资源

喜欢这个项目吗?

'购买此套件' 按钮会直接带您进入购物车,您可以在购物车中轻松添加或移除产品。