这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 活动中心 » 板卡试用 » 【EV76S68A试用】3-摇杆驱动(ADC)

共1条 1/1 1 跳转至

【EV76S68A试用】3-摇杆驱动(ADC)

菜鸟
2025-08-17 19:02:29     打赏

项目概述

我成功实现了基于SAME51J20A的双轴摇杆驱动,为后去功能开发奠定了基础。主要实现目的替代之前通过串口实现的按键功能,当前仅用一个摇杆实现上下左右和确认功能。

第一章 关键外设配置 (两个ADC通道 + 一个IO)

1.1 硬件连接:

image.png

1.2 软件配置

本来是计划使用DMA+ADC的结果没配置成功,因此采用这种方式

image.png

1.3 软件实现策略


模块方案原因
ADC轮询采样DMA配置未完成,快速实现
按键检测GPIO轮询暂未使用中断


1.4 代码实现

// custom/hal/peripheral/adc_hal.c
#include "adc_hal.h"

// ADC初始化
static bool adc_init(void)
{
    // 初始化ADC外设(使用MPLAB Harmony配置的参数)
    ADC0_Initialize();
    ADC0_Enable();
    return true;
}

// 获取ADC值
static uint16_t adc_get_value(adc_channel_t channel)
{
    if (channel >= ADC_CHANNEL_MAX) {
        return 0;
    }

    // 选择通道
    switch (channel) {
    case ADC_CHANNEL_0:
        ADC0_ChannelSelect(ADC_POSINPUT_AIN0, ADC_NEGINPUT_GND);
        break;
    case ADC_CHANNEL_1:
        ADC0_ChannelSelect(ADC_POSINPUT_AIN1, ADC_NEGINPUT_GND);
        break;
    case ADC_CHANNEL_2:
        ADC0_ChannelSelect(ADC_POSINPUT_AIN2, ADC_NEGINPUT_GND);
        break;
    case ADC_CHANNEL_3:
        ADC0_ChannelSelect(ADC_POSINPUT_AIN3, ADC_NEGINPUT_GND);
        break;
    case ADC_CHANNEL_4:
        ADC0_ChannelSelect(ADC_POSINPUT_AIN4, ADC_NEGINPUT_GND);
        break;
    case ADC_CHANNEL_5:
        ADC0_ChannelSelect(ADC_POSINPUT_AIN5, ADC_NEGINPUT_GND);
        break;
    case ADC_CHANNEL_6:
        ADC0_ChannelSelect(ADC_POSINPUT_AIN6, ADC_NEGINPUT_GND);
        break;
    case ADC_CHANNEL_7:
        ADC0_ChannelSelect(ADC_POSINPUT_AIN7, ADC_NEGINPUT_GND);
        break;
    case ADC_CHANNEL_8:
        ADC0_ChannelSelect(ADC_POSINPUT_AIN8, ADC_NEGINPUT_GND);
        break;
    case ADC_CHANNEL_9:
        ADC0_ChannelSelect(ADC_POSINPUT_AIN9, ADC_NEGINPUT_GND);
        break;
    case ADC_CHANNEL_10:
        ADC0_ChannelSelect(ADC_POSINPUT_AIN10, ADC_NEGINPUT_GND);
        break;
    case ADC_CHANNEL_11:
        ADC0_ChannelSelect(ADC_POSINPUT_AIN11, ADC_NEGINPUT_GND);
        break;
    case ADC_CHANNEL_12:
        ADC0_ChannelSelect(ADC_POSINPUT_AIN12, ADC_NEGINPUT_GND);
        break;
    case ADC_CHANNEL_13:
        ADC0_ChannelSelect(ADC_POSINPUT_AIN13, ADC_NEGINPUT_GND);
        break;
    case ADC_CHANNEL_14:
        ADC0_ChannelSelect(ADC_POSINPUT_AIN14, ADC_NEGINPUT_GND);
        break;
    case ADC_CHANNEL_15:
        ADC0_ChannelSelect(ADC_POSINPUT_AIN15, ADC_NEGINPUT_GND);
        break;
    default:
        return 0;
    }

    // 启动转换
    ADC0_ConversionStart();

    // 等待转换完成
    while (!ADC0_ConversionStatusGet()) {
        // 等待转换完成
    }

    // 读取结果
    uint16_t result = ADC0_ConversionResultGet();

    return result;
}

// ADC HAL实例
const adc_hal_t adc_hal = {.init = adc_init, .get_value = adc_get_value};

以对象的方式对外提供adc_hal对象,主要包括初始化函数以及获取对应通道raw值的接口

其中

init主要是配置adc以及使能ADC0的时钟功能

get_value实现为,选择对应通道,开始转换,等待转换完成,然后转换结果,耗时12.5us

第二章 基于外设实现摇杆驱动

首先明确知道摇杆有x和y两轴,即对应两个adc通道,因此对应上下左右即代表y轴和x轴的adc值
实现代码:

// custom/hal/module/input/joystick.c
#include "joystick.h"

