0
\$\begingroup\$

I can't share the schematics as they are not mine, I'm trying to add an analog thumbstick (4 pins, Vcc/GND/x/y) to my atmega32u4 powered keyboard.

The thumbstick pins are connected to pins PB4 (ADC11) and PB5 (ADC12) on the atmega chip (I did a continuity test to be sure).

I did do some measurements with my multimeter, so here are the facts I gathered:

  • AVcc <> GND : 5V
  • When the analog joystick is plugged in:
    • GND <> PB4: 2.5V (and it fluctuates when I move the joystick)
    • GND <> PB5: 2.5V (same)
  • When the analog joystick is unplugged:
    • GND <> PB4: 0V
    • GND <> PB5: 0V

I always get 1023, even when the joystick is not plugged in !

I tried the following methods, which all yield the same result:

  • manually settings the bits in the registers (ADCSRA, ADMUX)
  • using LUFA
  • using QMK's adc_read helper function

Here is the code I used (which I hacked from the LUFA VirtualSerial) example (original can be found here); I guess the interesting parts are in the my_adc_init & my_adc_read function.

#include "VirtualSerial.h"
#include <LUFA/Drivers/Peripheral/ADC.h>
#include "../../../../LUFA/Drivers/Peripheral/AVR8/ADC_AVR8.h"

void my_adc_init(void)
{
    ADC_Init(ADC_SINGLE_CONVERSION | ADC_PRESCALE_32);
    ADC_SetupChannel(11); // Y
    ADC_SetupChannel(12); // X
}


uint16_t my_adc_read(void)
{
    ADC_StartReading(ADC_REFERENCE_INT2560MV | ADC_CHANNEL11);
    while (!(ADC_IsReadingComplete()));
    return ADC_GetResult();
}

USB_ClassInfo_CDC_Device_t VirtualSerial_CDC_Interface =
{
    .Config =
    {
        .ControlInterfaceNumber   = INTERFACE_ID_CDC_CCI,
        .DataINEndpoint           =
        {
            .Address          = CDC_TX_EPADDR,
            .Size             = CDC_TXRX_EPSIZE,
            .Banks            = 1,
        },
        .DataOUTEndpoint =
        {
            .Address          = CDC_RX_EPADDR,
            .Size             = CDC_TXRX_EPSIZE,
            .Banks            = 1,
        },
        .NotificationEndpoint =
        {
            .Address          = CDC_NOTIFICATION_EPADDR,
            .Size             = CDC_NOTIFICATION_EPSIZE,
            .Banks            = 1,
        },
    },
};

static FILE USBSerialStream;

int i = 0;

int main(void)
{
    SetupHardware();

    CDC_Device_CreateStream(&VirtualSerial_CDC_Interface, &USBSerialStream);

    GlobalInterruptEnable();

    for (;;)
    {
        CDC_Device_ReceiveByte(&VirtualSerial_CDC_Interface);

        CDC_Device_USBTask(&VirtualSerial_CDC_Interface);
        USB_USBTask();
        if (++i % 50 == 0)
        {
            i = 0;
            fprintf(&USBSerialStream, "w00t: %d\r\n", my_adc_read());
        }
    }
}

void SetupHardware(void)
{
#if (ARCH == ARCH_AVR8)
    /* Disable watchdog if enabled by bootloader/fuses */
    MCUSR &= ~(1 << WDRF);
    wdt_disable();

    /* Disable clock division */
    clock_prescale_set(clock_div_1);
#elif (ARCH == ARCH_XMEGA)
    /* Start the PLL to multiply the 2MHz RC oscillator to 32MHz and switch the CPU core to run from it */
    XMEGACLK_StartPLL(CLOCK_SRC_INT_RC2MHZ, 2000000, F_CPU);
    XMEGACLK_SetCPUClockSource(CLOCK_SRC_PLL);

    /* Start the 32MHz internal RC oscillator and start the DFLL to increase it to 48MHz using the USB SOF as a reference */
    XMEGACLK_StartInternalOscillator(CLOCK_SRC_INT_RC32MHZ);
    XMEGACLK_StartDFLL(CLOCK_SRC_INT_RC32MHZ, DFLL_REF_INT_USBSOF, F_USB);

    PMIC.CTRL = PMIC_LOLVLEN_bm | PMIC_MEDLVLEN_bm | PMIC_HILVLEN_bm;
#endif

    /* Hardware Initialization */
    USB_Init();
    my_adc_init();
}


void EVENT_USB_Device_Connect(void)
{
}

void EVENT_USB_Device_Disconnect(void)
{
}

void EVENT_USB_Device_ConfigurationChanged(void)
{
    CDC_Device_ConfigureEndpoints(&VirtualSerial_CDC_Interface);
}

void EVENT_USB_Device_ControlRequest(void)
{
    CDC_Device_ProcessControlRequest(&VirtualSerial_CDC_Interface);
}

void EVENT_CDC_Device_ControLineStateChanged(USB_ClassInfo_CDC_Device_t *const CDCInterfaceInfo)
{
    bool HostReady = (CDCInterfaceInfo->State.ControlLineStates.HostToDevice & CDC_CONTROL_LINE_OUT_DTR) != 0;

    (void)HostReady;
}

Disclaimer, I'm very new to all of this, trying to learn as I go (:

\$\endgroup\$
  • \$\begingroup\$ No schematics, bad luck then. What is on AREF pin? Why are you using 2.56V internal reference if your ADC inputs go well beyond that to 5V? Try changing to AVCC as reference. \$\endgroup\$ – Justme May 12 at 9:59
  • \$\begingroup\$ I know :( AREF is connected to GND (tested with a multimeter). I tried changing it to ADC_REFERENCE_AVCC but I get the same result. But if that was the issue, wouldn't I get a reading of 0 when there is nothing plugged in ? \$\endgroup\$ – pyrho May 12 at 14:09
  • \$\begingroup\$ No it must not be grounded as it is the conversion reference. Selecting AVCC as reference won't work if AREF pin is connected to ground externally. Reading when there is nothing plugged in will not be 0 as the input is floating at a potetial you don't know. \$\endgroup\$ – Justme May 12 at 14:18
  • \$\begingroup\$ The datasheet states on page 304: "If no external voltage is applied to the AREF pin, the user may switch between AVCC and 2.56V as reference selection.". And wouldn't using the Internal Vref worked, even if it won't be as accurate for my reading between 0 and 5v . Either way, I cannot modify the PCB :( So I'd like to find a way to deal with this, with my ARef pin grounded. \$\endgroup\$ – pyrho May 12 at 14:47
  • \$\begingroup\$ Ground is 0V so grounding AREF means applying 0V as external reference. So you may not switch between any internal reference. Selecting AVCC or 2.56V reference will connect it to AREF node so in your case internal reference is shorted to ground via AREF pin. Only way to use ADC is to modify the device so that AREF pin is not grounded. Cut a PCB trace, unsolder the pin and lift it up, or if DIP device, cut the pin or add IC sockets so that AREF does not go to PCB. Or stop using ADC and determine the joystick resistance via time it takes to charge a capacitor. \$\endgroup\$ – Justme May 12 at 15:23
1
\$\begingroup\$

AREF is reference for conversion, it must not be grounded or all readings above 0V will be 1023.

\$\endgroup\$
  • \$\begingroup\$ That was the issue !!!! I lifted the Aref pin and I now get proper readings using ADC_REFERENCE_AVCC , thank you so much (: \$\endgroup\$ – pyrho May 12 at 20:39

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.