Real-Time Joystick Position Tracking
实时追踪并记录摇杆移动,搭配持久化 Flash 存储实现操作日志记录
你将学到和构建的内容
简介
Real-Time 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)、机器人控制系统及其他控制接口场景,是构建交互式控制系统的理想组件。
mikroBUS 2
Flash 12 Click
Flash 12 Click 是一款紧凑型扩展板,提供高度可靠的存储解决方案。该板搭载 Renesas 出品的 AT25EU0041A,这是一款 4Mbit 串行 Flash 存储器,以超低功耗著称。此 Click 板专为运行于 IoT 网络边缘的系统设计,特别适合作为程序代码的存储与直接从 NOR Flash 执行的解决方案。该器件采用创新的擦除架构,在读写擦除各操作中均实现了低功耗和快速响应,擦除时间短,是性能与效率兼具的闪存存储器。Flash 12 Click 特别适用于新一代低功耗 IoT 设备的开发,满足快速启动、代码缓存、事件记录和数据日志等应用场景下对低功耗、高性能存储的需求。
功能概述
开发板
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”)的安装孔位于角落,便于使用螺丝固定,实现简便安装。
微控制器概述
MCU卡片 / MCU

建筑
ARM Cortex-M4
MCU 内存 (KB)
10
硅供应商
STMicroelectronics
引脚数
100
RAM (字节)
100
一步一步来
项目组装
实时跟踪您的结果
应用程序输出
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”替换为要显示的参数。

软件支持
库描述
Real-Time Joystick Position Tracking Solution 使用 NECTO Studio 开发,确保兼容 mikroSDK 的开源库和工具。该解决方案采用即插即用的设计,支持快速实施和测试,并与所有配备 mikroBUS™ 插座的开发板、入门套件和 mikromedia 板完全兼容。
示例描述
Real-Time 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 值转换为具体的摇杆方向状态,如 UP、DOWN、LEFT、RIGHT 和 NEUTRAL。flash12_init
- 初始化 Flash 12 Click,用于在非易失性存储器中存储摇杆位置信息。flash12_memory_write
- 将摇杆位置数据写入指定地址的 Flash 存储器。flash12_memory_read
- 从 Flash 存储器中读取已存储的摇杆位置信息以进行验证。
应用初始化
初始化过程配置系统以实现实时摇杆追踪功能:
1. 初始化 UART 日志器用于调试信息输出。
2. 配置 Joystick 3 Click 通过 SPI 接口读取摇杆位置。
3. 初始化 Flash 12 Click,用于将摇杆位置信息持久化存储到 Flash 中。
应用任务
主应用循环持续执行以下任务:
1. 读取摇杆的 ADC 值以检测当前移动状态。
2. 将 ADC 值映射为具体的方向状态(如 UP、DOWN、LEFT、RIGHT)。
3. 将当前状态与前一状态比较,判断是否发生方向变化。
4. 如果检测到变化,则将当前方向记录写入 Flash 存储器。
5. 从 Flash 读取并验证已存储的数据,确保日志准确。
6. 等待 2 秒后再次检查摇杆位置变化。
开源
代码示例
完整的应用程序代码和一个现成的项目可以通过NECTO Studio包管理器直接安装到NECTO Studio。 应用程序代码也可以在MIKROE的GitHub账户中找到。
/*
* Solution Name: Real-Time Joystick Position Tracking Solution
*
* Description:
* This embedded application continuously tracks joystick positions
* using the Joystick 3 Click and logs the detected positions to Flash memory
* via the Flash 12 Click.
*
* The system utilizes the following Click boards:
* - Joystick 3 Click: Captures joystick position in raw ADC values and detects
* movement directions.
* - Flash 12 Click: Stores detected joystick positions in non-volatile memory.
*
* The `application_init` function initializes both Joystick 3 and Flash 12 Clicks,
* ensuring proper configuration and communication, and sets up the UART logger for debugging.
*
* The `application_task` function monitors joystick position changes 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 (Joystick position tracking)
* - MIKROBUS_2: Flash 12 Click (Persistent storage for joystick positions)
*
* Key Features:
* - Real-time detection of joystick position changes.
* - 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: 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;
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; /**< 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 Click initialization.
joystick3_cfg_setup( &joystick3_cfg );
JOYSTICK3_MAP_MIKROBUS( joystick3_cfg, MIKROBUS_1 );
if ( SPI_MASTER_ERROR == joystick3_init( &joystick3, &joystick3_cfg ) )
{
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 = STARTING_ADDRESS;
uint8_t data_buf[32] = { 0 };
log_printf( &logger, "\r\n--- Reading Stored Joystick Positions ---\r\n" );
while ( 1 )
{
memset( data_buf, 0, sizeof( data_buf ) );
if ( FLASH12_OK != flash12_memory_read( &flash12, mem_address, data_buf, sizeof( data_buf ) ) )
{
log_printf( &logger, "Error reading memory at 0x%.6lX\r\n", mem_address );
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 logged data.\r\n" );
break;
}
log_printf( &logger, "Memory 0x%.6lX: %s\r\n", mem_address, data_buf );
mem_address += strlen( (char*)data_buf ) + 1; // Move to the next stored entry
}
log_printf( &logger, "-----------------------------------------\r\n" );
}
void application_task ( void )
{
static uint32_t mem_address = STARTING_ADDRESS; // Rolling memory address
static joystick3_position_t last_position = JOYSTICK3_POSITION_NEUTRAL;
uint16_t raw_x, raw_y;
uint8_t data_buf[32] = { 0 }; // Buffer for logging position
// Read Joystick raw ADC values
if ( JOYSTICK3_OK == joystick3_read_raw_adc ( &joystick3, &raw_x, &raw_y ) )
{
joystick3_position_t current_position = joystick3_get_position( raw_x, raw_y );
// Log position if it changes
if ( current_position != last_position )
{
const char* position_str = "UNKNOWN";
switch ( current_position )
{
case JOYSTICK3_POSITION_NEUTRAL: position_str = "NEUTRAL"; break;
case JOYSTICK3_POSITION_UP: position_str = "UP"; break;
case JOYSTICK3_POSITION_UPPER_RIGHT: position_str = "UPPER-RIGHT"; break;
case JOYSTICK3_POSITION_RIGHT: position_str = "RIGHT"; break;
case JOYSTICK3_POSITION_LOWER_RIGHT: position_str = "LOWER-RIGHT"; break;
case JOYSTICK3_POSITION_DOWN: position_str = "DOWN"; break;
case JOYSTICK3_POSITION_LOWER_LEFT: position_str = "LOWER-LEFT"; break;
case JOYSTICK3_POSITION_LEFT: position_str = "LEFT"; break;
case JOYSTICK3_POSITION_UPPER_LEFT: position_str = "UPPER-LEFT"; break;
}
// Display position on UART
log_printf( &logger, "Joystick position: %s\r\n", position_str );
// Write position to Flash memory
memset( data_buf, 0, sizeof( data_buf ) );
strncpy( (char*)data_buf, position_str, sizeof( data_buf ) - 1 );
if ( FLASH12_OK == flash12_memory_write( &flash12,
mem_address,
data_buf,
strlen( position_str ) + 1 ) )
{
log_printf( &logger, "Stored in Flash at 0x%.6lX: %s\r\n",
mem_address,
data_buf );
mem_address += strlen( position_str ) + 1; // Move address forward
}
last_position = current_position; // Update last position
Delay_ms( 2000 );
}
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
额外支持
资源
类别:Human-Machine Interface (HMI)