felis / UHS30

For information about the project see README below
GNU General Public License v2.0
133 stars 39 forks source link

Support to work with the official Arduino STM32 core #57

Open rtek1000 opened 4 years ago

rtek1000 commented 4 years ago

I tried to compile (STM32F103C8T6) but the first error that occurred seems to be relative to the interrupt register definition.

c:\arduino ide\arduino-1.8.10-windows-portable\portable\sketchbook\libraries\dyn_swi\dyn_swi.h:106:2: error: #error SWI_IRQ_NUM not defined (CMSIS)

error SWI_IRQ_NUM not defined (CMSIS)

https://github.com/stm32duino/Arduino_Core_STM32

I found this post that might help someone identify that SPI port interrupt register:

https://os.mbed.com/forum/mbed/topic/26688/?page=1#comment-50909

Maybe it will be interesting for some MBED developers.

I explain how to extend the SPISlave class for use interrupts on STM32 MCU. With this solution we do not need to change anything in the basic SPISlave class.

First step is common and strighforward. We create instance of SPISlave class and do some configuration (format, frequency, ...). Next we setup SPI CR2 register to enable interrupts. Especially:

  • TXEIE bit to enable 'Tx buffer empty' interrupt - RXNEIE bit to enable 'RX buffer not empty' interrupt

and at last we install own ISR routine for SPI interrupts.

Some explanaition.

Because SPI work in full duplex mode we must have 'filler' (empty) character. I use null byte as filler. Send or receive null byte means that there is nothing to send (or receive). As a consequence this protocol cant be used for binary data. Null bytes must be encoded/decoded on both side (escape sequence processing).

My ISR routine cooperate with two buffers and simple API: - saveInBuf save received characters in external buffer - char2send get next byte to send from transmit buffer

Local (static) buffer 'intBuf' is used in the case when main receive buffer is (temporarily) in use and received character can't be saved.

I run test program on Nucleo-STM32F103RB (64 MHz HSI) connected to Orange Pi Lite (Armbian Linux). On Orange Pi side I use two transmission buffers and Timer interrupt to check for pending transmission (once per 100 us). I configure SPI on both side in 8 bit mode and 16 Mhz frequency.

extern uint32_t Default_Handler;
SPI_TypeDef *cspi = SPI2;
bool blockReader, blockWriter;

void setIRQvector(IRQn_Type it, uint32_t fa)
{
    uint32_t dh;
    if(it >= 0)
    {
        if(!fa)
        {
            NVIC_DisableIRQ(it);
            dh = (uint32_t) Default_Handler;
            NVIC_SetVector(it, dh);
        }
        else
        {
            NVIC_SetVector(it, fa);
            NVIC_EnableIRQ(it);
        }
    }
}

#define TXEIE_MASK  0x80
#define RXNEIE_MASK 0x40

void enableSPIinterrupt(bool enable)
{
    uint32_t isr = 0;
    if(enable)
    {
        isr = (uint32_t) rwSPIservice;
        cspi->CR2 |= (TXEIE_MASK | RXNEIE_MASK);
    }
    else
        cspi->CR2 &= ~(TXEIE_MASK | RXNEIE_MASK);
    setIRQvector(SPI2_IRQn, isr);
}

#define RXNE_MASK   1
#define TXE_MASK        2

void rwSPIservice(void)
{
    static char intBuf[16];
    static uint16_t cInBuf;
    int tc;

    if(cspi->SR & RXNE_MASK && cInBuf < sizeof(intBuf))
    {
        tc = cspi->DR;
        if(tc)
            intBuf[cInBuf++] = tc;
    }
    if(cInBuf > 0 && !blockReader)
    {
        short saved = saveInBuf(intBuf, cInBuf);
        if(saved > 0)
        {
            cInBuf -= saved;
            if(cInBuf > 0)
                memmove(intBuf, intBuf + saved, cInBuf);
        }
    }
    if(cspi->SR & TXE_MASK)
    {
        tc = 0;
        if(!blockWriter)
        {
            tc = char2send();
            if(tc < 0)
                tc = 0;
        }
        cspi->DR = tc;
    }
}
xxxajk commented 4 years ago

