embassy-rs / embassy

Modern embedded framework, using Rust and async.
https://embassy.dev
Apache License 2.0
5.57k stars 773 forks source link

embassy-stm32 on STM32F1: SPI1 malfunction with "alternate" pins - how to disable JTAG TRACESWO/NJRST? #1476

Open janschiefer opened 1 year ago

janschiefer commented 1 year ago

Hello,

I'm currently debugging a particularly nasty and not too well documented issue with SPI on STM32F1 MCUs and embassy-stm32.

The basic problem is, I have a board design where I use the "alternate" PB3, PB4 and PB5 pins for the SPI1 peripherial with the STM32F103 chip instead of the "primary" pins PA5, PA6, PA7.

image

Under any circumstances, there just was no output on MOSI, MISO or SCK pins whatsoever.

I could not get them working on Rust and started fiddling around with the oscilloscope and the "official" STM32 HAL in C and with STM32CubeMX.

The problem seems that there are the SYS_JTD_TRACESWO and SYS_NJTRST pin confugurations for PB3 and PB4 that are unused most of the time in practice, but seem to be enabled somehow by default and messing with SPI1 configuration.

After initial failure to get SPI1 with the official C HAL as well, I found out ( with "inofficial" documentation ) that you actually either have to

disable SWD debugging entirely with __HAL_AFIO_REMAP_SWJ_DISABLE()

or

"disable JTAG, but enable SWD?!" with __HAL_AFIO_REMAP_SWJ_NOJTAG()

( --> https://codedocs.xyz/polfeliu/cantata/group__GPIOEx__AFIO__AF__REMAPPING.html is not very helpful...)

When I use either function in C before SPI1 configuration, SPI1 with alternate pin mapping works flawlessly ( and with the second option, I can still use SWD ).

These functions actually modify the "JTAG/SWD alternate function remapping" bits in the AFIO register. See page 176 here: https://www.st.com/resource/en/reference_manual/cd00171190-stm32f101xx-stm32f102xx-stm32f103xx-stm32f105xx-and-stm32f107xx-advanced-arm-based-32-bit-mcus-stmicroelectronics.pdf

Is there actually a method to disable these JTAG pins with embassy/embassy-stm32 on STM32F1, so SPI1 is usable with the "alternate" pins?

janschiefer commented 1 year ago

Ok. I fixed this by manually configuring the AFIO register and SPI1 remap register.

    let p = embassy_stm32::init( ... );

    unsafe {
        embassy_stm32::pac::AFIO.mapr().modify(|w| {

            w.set_swj_cfg(0b0000_0010); // this is equal to __HAL_AFIO_REMAP_SWJ_NOJTAG() in C
            w.set_spi1_remap(true);

        });

    }

    let spi = Spi::new( ...);

Ay chance this be integrated into the embassy SPI configuration for STM32F1 code?

Dirbaio commented 1 year ago

STM32F1 AFIO is not supported yet unfortunately. The F1's GPIO remapping works differently to all the other families, this is why it has seen less attention.

The first step would be to add the F1 pin remap info to https://github.com/embassy-rs/stm32-data, then once we have that embassy-stm32 could autogenerate the right traits etc for it to work automatically like in the other families.

If this is something you want to tackle, I can walk you through it.

janschiefer commented 1 year ago

Are there any examples for pin remapping in stm32-data on other STM32 MCU families?

Dirbaio commented 1 year ago

no :( "remaps" don't exist in other families. In the other families, you can pick any combination of pins.

For example, on all families except F1 pin mapping works like this:

and you can mix and match at will. For example RX=PB4, TX=PE12. 9 combinations total in this example.

and on F1 it works like this

and these are all the combinations. You can't mix and match: you can't set RX=PA5, TX=PB5.

There's currently no way to represent the F1 style in the stm32-data json files. We'd have to invent a way to represent it, then scrape the data in that format, then make embassy-stm32 able to read it and generate code from that.

janschiefer commented 1 year ago

That's unfortunate.

For now, maybe write something to the FAQs with the "hacky" workaround from above...?

shimunn commented 3 months ago

Same issue with an stm32l151cb-a, I've already searched through the reference manual but I can't find any helpful info on how exactly jtag can be disabled.

Using the JTAG pins as GPIO as was indicated here didn't help:

    {
        let _jt_do = Input::new(p.PB3, Pull::None);
        let _jt_di = Input::new(p.PA15, Pull::None);
    }