danielgallagher0 / bluenrg

Device crate for STMicro's BlueNRG-based Bluetooth RF modules
Apache License 2.0
21 stars 8 forks source link

Error WouldBlock #3

Closed sousandrei closed 3 years ago

sousandrei commented 3 years ago

Hey!

First of all thanks for the crate! Saving my life. (I would love to help btw)

So I found a post on 219design on some code using this lib and together with the tests here I'm piecing an example together. Unfortunately I'm encountering the aforementioned error on running any command on the with_spi block (more specifically on line 98). Latest one I tried was read_local_version_information. If I introduce the block! macro, it stays there forever :/

I'm usin a STN32L433RC-P board with a X-Nucleo-BNRG2A1 shield. Code dump (just in case): It initializes a bunch of stuff and then blinks a LED on the main loop. In the middle I'm trying to call the BT module

#![no_std]
#![no_main]

// use panic_halt as _;
use panic_semihosting as _;

use cortex_m;
use cortex_m_rt as rt;
use embedded_hal as ehal;
use stm32l4xx_hal as hal;

use cortex_m_semihosting::hprintln;
use ehal::spi::{Mode, Phase, Polarity};
use hal::{delay::Delay, prelude::*, stm32};
use rt::{entry, exception, ExceptionFrame};

use bluenrg::BlueNRG;
use bluetooth_hci::host::Hci;

// SPI mode
pub const MODE: Mode = Mode {
    polarity: Polarity::IdleLow,
    phase: Phase::CaptureOnFirstTransition,
};

#[entry]
fn main() -> ! {
    hprintln!("start").unwrap();

    let cp = cortex_m::Peripherals::take().unwrap();
    let dp = stm32::Peripherals::take().unwrap();

    // ???
    dp.RCC.ahb2enr.write(|w| w.gpioaen().set_bit());

    let mut flash = dp.FLASH.constrain();
    let mut rcc = dp.RCC.constrain();
    let mut pwr = dp.PWR.constrain(&mut rcc.apb1r1);

    let clocks = rcc.cfgr.freeze(&mut flash.acr, &mut pwr);

    let mut gpioa = dp.GPIOA.split(&mut rcc.ahb2);
    let mut gpiob = dp.GPIOB.split(&mut rcc.ahb2);

    let mut led = gpiob
        .pb13
        .into_push_pull_output(&mut gpiob.moder, &mut gpiob.otyper);

    let mut timer = Delay::new(cp.SYST, clocks);

    //==========================================
    // SPI

    hprintln!("enable spi").unwrap();

    let mut dc = gpiob
        .pb1
        .into_push_pull_output(&mut gpiob.moder, &mut gpiob.otyper);

    let sck = gpioa.pa5.into_af5(&mut gpioa.moder, &mut gpioa.afrl);
    let miso = gpioa.pa6.into_af5(&mut gpioa.moder, &mut gpioa.afrl);
    let mosi = gpioa.pa7.into_af5(&mut gpioa.moder, &mut gpioa.afrl);

    dc.set_low().ok();

    let mut spi = hal::spi::Spi::spi1(
        dp.SPI1,
        (sck, miso, mosi),
        MODE,
        1.mhz(),
        clocks,
        &mut rcc.apb2,
    );

    hprintln!("create spi interface").unwrap();

    //==========================================
    // BLUETOOTH

    let data_ready = gpioa
        .pa0
        .into_pull_down_input(&mut gpioa.moder, &mut gpioa.pupdr);

    let chip_select = gpioa
        .pa1
        .into_push_pull_output(&mut gpioa.moder, &mut gpioa.otyper);

    let reset_pin = gpioa
        .pa8
        .into_push_pull_output(&mut gpioa.moder, &mut gpioa.otyper);

    let mut rx_buffer: [u8; 128] = [0; 128];

    let mut bnrg = BlueNRG::new(&mut rx_buffer, chip_select, data_ready, reset_pin);

    match bnrg.with_spi(&mut spi, |c| c.read_local_version_information()) {
        Ok(r) => hprintln!("ok: {:#?}", r).unwrap(),
        Err(e) => hprintln!("err: {:#?}", e).unwrap(),
    };

    hprintln!("create bt interface").unwrap();

    //==========================================

    let mut i = 0;
    loop {
        hprintln!("loop {}", i).unwrap();
        i += 1;

        timer.delay_ms(500 as u32);
        led.set_high().ok();

        timer.delay_ms(500 as u32);
        led.set_low().ok();
    }
}

#[exception]
fn HardFault(ef: &ExceptionFrame) -> ! {
    panic!("{:#?}", ef);
}
danielgallagher0 commented 3 years ago

