Hypnotriod / midi-box-stm32

stm32 usb midi driver demo application
49 stars 6 forks source link

Troubles porting to STM32H743 #4

Closed davidedelvento closed 1 year ago

davidedelvento commented 1 year ago

I'm following your instructions to port to said MCU (actually on a Nucleo board, for now), and it's not working.

  1. There is no HAL_PCDEx_PMAConfig() to replace in USB_DEVICE/Target/usbd_conf.c In fact, unlike in stm32f1xx_hal_pcd_ex.c, there is no HAL_PCDEx_PMAConfig() definition in Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_pcd_ex.c -- so I simply skipped that step, thinking that perhaps on this board that is unnecessary.

  2. In USB_DEVICE/App/usb_device.c I also had to change the following

if (USBD_RegisterClass(&hUsbDeviceFS, &USBD_HID) != USBD_OK)       // replace this line
if (USBD_RegisterClass(&hUsbDeviceFS, &USBD_MIDI) != USBD_OK)     // with this one 

By doing so, USBD_MIDI_GetState(&hUsbDeviceFS) is never MIDI_IDLE so I can't send out packets if I do this check. On the other hand, if I don't and attempt to send them out anywhere, nothing seems to happen.

Looking at the available MIDI devices from the system where I connected the board, I see an STM32 thing available as MIDI Output (which is not what I want) but nothing as MIDI Input.

Do you have any insight on what might be going wrong here?

Thanks in advance for that and thanks for posting such a nice project out there!

Hypnotriod commented 1 year ago

Did you specify the MIDI_IN_PORTS_NUM, MIDI_OUT_PORTS_NUM macros in main.h ?

davidedelvento commented 1 year ago

Did you specify the MIDI_IN_PORTS_NUM, MIDI_OUT_PORTS_NUM macros in main.h ?

Of course! I followed closely your instructions:

$ grep MIDI ./Core/Inc/main.h
#define MIDI_IN_PORTS_NUM   0x01 // Specify input ports number of your device
#define MIDI_OUT_PORTS_NUM  0x03 // Specify output ports number of your device
Hypnotriod commented 1 year ago

I guess ST have some differences in usb driver for H7 family. I have no such devices in my hands, so there is not much I can help here. Can you attach your test project, so I'll check it?

davidedelvento commented 1 year ago

I guess ST have some differences in usb driver for H7 family.

Yes, that was my conclusion too. I was hoping to get some insight about that from you...

I have no such devices in my hands, so there is not much I can help here.

Yes, I wish they had an emulator where you could run the code pretending you had the necessary hardware. It would ease development so much!!

Thank you so much for the offer to check my code. Here it is, as you can see pretty basic

blink.zip

davidedelvento commented 1 year ago

Looking more closely at your code, I noticed that in your router example you use quite a lot UART from the USART interface.

I find that confusing since AFAIK the UART/USART interface should not be needed in the USB for neither HID nor MIDI class devices. I'm quite a novice with USB though, so I may be missing something?

As you may have noticed if you looked at my code, I don't do anything with UART/USART (there was an autogenerated MX_USART3_UART_Init() in the Cube project I started with, but I had to comment it out since it never returned and made the code hang). Perhaps that's the problem?