SWI isn't what you think it is. For an unknown ARM board, you need to identify an unused NVIC IRQ, and steal it for UHS use. Example, if NVIC irq 20 is not being used, add the following to the very top of your sketch:

#define SWI_IRQ_NUM 20

Secondly, it might not play nice with an RTOS, yet. That's something that needs work.

Finally, it might not play nice with fast rate watchdog timers, depending on the drivers used.

rtek1000 commented 4 years ago

Should the UHS_HID_RAW.ino example work with Arduino Due?

I am trying to understand how it works in an ARM, so try to adapt to STM32, but something is strange.

On the Arduino Mega 2560 all keys are printed the moment they are pressed. But in Arduino Due only a few keys work this way. The other keys are stored, and are printed when any of those normal keys are pressed.

It seems as if a buffer was added to wait for a specific key before dealing with the other keys.

rtek1000 commented 4 years ago

Is there any prediction for this library to stop being Extremely Alpha?

This library has been in development for over 5 years, and appears to be stable.

Other developers are not seeing this description in a positive way:

Just a quick note from the README:

Pre-release of USB Host Library version 3.0. No technical support offered at this time.

This set of libraries is EXTREMELY ALPHA!

https://github.com/stm32duino/Arduino_Core_STM32/issues/768

JuniorJPDJ commented 4 years ago

Have you managed to make it work reliable with STM32F103C8T6? I'm buying this uC and UHS as host. I need both - host and device.

rtek1000 commented 4 years ago

Have you managed to make it work reliable with STM32F103C8T6? I'm buying this uC and UHS as host. I need both - host and device.

Sorry, I'm not working with the MAX3421E at the moment. I am using STM32F407VG and STM32CubeIDE. This F407 has 2 USB ports (OTG), it can operate as a host and device. The STM32CubeIDE library can operate as MSC, HID, CDC etc. Less adaptations, smaller PCB size, less interference with external communication. The performance of pen drive writing surprised me a lot.

See all the videos, it's worth: https://www.youtube.com/watch?v=rI3yBmnfAZU&list=PLnMKNibPkDnFFRBVD206EfnnHhQZI4Hxa

You may want to download the material to accompany the videos: https://www.st.com/content/st_com/en/support/learning/stm32-education/stm32-moocs/STM32-USB-training.html

Good luck!

xxxajk commented 4 years ago

Hello guys. Right now I am very busy with my client, inventing new hardware for products. I am very welcome to pull requests. Just please follow the code style, and recommended tools for best results and least amount of having to reformat, etc.

Platforms and On-chip USB brief info: If you look at any of the other ARM supported boards (PJRC Teensy) just use those as a template, and provide the same basic methods plus anything additional. CMSIS ISR stuff is already supported. You don't have to sort out much of the NVIC stuff. There are three base MCU flavors, Atmel mega, ARM and MIPS. Atmel is the only one that allows reentrant IRQ, so ARM and MIPS do the same basic functionality by triggering another hardware ISR using the SWI class stuff. DUE will already work but with MAX3421e, if you want to go ahead and support the native USB, go ahead.

More about On-chip USB, operating philosophy: Additional methods won't normally be used by the user but should be declared public, in the event that someone might find them useful. Same goes for variables. The thinking here is that if you want to use it as a base class, it is MUCH easier to allow public access for debugging outside of the class. The same goes for any drivers. Just make sure that they source from one or more of the provided base classes, or if there is some kind of new class (is there?) use any of the current bases as a template.

View from the library user: The entire point of how UHS3 operates is to have the least amount of code in the sketch to perform basic tasks, yet allow the library user to use drivers as a parent class to extend a driver. This eliminates the user having to "think about" things like scheduling when to perform USB housekeeping events. This is handled by the system's USB hardware, which as far as I can see provides a 1ms timer (or similar) on every single one. USE IT and set the priority higher than the SWI IRQ and you won't have any issues. Please make all code load as header files. This allows everything that is needed to be included into the actual sketch. Why? Read on!

Actual development case: Fun fact here is that you could actually write an entire driver fully within a sketch by sub-classing it, and once you are really happy with how it works, move it to the library, add examples, and do a pull request. Let's see any other library do that... Haven't found any that do it well, or at all.

More questions? Ask.