Try switching chip_select to pa11 instead of pa1, after double-checking my work:

If I'm reading the manuals correctly, the chip select pin on the BNRG2A1 (p 11) is CN5, pin 3, which would map to PA11 on the Nucleo L4xxRx-P (p 37).

I didn't check the data_ready or reset pins, so if chip_select is wrong, you should double-check those too.

sousandrei commented 3 years ago

You are 100% right. I was lost on whos PA was it, BT board or Nucleo board. I was also was not using block! as I should haha, hence errors galore. Then I would say the issue with WouldBlock is "solved", thanks for the attention <3 I'm still struggling tho and if you have time please keep on reading

I managed to get past the initial test with the following changes but don't seem to understand how to code for BT. Documenting here the changes.

Checking again I got the SPI pins changed to (NOTE: PB13 is also used by the user LED on the nucleo board :thinking: )

let sck = gpiob.pb13.into_af5(&mut gpiob.moder, &mut gpiob.afrh);
let miso = gpiob.pb14.into_af5(&mut gpiob.moder, &mut gpiob.afrh);
let mosi = gpiob.pb15.into_af5(&mut gpiob.moder, &mut gpiob.afrh);

Since I'm using more than GPIOA, I also enabled clock for other IOs, seems correct right?

dp.RCC.ahb2enr.write(|w| w.gpioaen().set_bit());
dp.RCC.ahb2enr.write(|w| w.gpioben().set_bit());
dp.RCC.ahb2enr.write(|w| w.gpiocen().set_bit());

chip_select pin is indeed pa11 reset is PA8 in the BT board, which translates to PC7 on the Nucleo board (given the correct jumpers and all) PA0 in the BT board which maps to PA0 in the Nucleo (nice) set to high as in the sheet it mentions that is has to be up or shorted into VCC for the chip to boot

I could not find a data_ready pin in the sheets :/ Naming is not familiar to me and I had to guess around Would you say that using PA1 on the Nucleo which maps to PA1 on the BT board that has the label DIO1_SPI_CS makes sense or I am going completely insane

Last change, the part where I try to read the local version got changed to the following. My current understanding (gut feelings) tells me that this should be enough to get it discoverable? (Ex: seen by phone in a BLE scan APP)

bnrg.with_spi(&mut spi, |c| {
    block!(GattCommands::init(c as &mut dyn GattCommands<Error = _>)).unwrap();
    block!(GapCommands::init(
        c as &mut dyn GapCommands<Error = _>,
        bluenrg::gap::Role::PERIPHERAL,
        false,
        7,
    ))
    .unwrap();
});

bnrg.with_spi(&mut spi, |c| {
    block!(c.le_set_advertising_parameters(&AdvertisingParameters {
        advertising_interval: AdvertisingInterval::for_type(
            AdvertisingType::ConnectableUndirected,
        )
        .with_range(Duration::from_millis(20), Duration::from_millis(1000),)
        .unwrap(),
        own_address_type: OwnAddressType::Public,
        peer_address: BdAddrType::Random(BdAddr([0x01, 0x02, 0x03, 0x04, 0x05, 0x06,])),
        advertising_channel_map: Channels::CH_37 | Channels::CH_39,
        advertising_filter_policy: AdvertisingFilterPolicy::AllowConnectionAndScan,
    }))
    .unwrap()
});

bnrg.with_spi(&mut spi, |c| {
    block!(c.le_set_advertising_enable(true)).unwrap()
});
danielgallagher0 commented 3 years ago

Oh, I see the problem. The X-Nucleo-BNRG2A1 uses a BlueNRG-M2SP, which is an "application processor module", which can run user code in addition to the BLE stack. The BlueNRG-MS that this crate is intended for is a BLE co-processor only, and has a different (and more limited) interface to the host processor.

With appropriate firmware, you should be able to get the BlueNRG-M2SP to behave like the BlueNRG-MS, and use one of the GPIOs as the data_ready pin. I don't see any pre-canned firmware from STMicro that provides that though. So your guess for DIO1_SPI_CS is as good as any.

As for getting the device discoverable, I use [set_discoverable](https://docs.rs/bluenrg/0.1.0/bluenrg/gap/trait.Commands.html#tymethod.set_discoverable) instead of the lower-level le_* commands. I'm not sure if it will work on the M2SP or not, though. That would probably depend on the firmware on the M2SP again, since it's a vendor-specific command.

sousandrei commented 3 years ago

This is deeper than I thought haha Well, I'm going to dive in trying to make the other Cortex talk to the actual BT chip and then maybe make it talk to the original Nucleo board. All in all thanks for the help! Closing in the issue