#include "custom/hal/peripheral/adc_hal.h"
#include "definitions.h"

// 摇杆初始化
static bool
joystick_init(joystick_device_t* joystick, uint8_t x_channel, uint8_t y_channel, const joystick_config_t* config)
{
    if (joystick == NULL) {
        return false;
    }
    adc_hal.init();

    joystick->x_channel = x_channel;
    joystick->y_channel = y_channel;

    // 设置默认配置
    if (config != NULL) {
        joystick->config = *config;
    }
    else {
        joystick->config.center_x  = 2048; // 默认中心值
        joystick->config.center_y  = 2048; // 默认中心值
        joystick->config.deadzone  = 100;  // 默认死区
        joystick->config.threshold = 200;  // 默认阈值
    }

    joystick->initialized = true;
    return true;
}

// 摇杆反初始化
static void joystick_deinit(joystick_device_t* joystick)
{
    if (joystick == NULL || !joystick->initialized) {
        return;
    }

    joystick->initialized = false;
}

// 获取摇杆位置
static joystick_position_t joystick_get_position(joystick_device_t* joystick)
{
    joystick_position_t position = {0, 0};

    if (joystick == NULL || !joystick->initialized) {
        return position;
    }

    // 获取X轴值
    position.x = adc_hal.get_value(joystick->x_channel);

    // 获取Y轴值
    position.y = adc_hal.get_value(joystick->y_channel);

    return position;
}

// 获取摇杆方向
static joystick_direction_t joystick_get_direction(joystick_device_t* joystick)
{
    if (joystick == NULL || !joystick->initialized) {
        return JOYSTICK_DIR_CENTER;
    }

    joystick_position_t pos = joystick_get_position(joystick);

    int16_t dx              = pos.x - joystick->config.center_x;
    int16_t dy              = pos.y - joystick->config.center_y;

    // 检查是否在死区内
    if ((dx * dx + dy * dy) < (joystick->config.deadzone * joystick->config.deadzone)) {
        return JOYSTICK_DIR_CENTER;
    }

    // 判断方向
    bool is_up    = dy < -joystick->config.threshold;
    bool is_down  = dy > joystick->config.threshold;
    bool is_left  = dx < -joystick->config.threshold;
    bool is_right = dx > joystick->config.threshold;

    if (is_up) {
        return JOYSTICK_DIR_UP;
    }
    else if (is_down) {
        return JOYSTICK_DIR_DOWN;
    }
    else if (is_left) {
        return JOYSTICK_DIR_LEFT;
    }
    else if (is_right) {
        return JOYSTICK_DIR_RIGHT;
    }

    return JOYSTICK_DIR_CENTER;
}

// 检查摇杆是否移动
static bool joystick_is_moved(joystick_device_t* joystick)
{
    if (joystick == NULL || !joystick->initialized) {
        return false;
    }

    joystick_position_t pos = joystick_get_position(joystick);

    int16_t dx              = pos.x - joystick->config.center_x;
    int16_t dy              = pos.y - joystick->config.center_y;

    return (dx * dx + dy * dy) > (joystick->config.threshold * joystick->config.threshold);
}

// 摇杆模块实例
const joystick_module_t joystick_module = {
    .init          = joystick_init,
    .deinit        = joystick_deinit,
    .get_position  = joystick_get_position,
    .get_direction = joystick_get_direction,
    .is_moved      = joystick_is_moved};


第三章 功能验证与测试

3.1 验证代码实现

    // 初始化摇杆
    joystick_config_t joystick_config = {.center_x = 2048, .center_y = 2048, .deadzone = 100, .threshold = 200};
    joystick_module.init(&joystick1, JOYSTICK1_X_CHANNEL, JOYSTICK1_Y_CHANNEL, &joystick_config);
    
    
    
// 测试摇杆功能
static void test_joystick(void)
{
    // 获取摇杆方向
    joystick_direction_t direction = joystick_module.get_direction(&joystick1);

    // 检查摇杆方向是否发生变化
    if (direction != last_joystick_direction) {
        last_joystick_direction = direction;

        const char* msg         = NULL;
        switch (direction) {
        case JOYSTICK_DIR_CENTER:
            msg = "Joystick: Center\r\n";
            break;
        case JOYSTICK_DIR_UP:
            msg = "Joystick: Up\r\n";
            break;
        case JOYSTICK_DIR_DOWN:
            msg = "Joystick: Down\r\n";
            break;
        case JOYSTICK_DIR_LEFT:
            msg = "Joystick: Left\r\n";
            break;
        case JOYSTICK_DIR_RIGHT:
            msg = "Joystick: Right\r\n";
            break;
        default:
            msg = "Joystick: Unknown\r\n";
            break;
        }

        serial_hal.write((uint8_t*)msg, strlen(msg));
    }
}


3.2 实现效果

当摇杆上下左右摇动时摇杆状态

image.png




关键词: EV76S68A     Joystick     ADC    

共1条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]