Also, changing a bit of the initialization (removing ETH which I don't use and was autogenerated) and using an Android MIDI debugger (MIDI Scope, see screenshot below), I was able to see the STM32 as a MIDI device, with the attached information. In such circumstances, the USBD_MIDI_GetState(&hUsbDeviceFS) even became MIDI_IDLE, and from the STM32 it seemed to send the messages correctly!!

Unfortunately, the message was never received by the Android MIDI Scope and I suspect it was actually never sent out of the Nucleo board (I have used MIDI Scope in other circumstances and I've found it to be quite reliable)

Screenshot_20221125-131930

davidedelvento commented 1 year ago

I was able to see the STM32 as a MIDI device, with the attached information. In such circumstances, the USBD_MIDI_GetState(&hUsbDeviceFS) even became MIDI_IDLE

Clarification: the MIDI device appeared not after the call to MX_USB_DEVICE_Init() as I expected but after the first call to USBD_MIDI_GetState(&hUsbDeviceFS) (which did not return MIDI_IDLE) and the subsequent 0.5 sec wait. Subsequent calls to USBD_MIDI_GetState(&hUsbDeviceFS) returned MIDI_IDLE.

Moreover not one, but three identical "STMicroelectronics STM32 Human Interface Device" appeared in the Android MIDI Scope, not sure why the device registered itself not one, not two, but three times.

Hypnotriod commented 1 year ago

I have looked at your code and what I've found:

uint8_t msg_on[3] = { 0x90, note_sequence[note_pos], 127}; // is not suit
uint8_t msg_on[4] = { 0x09, 0x90, note_sequence[note_pos], 127}; // proper message to cable 0

It looks like the midi message, but it is not the usb-midi message. You should read the midi10.pdf manual first, to understand more what is going under hood of usb-midi protocol.
The proper usb-midi message/event consists of 4 bytes (first byte is combination of midi cable and message), and when you are sending the report with USBD_MIDI_SendReport, it should be 64 bytes long (because of MIDI_EPIN_SIZE you are passing next) and consist of up to 16 messages (otherwise the rest should be filled with zeros)
And about the case when 3 devices are shown instead of one:

#define MIDI_IN_PORTS_NUM   0x01 // Specify input ports number of your device
#define MIDI_OUT_PORTS_NUM  0x03 // Specify output ports number of your device

Literary means number of input ports and output ports of your device. Each port is related to midi cable.

Hypnotriod commented 1 year ago

Anyway, I have updated the README, where I've tried to explain a bit more about the driver usage and remove some misconceptions I made.
I hope it will help you more :)

davidedelvento commented 1 year ago

Thank you so much for pointing me to MIDI-USB having different packets than pure MIDI.

For the number of ports, I had figured that out but did not want to flood this ticket (and your patience) with lots of minutia, so I had that clear and you confirmed that.

In the past, I used USB-MIDI on other embedded platforms and those required only the MIDI packet itself (rather than the whole USB-MIDI packet) to be created on the application side, so not only I never bothered to look, but I never thought about looking! Now I have done it and rectified my code accordingly. At first, the effect was still the same, i.e. absolutely nothing was visible in the Android MIDI Scope

So, to investigate better what was going on, I decided to deal with the hassle of setting up another computer as the host for the STM32 board on the USB-MIDI side, rather than an Android device (I was not sure if it was okay, and so I avoided using the same machine for controlling the board from a programming point of view and for being its USB-MIDI host). I used wireshare, tshark and usbmon to fully capture the USB traffic. In fact the traffic is there and MIDI computer applications are able to recognize it and act accordingly (i.e. play notes).

However, both lsusb and the MIDI computer applications identify the board as STM32 Human Interface rather than a fully MIDI device, so perhaps that's what is confusing Android? I am not sure, it could be just the identifying string rather than the protocol, but perhaps worth investigating to make this project fully MIDI complaint, if it isn't already.

Regarding the length of the packets, I looked in depth at the your code and I think I now understood that part too. I am still confused why have you made that decision though. To me it looks like you are trying to increase bandwidth at the expense of latency, especially in the event in which there aren't many MIDI packets to send (route in your demonstration). Given the characteristics of MIDI and USB, it appears that the other trade-off (sending packages immediately, to improve latency at the expense of bandwidth) seemed better, since USB has already much more bandwidth than most MIDI applications need, and if anything in most cases latency is the issue. Am I missing something?

As far as I am concerned, this issue could be closed, unless you would like to further discuss/investigate these two things (latency and enumeration as MIDI device rather than HID).

Thanks again for your patience with me and perseverance with the issue.

Hypnotriod commented 1 year ago

It is great!
About HAL_PCDEx_PMAConfig call, this is configuration of USB FIFO RAM for the usb endpoints. I guess not all STM32 devices have this thing. So, if you are saying that it is working without that configuration, I need to mention it in README as well.
About "sending packages immediately" - actually, program jumps to try_to_send_usb_midi_report label all the time, and if there is something to send and midi is idle, so it sends report immediately, no latency. But you can even win sometimes because of several messages may simultaneously come from different input ports (I have 3 of them)
One thing I missed that I shouldn't send whole 64 bits all the time, even if there is only one event packet to send, because MIDI_EPIN_SIZE aka wMaxPacketSize is the maximum data packet size, but not the only one. So, it will be nice if can test and prove it :)

Hypnotriod commented 1 year ago

And about STM32 Human Interface - it is just the name of your device which you can change in the CubeMX as well as vendor id, product id, etc. It is under usb_desc.c file

#define USBD_VID     1155
#define USBD_LANGID_STRING     1033
#define USBD_MANUFACTURER_STRING     "STMicroelectronics"
#define USBD_PID_FS     22315
#define USBD_PRODUCT_STRING_FS     "STM32 MIDI interface"
#define USBD_CONFIGURATION_STRING_FS     "MIDI Config"
#define USBD_INTERFACE_STRING_FS     "MIDI Interface"