A MIDI controller demo based on STM32F401CDT6
This project is just a demonstration of using STM32F4 USB interface as a MIDI device. It receives and transmitt MIDI messages of the standard types:
- Note on and off
- Program Change
- Control Change
The implementation of the MIDI class for the STM32F401's USB Device interface is the same as that available in the STM32 MIDI Brain project (https://github.com/samjkent/stm32f4-midi-brain) with a small bug fix. Of this code, what I changed most was the user interface (files usbd_midi_if.c and usbd_midi_if.h). I included data structures and callback functions for receiving MIDI messages. I've also updated the functions for sending MIDI messages.
In the main program (file main.c) you can check how MIDI messages are read and written. The functions for this are:
- Sending MIDI messages:
MIDI_SendNote: sends a noteMIDI_SendProgramChangesends a Program Change message and,MIDI_SendControlChangesends a Controller Change message.
- For receiving, there are 3 Callbacks that must be defined by the user (they are initialy defined as
weak):MIDI_ReceivedNoteCallbackfor receiving a MIDI note.MIDI_ReceivedProgramChangeCallbackfor Program Change messages.MIDI_ReceivedControlChangeCallbackfor Controller Change messages.
The usbd_midi_if.h file defines the types of variables used by these functions (they are unions).
The source code project is built based on the ST libraries, using STM32CubeIDE version 1.16.0. For this demo I used the board
I'll explain below how you can compile the code so that you have a USB MIDI device for a microcontroller from the STM32F4 family.
The quickest way for you to test this code and use the USB MIDI Class is to import the project into your STM32CubeIDE workspace. You can do this with the Import Existing Projects into Workspace function in STM32CubeIDE.
I used version 1.16.0 of STM32CubeIDE.
If you already have a project in STM32CubeIDE and just want to use the MIDI class, I've included below a step-by-step guide on how to generate the project in STM32CubeIDE so that you can compile and test the code without having to import my project. I believe it works for any STM32F4 that has a USB Device interface using the CubeMX libraries.
- Use your project or, if you prefer, create a new project in STM32CubeIDE and run some simple firmware, such as a Blink LED, to make sure everything is working (clock configuration, debug interface and everything else).
- Using the Device Configuration Tool, in the Connectivity category, select
USB_OTG_FSand set it toDevice_Onlymode. In NVIC Settings, enable the USB interface interrupt. - In the Middleware and Software Packs category, select
USB_DEVICEand choose a Class For FS IP. I choseCustom Human Interface Device Class (HID). I believe that other classes can be chosen, as they will not be used. The MIDI class will be added manually in the sequence, so we'll baypass the class chosen here. In theParameter Settingsfields you can configure the number of interfaces (I've left the default value) and in Device Descriptor you can change the manufacturer, product and configuration strings. - Check the Clock Configuration in the Device Configuration Tool. The USB device requires a 48MHz clock. If you use an STM32F401 with a 25MHz cristal oscillator you can use the following clock configuration values:
5. PLL Source MUX: HSE
- PLL / M: 25
- PLL * N: 336
- PLL / P: 4
- PLL / Q: 7
- System Clock MUX: PLLCLK
- AHB Prescaler = 1
- Generate the code and save.
- Copy the MIDI folder in
Middlewares/ST/STM32_USB_Device_Library/Classto the respective folder in your project. - Add the
Middlewares/ST/STM32_USB_Device_Library/Class/MIDI/Incfolder to the list of includes in your project's properties. - Copy the
usbd_midi_if.candusbd_midi_if.hfiles that are in the/USB_DEVICE/Appfolder to the respective folder in your project. - Edit the
usb_device.cfile in the/USB_DEVICE/Appfolder. At the beginning, include the header for the user program interface:
/* USER CODE BEGIN Includes */
#include "usbd_midi_if.h" // Add this line.
/* USER CODE END Includes */Change the void MX_USB_DEVICE_Init(void) function to include:
/* USER CODE BEGIN USB_DEVICE_Init_PreTreatment */
if (USBD_Init(&hUsbDeviceFS, &FS_Desc, DEVICE_FS) != USBD_OK)
{
Error_Handler();
}
if (USBD_RegisterClass(&hUsbDeviceFS, &USBD_MIDI) != USBD_OK)
{
Error_Handler();
}
if (USBD_MIDI_RegisterInterface(&hUsbDeviceFS, &USBD_MIDI_fops) != USBD_OK)
{
Error_Handler();
}
if (USBD_Start(&hUsbDeviceFS) != USBD_OK)
{
Error_Handler();
}
return;
/* USER CODE END USB_DEVICE_Init_PreTreatment */With this code, the function returns before registering the chosen class with the Device Configuration Tool, so this is the baypass mantioned above.
You can now compile and test. When you connect your device to the computer's USB interface, it will be recognized as a MIDI device. In my case, the lsusb command in linux recognizes the microcontroller as 0483:5750 STMicroelectronics LED badge -- mini LED display -- 11x44. This is due to the PID and VID configured in the Device Descriptor, which are registered by the ST.
In linux you can use the program midisnoop to check the MIDI messages sent from the USB Device.