cancel
Showing results for 
Search instead for 
Did you mean: 

Problem with STM32F401RE as USB HID Device when using multi ADC with DMA

pablin235
Associate II

Hi!

Some time ago I did a post asking for a problem i had with this board used as a USB HID Device for a Midi Keyboard project when i wanted to read the value of a potentiometer with the ADC using DMA and send it as a Control Change command.

I solved the problem and i managed to send the control change command only when the knob is rotated, using only one channel and one potentiometer. (I'm also sending Note on and Note off commands with switches)

The configuration i used for only one channel that works is the following:

Spoiler
1a.png1b.png1c.png1d.png1e.png

And this is the code (please note that it has a lot to improve still, i'm just testing everything works for now, but with only one channel it's working)

#include "main.h"
#include "usb_device.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "usbd_hid.h"
#include "struct.h"
#include <stdlib.h>
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/
ADC_HandleTypeDef hadc1;
DMA_HandleTypeDef hdma_adc1;

/* USER CODE BEGIN PV */
extern USBD_HandleTypeDef hUsbDeviceFS;

uint32_t pot = 0;
uint32_t potAnterior = 0;
uint32_t potVar = 0;
const uint8_t potThreshold = 4;

//MIDI Message arrays: 4 Bytes
uint8_t midiNoteOn[7][4];
uint8_t midiNoteOff[7][4];
uint8_t midiControlChange[4];

uint8_t botonEstadoActual[7] = {0};
uint8_t botonEstadoAnterior[7] = {0};
const uint8_t botonTimeOut = 10;
uint8_t lastDebounceTime[7] = {0};
uint8_t botonTimer[7] = {0};

GPIO GPIO_Switch[7] = { {SW1_GPIO_Port,SW1_Pin},
						{SW2_GPIO_Port,SW2_Pin},
						{SW3_GPIO_Port,SW3_Pin},
						{SW4_GPIO_Port,SW4_Pin},
						{SW5_GPIO_Port,SW5_Pin},
						{SW6_GPIO_Port,SW6_Pin},
						{SW7_GPIO_Port,SW7_Pin}};

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_DMA_Init(void);
static void MX_ADC1_Init(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{

  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */
  //NOTE ON MESSAGE
  //Header Byte (Solo para MIDI USB)
  midiNoteOn[0][0] = 0x09; //Cable Number 0 (puede ser de 0 a F), el 9 es el Codigo (CIN) de NoteOn
  //Status Byte (El primer Nibble es el comando (NoteOn) y el segundo el Canal
  midiNoteOn[0][1] = 0x90; //9 es para NoteOn y 0 es el Canal 0
  //Data Byte 1 --> Es el numero de nota, valor entre 0 y 127 pues el MSB de un data byte siempre es 0
  midiNoteOn[0][2] = 0x24; //0x40 es 64
  //Data Byte 2 --> Es la velocidad, la dejamos en un valor constante por no medirla
  midiNoteOn[0][3] = 0x7F; //0x7F es 127, ponemos el maximo

  midiNoteOn[1][0] = 0x09; //Cable Number 0 (puede ser de 0 a F), el 9 es el Codigo (CIN) de NoteOn
  //Status Byte (El primer Nibble es el comando (NoteOn) y el segundo el Canal
  midiNoteOn[1][1] = 0x90; //9 es para NoteOn y 0 es el Canal 0
  //Data Byte 1 --> Es el numero de nota, valor entre 0 y 127 pues el MSB de un data byte siempre es 0
  midiNoteOn[1][2] = 0x26; //0x40 es 64
  //Data Byte 2 --> Es la velocidad, la dejamos en un valor constante por no medirla
  midiNoteOn[1][3] = 0x7F; //0x7F es 127, ponemos el maximo

  midiNoteOn[2][0] = 0x09; //Cable Number 0 (puede ser de 0 a F), el 9 es el Codigo (CIN) de NoteOn
  //Status Byte (El primer Nibble es el comando (NoteOn) y el segundo el Canal
  midiNoteOn[2][1] = 0x90; //9 es para NoteOn y 0 es el Canal 0
  //Data Byte 1 --> Es el numero de nota, valor entre 0 y 127 pues el MSB de un data byte siempre es 0
  midiNoteOn[2][2] = 0x28; //0x40 es 64
  //Data Byte 2 --> Es la velocidad, la dejamos en un valor constante por no medirla
  midiNoteOn[2][3] = 0x7F; //0x7F es 127, ponemos el maximo

  midiNoteOn[3][0] = 0x09; //Cable Number 0 (puede ser de 0 a F), el 9 es el Codigo (CIN) de NoteOn
  //Status Byte (El primer Nibble es el comando (NoteOn) y el segundo el Canal
  midiNoteOn[3][1] = 0x90; //9 es para NoteOn y 0 es el Canal 0
  //Data Byte 1 --> Es el numero de nota, valor entre 0 y 127 pues el MSB de un data byte siempre es 0
  midiNoteOn[3][2] = 0x29; //0x40 es 64
  //Data Byte 2 --> Es la velocidad, la dejamos en un valor constante por no medirla
  midiNoteOn[3][3] = 0x7F; //0x7F es 127, ponemos el maximo

  midiNoteOn[4][0] = 0x09; //Cable Number 0 (puede ser de 0 a F), el 9 es el Codigo (CIN) de NoteOn
  //Status Byte (El primer Nibble es el comando (NoteOn) y el segundo el Canal
  midiNoteOn[4][1] = 0x90; //9 es para NoteOn y 0 es el Canal 0
  //Data Byte 1 --> Es el numero de nota, valor entre 0 y 127 pues el MSB de un data byte siempre es 0
  midiNoteOn[4][2] = 0x2B; //0x40 es 64
  //Data Byte 2 --> Es la velocidad, la dejamos en un valor constante por no medirla
  midiNoteOn[4][3] = 0x7F; //0x7F es 127, ponemos el maximo

  midiNoteOn[5][0] = 0x09; //Cable Number 0 (puede ser de 0 a F), el 9 es el Codigo (CIN) de NoteOn
  //Status Byte (El primer Nibble es el comando (NoteOn) y el segundo el Canal
  midiNoteOn[5][1] = 0x90; //9 es para NoteOn y 0 es el Canal 0
  //Data Byte 1 --> Es el numero de nota, valor entre 0 y 127 pues el MSB de un data byte siempre es 0
  midiNoteOn[5][2] = 0x2D; //0x40 es 64
  //Data Byte 2 --> Es la velocidad, la dejamos en un valor constante por no medirla
  midiNoteOn[5][3] = 0x7F; //0x7F es 127, ponemos el maximo

  midiNoteOn[6][0] = 0x09; //Cable Number 0 (puede ser de 0 a F), el 9 es el Codigo (CIN) de NoteOn
  //Status Byte (El primer Nibble es el comando (NoteOn) y el segundo el Canal
  midiNoteOn[6][1] = 0x90; //9 es para NoteOn y 0 es el Canal 0
  //Data Byte 1 --> Es el numero de nota, valor entre 0 y 127 pues el MSB de un data byte siempre es 0
  midiNoteOn[6][2] = 0x2F; //0x40 es 64
  //Data Byte 2 --> Es la velocidad, la dejamos en un valor constante por no medirla
  midiNoteOn[6][3] = 0x7F; //0x7F es 127, ponemos el maximo

  //NOTE OFF MESSAGE
  //Header Byte (Solo para MIDI USB)
  midiNoteOff[0][0] = 0x08; //Cable Number 0 (puede ser de 0 a F), el 8 es el Codigo (CIN) de NoteOff
  //Status Byte (El primer Nibble es el comando (NoteOn) y el segundo el Canal
  midiNoteOff[0][1] = 0x80; //8 es para NoteOff y 0 es el Canal 0
  //Data Byte 1 --> Es el numero de nota, valor entre 0 y 127 pues el MSB de un data byte siempre es 0
  midiNoteOff[0][2] = 0x24; //0x40 es 64
  //Data Byte 2 --> Es la velocidad, la dejamos en un valor constante por no medirla
  midiNoteOff[0][3] = 0x7F; //0x7F es 127, ponemos el maximo

  midiNoteOff[1][0] = 0x08; //Cable Number 0 (puede ser de 0 a F), el 8 es el Codigo (CIN) de NoteOff
  //Status Byte (El primer Nibble es el comando (NoteOn) y el segundo el Canal
  midiNoteOff[1][1] = 0x80; //8 es para NoteOff y 0 es el Canal 0
  //Data Byte 1 --> Es el numero de nota, valor entre 0 y 127 pues el MSB de un data byte siempre es 0
  midiNoteOff[1][2] = 0x26; //0x40 es 64
  //Data Byte 2 --> Es la velocidad, la dejamos en un valor constante por no medirla
  midiNoteOff[1][3] = 0x7F; //0x7F es 127, ponemos el maximo

  midiNoteOff[2][0] = 0x08; //Cable Number 0 (puede ser de 0 a F), el 8 es el Codigo (CIN) de NoteOff
  //Status Byte (El primer Nibble es el comando (NoteOn) y el segundo el Canal
  midiNoteOff[2][1] = 0x80; //8 es para NoteOff y 0 es el Canal 0
  //Data Byte 1 --> Es el numero de nota, valor entre 0 y 127 pues el MSB de un data byte siempre es 0
  midiNoteOff[2][2] = 0x28; //0x40 es 64
  //Data Byte 2 --> Es la velocidad, la dejamos en un valor constante por no medirla
  midiNoteOff[2][3] = 0x7F; //0x7F es 127, ponemos el maximo

  midiNoteOff[3][0] = 0x08; //Cable Number 0 (puede ser de 0 a F), el 8 es el Codigo (CIN) de NoteOff
  //Status Byte (El primer Nibble es el comando (NoteOn) y el segundo el Canal
  midiNoteOff[3][1] = 0x80; //8 es para NoteOff y 0 es el Canal 0
  //Data Byte 1 --> Es el numero de nota, valor entre 0 y 127 pues el MSB de un data byte siempre es 0
  midiNoteOff[3][2] = 0x29; //0x40 es 64
  //Data Byte 2 --> Es la velocidad, la dejamos en un valor constante por no medirla
  midiNoteOff[3][3] = 0x7F; //0x7F es 127, ponemos el maximo

  midiNoteOff[4][0] = 0x08; //Cable Number 0 (puede ser de 0 a F), el 8 es el Codigo (CIN) de NoteOff
  //Status Byte (El primer Nibble es el comando (NoteOn) y el segundo el Canal
  midiNoteOff[4][1] = 0x80; //8 es para NoteOff y 0 es el Canal 0
  //Data Byte 1 --> Es el numero de nota, valor entre 0 y 127 pues el MSB de un data byte siempre es 0
  midiNoteOff[4][2] = 0x2B; //0x40 es 64
  //Data Byte 2 --> Es la velocidad, la dejamos en un valor constante por no medirla
  midiNoteOff[4][3] = 0x7F; //0x7F es 127, ponemos el maximo

  midiNoteOff[5][0] = 0x08; //Cable Number 0 (puede ser de 0 a F), el 8 es el Codigo (CIN) de NoteOff
  //Status Byte (El primer Nibble es el comando (NoteOn) y el segundo el Canal
  midiNoteOff[5][1] = 0x80; //8 es para NoteOff y 0 es el Canal 0
  //Data Byte 1 --> Es el numero de nota, valor entre 0 y 127 pues el MSB de un data byte siempre es 0
  midiNoteOff[5][2] = 0x2D; //0x40 es 64
  //Data Byte 2 --> Es la velocidad, la dejamos en un valor constante por no medirla
  midiNoteOff[5][3] = 0x7F; //0x7F es 127, ponemos el maximo

  midiNoteOff[6][0] = 0x08; //Cable Number 0 (puede ser de 0 a F), el 8 es el Codigo (CIN) de NoteOff
  //Status Byte (El primer Nibble es el comando (NoteOn) y el segundo el Canal
  midiNoteOff[6][1] = 0x80; //8 es para NoteOff y 0 es el Canal 0
  //Data Byte 1 --> Es el numero de nota, valor entre 0 y 127 pues el MSB de un data byte siempre es 0
  midiNoteOff[6][2] = 0x2F; //0x40 es 64
  //Data Byte 2 --> Es la velocidad, la dejamos en un valor constante por no medirla
  midiNoteOff[6][3] = 0x7F; //0x7F es 127, ponemos el maximo

 //CONTROL CHANGE MESSAGE POT
  //Header Byte (Solo para MIDI USB)
  midiControlChange[0] = 0x0B; //Cable Number 0 (puede ser de 0 a F), el B es el Codigo (CIN) de ControlChange
  //Status Byte (El primer Nibble es el comando (ControlChange) y el segundo el Canal
  midiControlChange[1] = 0xB0; //B es para ControlChange y 0 es el Canal 0
  //Data Byte 1 --> Es el numero de control 0 y 127 pues el MSB de un data byte siempre es 0
  midiControlChange[2] = 0x07; //0x07 es 7
  //Data Byte 2 --> Es el valor que se envia moviendo el pote
  midiControlChange[3] = 0x00; //Inicializamos en 0

  uint8_t retorno = 0;
  int i = 0;
  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_USB_DEVICE_Init();
  MX_ADC1_Init();
  /* USER CODE BEGIN 2 */
  HAL_ADC_Start_DMA(&hadc1, &pot, 1);
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */
	  for(i=0 ; i<7 ; i++)
	  {
		  botonEstadoActual[i] = HAL_GPIO_ReadPin(GPIO_Switch[i].port, GPIO_Switch[i].pin);
		  botonTimer[i] = HAL_GetTick() - lastDebounceTime[i];
		  if(botonTimer[i] > botonTimeOut)
		  {
			  if(botonEstadoActual[i] != botonEstadoAnterior[i])
			  {
				  lastDebounceTime[i] = HAL_GetTick();
				  if(botonEstadoActual[i] == 0)
				  {
					  while(((USBD_HID_HandleTypeDef *)hUsbDeviceFS.pClassData)->state==USBD_HID_BUSY)
					  {
					  }
					  retorno = USBD_HID_SendReport(&hUsbDeviceFS,midiNoteOn[i],4);
				  }
				  else
				  {
					  while(((USBD_HID_HandleTypeDef *)hUsbDeviceFS.pClassData)->state==USBD_HID_BUSY)
					  {
					  }
					  retorno = USBD_HID_SendReport(&hUsbDeviceFS,midiNoteOff[i],4);
				  }
				  botonEstadoAnterior[i] = botonEstadoActual[i];
			  }
		  }
	  }

	  potVar = abs(pot - potAnterior);
	  if (potVar > potThreshold)
	  {
		  if (pot != potAnterior)
		  {
			  midiControlChange[3] = (pot*127)/255;
			  while(((USBD_HID_HandleTypeDef *)hUsbDeviceFS.pClassData)->state==USBD_HID_BUSY)
			  {
			  }
			  retorno = USBD_HID_SendReport(&hUsbDeviceFS,midiControlChange,4);
		  }
		  potAnterior = pot;
	  }

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

The problem comes when i tried to add another potentiometer using 2 channels the same way, the only difference in the configuration was adding another analog input and another channel:

Spoiler
2a.png2b.png

And in the code i only changed the variable pot that i used as a buffer for the DMA to an array of 2 elements (pot[2])

The DMA initialization is the following:

HAL_ADC_Start_DMA(&hadc1, pot, 2);

Then i copypasted the part that sends Control Change to use 2 potentiometers instead of 1 (changing the variables used in the first code to 2 elements arrays)

	  potVar[0] = abs(pot[0] - potAnterior[0]);
	  if (potVar[0] > potThreshold)
	  {
		  if (pot[0] != potAnterior[0])
		  {
			  midiControlChange1[3] = (pot[0]*127)/255;
			  while(((USBD_HID_HandleTypeDef *)hUsbDeviceFS.pClassData)->state==USBD_HID_BUSY)
			  {
			  }
			  retorno = USBD_HID_SendReport(&hUsbDeviceFS,midiControlChange1,4);
		  }
		  potAnterior[0] = pot[0];
	  }

	  potVar[1] = abs(pot[1] - potAnterior[1]);
	  if (potVar[1] > potThreshold)
	  {
		  if (pot[1] != potAnterior[1])
		  {
			  midiControlChange2[3] = (pot[1]*127)/255;
			  while(((USBD_HID_HandleTypeDef *)hUsbDeviceFS.pClassData)->state==USBD_HID_BUSY)
			  {
			  }
			  retorno = USBD_HID_SendReport(&hUsbDeviceFS,midiControlChange2,4);
		  }
		  potAnterior[1] = pot[1];
	  }

It compiles OK and when i run it, i test it with a MIDI Monitor and the problem is that the potentiometer connected to Channel 0 (PA0) is sending Control Change all the time, and not just when i rotate the knob, and if i rotate nothing changes, and the second potentiometer seems to be doing nothing because the monitor only detects Channel 7 assigned to Channel 0

2c.png

So i think i must have an issue with the configuration of the ADC or the DMA, or maybe an issue in the code i'm not seeing.

I hope someone can help me

Thank you

1 REPLY 1
pablin235
Associate II

I just noticed I had the 2 channels configured as Channel 0 (as you can see in the picture i uploaded)

I changed that and now the 2 potentiometers sends differents values when moving the knob both the problem that sends all the time persists and it doesn't matter wich of the 2 potentiometers i move, the MIDI monitor always receive in MIDI channel 7 instead of MIDI channel 7 for one potentiometer (adc Channel 0) and MIDI channel 8 for the other potentiometer (adc Channel 1)