Support to BlueNRG_2 BLE component (#13246)

Add support to BlueNRG_2 BLE component

Signed-off-by: Andrea Palmieri <andrea.palmieri@st.com>

Co-authored-by: Andrea Palmieri <andrea.palmieri@st.com>
Co-authored-by: Paul Szczeanek <paul.szczepanek@arm.com>
pull/14067/head
Andrea Palmieri 2020-12-17 15:37:21 +01:00 committed by GitHub
parent 89bd565582
commit ae4a53e74a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 892 additions and 28 deletions

View File

@ -367,6 +367,30 @@ static void hciEvtParseLeConnCmpl(hciEvt_t *pMsg, uint8_t *p, uint8_t len)
pMsg->hdr.status = pMsg->leConnCmpl.status;
}
#ifndef CORDIO_RPA_SWAP_WORKAROUND
#define CORDIO_RPA_SWAP_WORKAROUND 0
#endif
#if CORDIO_RPA_SWAP_WORKAROUND
/*************************************************************************************************/
/*!
* \brief Check if address bits are all 0s or all 1s.
*
* \param p Pointer to address.
*
* \return TRUE if address is invalid.
*/
/*************************************************************************************************/
static bool_t isAddressInvalid(uint8_t *p)
{
for (int i = 0; i < 6; i++) {
if ((p[i] != 0xff) && (p[i] != 0x00)) {
return FALSE; // meaning valid address
}
}
return TRUE;
}
#endif // CORDIO_RPA_SWAP_WORKAROUND
/*************************************************************************************************/
/*!
* \brief Parse an HCI event.
@ -384,9 +408,21 @@ static void hciEvtParseLeEnhancedConnCmpl(hciEvt_t *pMsg, uint8_t *p, uint8_t le
BSTREAM_TO_UINT16(pMsg->leConnCmpl.handle, p);
BSTREAM_TO_UINT8(pMsg->leConnCmpl.role, p);
BSTREAM_TO_UINT8(pMsg->leConnCmpl.addrType, p);
BSTREAM_TO_BDA(pMsg->leConnCmpl.peerAddr, p);
BSTREAM_TO_BDA(pMsg->leConnCmpl.localRpa, p);
BSTREAM_TO_BDA(pMsg->leConnCmpl.peerRpa, p);
#if CORDIO_RPA_SWAP_WORKAROUND
if (isAddressInvalid(p)) {
memset(pMsg->leConnCmpl.peerRpa, 0x00, BDA_ADDR_LEN);
p += BDA_ADDR_LEN;
BSTREAM_TO_BDA(pMsg->leConnCmpl.localRpa, p);
BSTREAM_TO_BDA(pMsg->leConnCmpl.peerAddr, p);
} else
#endif // CORDIO_RPA_SWAP_WORKAROUND
{
BSTREAM_TO_BDA(pMsg->leConnCmpl.peerAddr, p);
BSTREAM_TO_BDA(pMsg->leConnCmpl.localRpa, p);
BSTREAM_TO_BDA(pMsg->leConnCmpl.peerRpa, p);
}
BSTREAM_TO_UINT16(pMsg->leConnCmpl.connInterval, p);
BSTREAM_TO_UINT16(pMsg->leConnCmpl.connLatency, p);
BSTREAM_TO_UINT16(pMsg->leConnCmpl.supTimeout, p);

View File

@ -76,6 +76,16 @@
"help": "Maximum number of EATT channels per DM connection.",
"value": 1,
"macro_name": "EATT_CONN_CHAN_MAX"
},
"rpa-swap-workaround": {
"help": "Check connection complete event if it needs the addresses swapped due to bug in controller",
"value": null,
"macro_name": "CORDIO_RPA_SWAP_WORKAROUND"
}
},
"target_overrides": {
"NUCLEO_WB55RG": {
"rpa-swap-workaround": true
}
}
}
}

View File

@ -401,30 +401,18 @@ private:
static GapConnectionCompleteEvent convert(const hciLeConnCmplEvt_t *conn_evt)
{
const bdAddr_t *peer_rpa = &conn_evt->peerRpa;
const bdAddr_t *peer_address = &conn_evt->peerAddr;
#if defined(TARGET_MCU_STM32WB55xx)
const bdAddr_t invalidAddress = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
if (conn_evt->addrType == DM_ADDR_RANDOM &&
memcmp(peer_address, invalidAddress, sizeof(invalidAddress)) == 0 &&
memcmp(peer_rpa, invalidAddress, sizeof(invalidAddress) != 0)
) {
std::swap(peer_rpa, peer_address);
}
#endif
return GapConnectionCompleteEvent(
conn_evt->status,
// note the usage of the stack handle, not the HCI handle
conn_evt->hdr.param,
(connection_role_t::type) conn_evt->role,
(peer_address_type_t::type) conn_evt->addrType,
*peer_address,
conn_evt->peerAddr,
conn_evt->connInterval,
conn_evt->connLatency,
conn_evt->supTimeout,
conn_evt->localRpa,
*peer_rpa
conn_evt->peerRpa
);
}
};

View File

@ -1,6 +1,10 @@
# Copyright (c) 2020 ARM Limited. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
if("BlueNRG_2" IN_LIST MBED_TARGET_LABELS)
add_subdirectory(COMPONENT_BlueNRG_2)
endif()
if("BlueNRG_MS" IN_LIST MBED_TARGET_LABELS)
add_subdirectory(COMPONENT_BlueNRG_MS)
endif()

View File

@ -0,0 +1,631 @@
/*
* Copyright (c) 2017-2020 ARM Limited
* Copyright (c) 2017-2020 STMicroelectronics
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stdio.h>
// drivers
#include "drivers/DigitalOut.h"
#include "drivers/SPI.h"
#include "drivers/InterruptIn.h"
#include "drivers/Timer.h"
// platform
#include "platform/mbed_wait_api.h"
// FEATURE_BLE/targets/TARGET_CORDIO
#include "CordioHCIDriver.h"
#include "CordioHCITransportDriver.h"
#include "hci_api.h"
#include "hci_cmd.h"
#include "hci_core.h"
#include "dm_api.h"
#include "bstream.h"
#include "hci_mbed_os_adaptation.h"
// rtos
#include "rtos/Thread.h"
#include "rtos/Semaphore.h"
#include "rtos/Mutex.h"
#define HCI_RESET_RAND_CNT 4
#define VENDOR_SPECIFIC_EVENT 0xFF
#define EVT_BLUENRG_2_INITIALIZED 0x0001
#define ACI_READ_CONFIG_DATA_OPCODE 0xFC0D
#define ACI_WRITE_CONFIG_DATA_OPCODE 0xFC0C
#define ACI_GATT_INIT_OPCODE 0xFD01
#define ACI_GAP_INIT_OPCODE 0xFC8A
#define PUBLIC_ADDRESS_OFFSET 0x00
#define RANDOM_STATIC_ADDRESS_OFFSET 0x80
#define LL_WITHOUT_HOST_OFFSET 0x2C
#define SPI_STACK_SIZE 1024
#define IRQ_TIMEOUT_DURATION 100 //ms
namespace ble {
namespace vendor {
namespace bluenrg_2 {
/**
* BlueNRG_2 HCI driver implementation.
* @see CordioHCIDriver
*/
class HCIDriver : public CordioHCIDriver {
public:
/**
* Construction of the BlueNRG_2 HCIDriver.
* @param transport: Transport of the HCI commands.
* @param rst: Name of the reset pin
*/
HCIDriver(CordioHCITransportDriver &transport_driver, PinName rst) :
CordioHCIDriver(transport_driver), rst(rst) { }
/**
* @see CordioHCIDriver::do_initialize
*/
virtual void do_initialize()
{
bluenrg_2_reset();
}
/**
* @see CordioHCIDriver::get_buffer_pool_description
*/
ble::buf_pool_desc_t get_buffer_pool_description()
{
// Use default buffer pool
return ble::CordioHCIDriver::get_default_buffer_pool_description();
}
/**
* @see CordioHCIDriver::start_reset_sequence
*/
virtual void start_reset_sequence()
{
reset_received = false;
bluenrg_2_initialized = false;
enable_link_layer_mode_ongoing = false;
/* send an HCI Reset command to start the sequence */
HciResetCmd();
}
/**
* @see CordioHCIDriver::do_terminate
*/
virtual void do_terminate()
{
}
/**
* @see CordioHCIDriver::handle_reset_sequence
*/
virtual void handle_reset_sequence(uint8_t *pMsg)
{
uint16_t opcode;
static uint8_t randCnt;
/* if event is a command complete event */
if (*pMsg == HCI_CMD_CMPL_EVT) {
/* parse parameters */
pMsg += HCI_EVT_HDR_LEN;
pMsg++; /* skip num packets */
BSTREAM_TO_UINT16(opcode, pMsg);
pMsg++; /* skip status */
/* decode opcode */
switch (opcode) {
case HCI_OPCODE_RESET: {
/* initialize rand command count */
randCnt = 0;
reset_received = true;
// bluenrg_2_initialized event has to come after the hci reset event
bluenrg_2_initialized = false;
}
break;
// ACL packet
case ACI_WRITE_CONFIG_DATA_OPCODE:
if (enable_link_layer_mode_ongoing) {
enable_link_layer_mode_ongoing = false;
aciGattInit();
}
break;
case ACI_GATT_INIT_OPCODE:
aciGapInit();
break;
case ACI_GAP_INIT_OPCODE:
aciReadConfigParameter(RANDOM_STATIC_ADDRESS_OFFSET);
break;
case ACI_READ_CONFIG_DATA_OPCODE:
// note: will send the HCI command to send the random address
pMsg++; /* skip address length */
set_random_static_address(pMsg);
break;
case HCI_OPCODE_LE_SET_RAND_ADDR:
HciSetEventMaskCmd((uint8_t *) hciEventMask);
break;
case HCI_OPCODE_SET_EVENT_MASK:
/* send next command in sequence */
HciLeSetEventMaskCmd((uint8_t *) hciLeEventMask);
break;
case HCI_OPCODE_LE_SET_EVENT_MASK:
// Note: the public address is not read because there is no valid public address provisioned by default on the target
// You can enable it thanks to json "valid-public-bd-address" config value
#if MBED_CONF_BLUENRG_2_VALID_PUBLIC_BD_ADDRESS == 1
/* send next command in sequence */
HciReadBdAddrCmd();
break;
case HCI_OPCODE_READ_BD_ADDR:
/* parse and store event parameters */
BdaCpy(hciCoreCb.bdAddr, pMsg);
/* send next command in sequence */
#endif
HciLeReadBufSizeCmd();
break;
case HCI_OPCODE_LE_READ_BUF_SIZE:
/* parse and store event parameters */
BSTREAM_TO_UINT16(hciCoreCb.bufSize, pMsg);
BSTREAM_TO_UINT8(hciCoreCb.numBufs, pMsg);
/* initialize ACL buffer accounting */
hciCoreCb.availBufs = hciCoreCb.numBufs;
/* send next command in sequence */
HciLeReadSupStatesCmd();
break;
case HCI_OPCODE_LE_READ_SUP_STATES:
/* parse and store event parameters */
memcpy(hciCoreCb.leStates, pMsg, HCI_LE_STATES_LEN);
/* send next command in sequence */
HciLeReadWhiteListSizeCmd();
break;
case HCI_OPCODE_LE_READ_WHITE_LIST_SIZE:
/* parse and store event parameters */
BSTREAM_TO_UINT8(hciCoreCb.whiteListSize, pMsg);
/* send next command in sequence */
HciLeReadLocalSupFeatCmd();
break;
case HCI_OPCODE_LE_READ_LOCAL_SUP_FEAT:
/* parse and store event parameters */
BSTREAM_TO_UINT16(hciCoreCb.leSupFeat, pMsg);
/* send next command in sequence */
hciCoreReadResolvingListSize();
break;
case HCI_OPCODE_LE_READ_RES_LIST_SIZE:
/* parse and store event parameters */
BSTREAM_TO_UINT8(hciCoreCb.resListSize, pMsg);
/* send next command in sequence */
hciCoreReadMaxDataLen();
break;
case HCI_OPCODE_LE_READ_MAX_DATA_LEN: {
uint16_t maxTxOctets;
uint16_t maxTxTime;
BSTREAM_TO_UINT16(maxTxOctets, pMsg);
BSTREAM_TO_UINT16(maxTxTime, pMsg);
/* use Controller's maximum supported payload octets and packet duration times
* for transmission as Host's suggested values for maximum transmission number
* of payload octets and maximum packet transmission time for new connections.
*/
HciLeWriteDefDataLen(maxTxOctets, maxTxTime);
}
break;
case HCI_OPCODE_LE_WRITE_DEF_DATA_LEN:
if (hciCoreCb.extResetSeq) {
/* send first extended command */
(*hciCoreCb.extResetSeq)(pMsg, opcode);
} else {
/* initialize extended parameters */
hciCoreCb.maxAdvDataLen = 0;
hciCoreCb.numSupAdvSets = 0;
hciCoreCb.perAdvListSize = 0;
/* send next command in sequence */
HciLeRandCmd();
}
break;
case HCI_OPCODE_LE_READ_MAX_ADV_DATA_LEN:
case HCI_OPCODE_LE_READ_NUM_SUP_ADV_SETS:
case HCI_OPCODE_LE_READ_PER_ADV_LIST_SIZE:
if (hciCoreCb.extResetSeq) {
/* send next extended command in sequence */
(*hciCoreCb.extResetSeq)(pMsg, opcode);
}
break;
case HCI_OPCODE_LE_RAND:
/* check if need to send second rand command */
if (randCnt < (HCI_RESET_RAND_CNT - 1)) {
randCnt++;
HciLeRandCmd();
} else {
signal_reset_sequence_done();
}
break;
default:
break;
}
} else {
/**
* vendor specific event
*/
if (pMsg[0] == VENDOR_SPECIFIC_EVENT) {
/* parse parameters */
pMsg += HCI_EVT_HDR_LEN;
BSTREAM_TO_UINT16(opcode, pMsg);
if (opcode == EVT_BLUENRG_2_INITIALIZED) {
if (bluenrg_2_initialized) {
return;
}
bluenrg_2_initialized = true;
if (reset_received) {
aciEnableLinkLayerModeOnly();
}
}
}
}
}
private:
void aciEnableLinkLayerModeOnly()
{
uint8_t data[1] = { 0x01 };
enable_link_layer_mode_ongoing = true;
aciWriteConfigData(LL_WITHOUT_HOST_OFFSET, data);
}
void aciGattInit()
{
uint8_t *pBuf = hciCmdAlloc(ACI_GATT_INIT_OPCODE, 0);
if (!pBuf) {
return;
}
hciCmdSend(pBuf);
}
void aciGapInit()
{
uint8_t *pBuf = hciCmdAlloc(ACI_GAP_INIT_OPCODE, 3);
if (!pBuf) {
return;
}
pBuf[3] = 0xF;
pBuf[4] = 0;
pBuf[5] = 0;
hciCmdSend(pBuf);
}
void aciReadConfigParameter(uint8_t offset)
{
uint8_t *pBuf = hciCmdAlloc(ACI_READ_CONFIG_DATA_OPCODE, 1);
if (!pBuf) {
return;
}
pBuf[3] = offset;
hciCmdSend(pBuf);
}
template<size_t N>
void aciWriteConfigData(uint8_t offset, uint8_t (&buf)[N])
{
uint8_t *pBuf = hciCmdAlloc(ACI_WRITE_CONFIG_DATA_OPCODE, 2 + N);
if (!pBuf) {
return;
}
pBuf[3] = offset;
pBuf[4] = N;
memcpy(pBuf + 5, buf, N);
hciCmdSend(pBuf);
}
void hciCoreReadResolvingListSize(void)
{
/* if LL Privacy is supported by Controller and included */
if ((hciCoreCb.leSupFeat & HCI_LE_SUP_FEAT_PRIVACY) &&
(hciLeSupFeatCfg & HCI_LE_SUP_FEAT_PRIVACY)) {
/* send next command in sequence */
HciLeReadResolvingListSize();
} else {
hciCoreCb.resListSize = 0;
/* send next command in sequence */
hciCoreReadMaxDataLen();
}
}
void hciCoreReadMaxDataLen(void)
{
/* if LE Data Packet Length Extensions is supported by Controller and included */
if ((hciCoreCb.leSupFeat & HCI_LE_SUP_FEAT_DATA_LEN_EXT) &&
(hciLeSupFeatCfg & HCI_LE_SUP_FEAT_DATA_LEN_EXT)) {
/* send next command in sequence */
HciLeReadMaxDataLen();
} else {
/* send next command in sequence */
HciLeRandCmd();
}
}
void bluenrg_2_reset()
{
/* Reset BlueNRG_2 SPI interface. Hold reset line to 0 for 1500us */
rst = 0;
wait_us(1500);
rst = 1;
/* Wait for the radio to come back up */
wait_us(100000);
}
mbed::DigitalOut rst;
bool reset_received;
bool bluenrg_2_initialized;
bool enable_link_layer_mode_ongoing;
};
/**
* Transport driver of the ST BlueNRG_2 shield.
* @important: With that driver, it is assumed that the SPI bus used is not shared
* with other SPI peripherals. The reasons behind this choice are simplicity and
* performance:
* - Reading from the peripheral SPI can be challenging especially if other
* threads access the same SPI bus. Indeed it is common that the function
* spiRead yield nothings even if the chip has signaled data with the irq
* line. Sharing would make the situation worse and increase the risk of
* timeout of HCI commands / response.
* - This driver can be used even if the RTOS is disabled or not present it may
* may be usefull for some targets.
*
* If The SPI is shared with other peripherals then the best option would be to
* handle SPI read in a real time thread woken up by an event flag.
*
* Other mechanisms might also be added in the future to handle data read as an
* event from the stack. This might not be the best solution for all BLE chip;
* especially this one.
*/
class TransportDriver : public CordioHCITransportDriver {
public:
/**
* Construct the transport driver required by a BlueNRG_2 module.
* @param mosi Pin of the SPI mosi
* @param miso Pin of the SPI miso
* @param sclk Pin of the SPI clock
* @param irq Pin used by the module to signal data are available.
*/
TransportDriver(PinName mosi, PinName miso, PinName sclk, PinName ncs, PinName irq)
: spi(mosi, miso, sclk), nCS(ncs), irq(irq), _spi_thread(osPriorityNormal, SPI_STACK_SIZE, _spi_thread_stack)
{
_spi_thread.start(mbed::callback(this, &TransportDriver::spi_read_cb));
}
virtual ~TransportDriver() { }
/**
* @see CordioHCITransportDriver::initialize
*/
virtual void initialize()
{
// Setup the spi for 8 bit data, low clock polarity,
// 2-edge phase, with an 1MHz clock rate
spi.format(8, 1);
spi.frequency(1000000);
// Deselect the BlueNRG_2 chip by keeping its nCS signal high
nCS = 1;
wait_us(500);
// Set the interrupt handler for the device
irq.mode(PullDown); // set irq mode
irq.rise(mbed::callback(this, &TransportDriver::HCI_Isr));
}
/**
* @see CordioHCITransportDriver::terminate
*/
virtual void terminate() { }
/**
* @see CordioHCITransportDriver::write
*/
virtual uint16_t write(uint8_t type, uint16_t len, uint8_t *pData)
{
// repeat write until successfull. A number of attempt or timeout might
// be useful
while (spiWrite(type, pData, len) == 0) { }
return len;
}
private:
uint16_t spiWrite(uint8_t type, const uint8_t *data, uint16_t data_length)
{
static const uint8_t header_master[] = { 0x0a, 0x00, 0x00, 0x00, 0x00 };
uint8_t header_slave[5];
uint16_t data_written = 0;
uint16_t write_buffer_size = 0;
_spi_mutex.lock();
irq.disable_irq();
/* CS reset */
nCS = 0;
// Wait until BlueNRG_2 is ready.
// When ready it will raise the IRQ pin.
_irq_timer.start();
while (!dataPresent()) {
auto us = _irq_timer.elapsed_time().count();
if (us > IRQ_TIMEOUT_DURATION * 1000) {
goto exit;
}
}
/* Exchange header */
for (uint8_t i = 0; i < sizeof(header_master); ++i) {
header_slave[i] = spi.write(header_master[i]);
}
write_buffer_size = header_slave[2] << 8 | header_slave[1];
if (write_buffer_size == 0 || write_buffer_size < (data_length + 1)) {
goto exit;
}
spi.write(type);
data_written = data_length;
for (uint16_t i = 0; i < data_length; ++i) {
spi.write(data[i]);
}
exit:
nCS = 1;
irq.enable_irq();
_irq_timer.stop();
_spi_mutex.unlock();
return data_written;
}
bool dataPresent()
{
return (irq == 1);
}
uint16_t spiRead(uint8_t *data_buffer, const uint16_t buffer_size)
{
static const uint8_t header_master[] = {0x0b, 0x00, 0x00, 0x00, 0x00};
uint8_t header_slave[5];
uint16_t read_length = 0;
uint16_t data_available = 0;
irq.disable_irq();
nCS = 0;
/* Read the header */
for (size_t i = 0; i < sizeof(header_master); i++) {
header_slave[i] = spi.write(header_master[i]);
}
data_available = (header_slave[4] << 8) | header_slave[3];
read_length = data_available > buffer_size ? buffer_size : data_available;
for (uint16_t i = 0; i < read_length; ++i) {
data_buffer[i] = spi.write(0x00);
}
nCS = 1;
irq.enable_irq();
return read_length;
}
/*
* might be split into two parts: the IRQ signaling a real time thread and
* the real time thread reading data from the SPI.
*/
void HCI_Isr(void)
{
_spi_read_sem.release();
}
void spi_read_cb()
{
uint8_t data_buffer[256];
while (true) {
_spi_read_sem.acquire();
_spi_mutex.lock();
while (irq == 1) {
uint16_t data_read = spiRead(data_buffer, sizeof(data_buffer));
on_data_received(data_buffer, data_read);
}
_spi_mutex.unlock();
}
}
/**
* Unsafe SPI, does not lock when SPI access happens.
*/
::mbed::SPI spi;
mbed::DigitalOut nCS;
mbed::InterruptIn irq;
rtos::Thread _spi_thread;
uint8_t _spi_thread_stack[SPI_STACK_SIZE];
rtos::Semaphore _spi_read_sem;
rtos::Mutex _spi_mutex;
mbed::Timer _irq_timer;
};
} // namespace bluenrg_2
} // namespace vendor
} // namespace ble
/**
* Cordio HCI driver factory
*/
ble::CordioHCIDriver &ble_cordio_get_hci_driver()
{
static ble::vendor::bluenrg_2::TransportDriver transport_driver(
MBED_CONF_BLUENRG_2_SPI_MOSI,
MBED_CONF_BLUENRG_2_SPI_MISO,
MBED_CONF_BLUENRG_2_SPI_SCK,
MBED_CONF_BLUENRG_2_SPI_NCS,
MBED_CONF_BLUENRG_2_SPI_IRQ
);
static ble::vendor::bluenrg_2::HCIDriver hci_driver(
transport_driver,
MBED_CONF_BLUENRG_2_SPI_RESET
);
return hci_driver;
}

View File

@ -0,0 +1,12 @@
# Copyright (c) 2020 ARM Limited. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
target_sources(mbed-ble-bluenrg2
INTERFACE
BlueNrg2HCIDriver.cpp
)
target_link_libraries(mbed-ble-bluenrg2
INTERFACE
mbed-ble
)

View File

@ -0,0 +1,160 @@
# BlueNRG_2
HCI driver for BlueNRG_2 BLE component
A simple table like this could help:
|Module Name|Processor Name|Bluetooth compliance|Status|Used in shields & boards|Link|
|-------------|-----------|-----|-|-|-|
|SPBTLE-RF |BlueNRG-MS (network processor) |v4.1 |Not recommended for new designs |X-NUCLEO-IDB05A1, DISCO-L475VG-IOT01A, DISCO-L562QE | https://www.st.com/en/wireless-transceivers-mcus-and-modules/spbtle-rf.html |
|BlueNRG-M0 |BlueNRG-MS (network processor) |v4.2 |Active (included in ST's Longevity Program) |X-NUCLEO-IDB05A2 | https://www.st.com/en/wireless-transceivers-mcus-and-modules/bluenrg-m0.html |
|BlueNRG-M2SP |BlueNRG-2 (application processor) |v5.0 |Active (included in ST's Longevity Program) |X-NUCLEO-BNRG2A1 | https://www.st.com/en/wireless-transceivers-mcus-and-modules/bluenrg-m2.html |
The library uses ARM Cordio stack. It does not work with the stock firmware that is loaded in the BLUENRG-M2SP BLE module of the X-NUCLEO-BNRG2A1 expansion board.
In order to use this library with X-NUCLEO-BNRG2A1, you need to update the firmware of the BLUENRG-M2SP BLE module mounted on that expansion.
To this aim, attach the X-NUCLEO-BNRG2A1 to the STM32 NUCLEO board through the Arduino Connector. You can use the binary provided for both STM32 NUCLEO-F401RE and STM32 NUCLEO-L476RG.
Download the binaries from the links below:
[BLE2_StackUpdater_F401RE.bin](https://github.com/STMicroelectronics/mbed-ble/blob/master/BlueNRG2_StackUpdater/BLE2_StackUpdater_F401RE.bin)
[BLE2_StackUpdater_L476RG.bin](https://github.com/STMicroelectronics/mbed-ble/blob/master/BlueNRG2_StackUpdater/BLE2_StackUpdater_L476RG.bin)
Before flashing your target board with the respective binary, be careful to properly set the SPI reset on pin **D7** as reported in the picture below:
![D7_SPI_rst](img/D7_SPI_rst.jpg)
At the end of the updating procedure, the LED labeled as **LD2** on the STM32 NUCLEO board will be blinking.
You can also check the status of the updating procedure on the serial terminal (baud rate set to 115200).
The picture below shows the log upon success:
![updLog](img/updLog.jpg)
If, for some reason, the updating procedure does not succeed you can use a standard ST-Link V2 with 5 jumper wires female-female together with the
[BlueNRG2_FlasherUtility](https://github.com/STMicroelectronics/mbed-ble/blob/master/BlueNRG2_FlasherUtility/en.STSW_BNRGFLASHER.zip) software tool (currently available only for Windows PC).
You need to connect the J12 pins of the X-NUCLEO-BNRG2A1 to the pins of the ST-Link V2 as shown in the picture below:
![ST-LINK](img/ST-LINK.jpg)
In particular, we have the following connections:
|Pin number on J12|Pin number on ST/Link V2|
|--------|----------|
|1 |1 |
|2 |9 |
|3 |12 |
|4 |7 |
|5 |15 |
Install the ST BlueNRG-1_2 Flasher Utility and open it, then select the SWD tab:
![Flasher_1](img/Flasher_1.jpg)
Erase the flash memory of the BlueNRG-2 chip:
![Flasher_2](img/Flasher_2.jpg)
Download the Firmware for the BLE module from the following link:
[DTM_SPI.hex](https://github.com/STMicroelectronics/mbed-ble/blob/master/BlueNRG2_Firmware/DTM_SPI.hex)
Load the Firmware in the ST BlueNRG-1_2 Flasher Utility and then press the "Flash" button:
![Flasher_3](img/Flasher_3.jpg)
If you should find some issues during the update process, you can try to repeat the procedure closing the J15 jumper on the X-NUCLEO-BNRG2A1 expansion board.
## Expansion Board
### X-NUCLEO-BNRG2A1
Bluetooth Low Energy Nucleo Expansion Board:
https://os.mbed.com/components/X-NUCLEO-BNRG2A1/
## Driver configuration
In order to use the BlueNRG-M2SP module together with other targets,
you may need to override default settings in your local `mbed_app.json` file
### Arduino Connector Compatibility Warning
Default Arduino connection is using:
```
"SPI_MOSI": "D11",
"SPI_MISO": "D12",
"SPI_nCS": "A1",
"SPI_RESET": "D7",
"SPI_IRQ": "A0",
"SPI_SCK": "D3",
"valid-public-bd-address": false
```
X-NUCLEO-BNRG2A1 is Arduino compatible with an exception: instead of using pin **D13** for the SPI clock, pin **D3** is used.
The default configuration for this library is having the SPI clock on pin **D3**.
You should use the jumper labeled as J14 to change the SPI clock pin configuration.
#### SPI clock jumper settings
|Settings|Pin 1|Pin 2|Pin 3|
|--------|-----|-----|-----|
|**D3** |Open |Short|Short|
|**D13** |Short|Short|Open |
In case you change the default configuration, then you also have to configure this library to use pin **D13** to drive the SPI clock.
To this aim you need to update your local mbed_app.json file with:
```
"target_overrides": {
"XXXX": {
"SPI_SCK": "D13"
},
```
If you use pin **D13** for the SPI clock, please be aware that on some STM32 Nucleo boards you may **not** drive the LED,
otherwise you will get a conflict: the LED on STM32 Nucleo boards is connected to pin **D13**.
To correctly set the SPI reset on pin **D7** resistor R117 should be mounted.
Alternatively, pin **D7** of the Arduino connector (**CN9**) and pin #5 of the jumper labeled as J12 should be bridged.
### Target Configuration
To use that library, the target requires some extra configuration in the application `mbed_app.json`. In the `target_overides` section:
* BlueNRG module has to be enabled
```json
"target.components_add": ["BlueNRG_2"]
```
* BLE feature has to be enabled
```json
"target.features_add": ["BLE"]
```
* Extra labels have to be defined to include the cordio stack and this library:
```json
"target.extra_labels_add": ["CORDIO"]
```
As an example, the target overide section for the `NUCLEO_F401RE` would be:
```json
"NUCLEO_F401RE": {
"target.components_add": ["BlueNRG_2"],
"target.features_add": ["BLE"]
"target.extra_labels_add": ["CORDIO"]
}
```
### Known limitations
Security does not work with privacy due to pairing failure.

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

View File

@ -0,0 +1,23 @@
{
"name": "bluenrg_2",
"config": {
"SPI_MOSI": "D11",
"SPI_MISO": "D12",
"SPI_nCS": "A1",
"SPI_RESET": "D7",
"SPI_IRQ": "A0",
"SPI_SCK": "D3",
"valid-public-bd-address": {
"help": "Read the BD public address at startup",
"value": false
}
},
"target_overrides": {
"*": {
"target.macros_add": [ "CORDIO_RPA_SWAP_WORKAROUND=1" ]
},
"NUCLEO_F446RE": {
"SPI_SCK": "D13"
}
}
}

View File

@ -37,9 +37,9 @@
#include "hci_mbed_os_adaptation.h"
// rtos
#include "Thread.h"
#include "Semaphore.h"
#include "Mutex.h"
#include "rtos/Thread.h"
#include "rtos/Semaphore.h"
#include "rtos/Mutex.h"
#define HCI_RESET_RAND_CNT 4

View File

@ -1,14 +1,14 @@
# BlueNRG_MS
BLE API wrapper Library for BlueNRG (Bluetooth Low Energy)
HCI driver for BlueNRG_MS BLE component
Maybe a simple table like this could help:
A simple table like this could help:
|Name|Type|Bluetooth compliance|Status|Used in shields & boards|Link|
|-----------|----------|-----|-|-|-|
|SPBTLE-RF |Module |v4.1 |Not recommended for new designs |X-NUCLEO-IDB05A1, DISCO-L475VG-IOT01A, DISCO-L562QE | https://www.st.com/en/wireless-transceivers-mcus-and-modules/spbtle-rf.html |
|BlueNRG-M0 |Module |v4.2 |Active (included in ST's Longevity Program) |No | https://www.st.com/en/wireless-transceivers-mcus-and-modules/bluenrg-m0.html |
|BlueNRG-MS |Processor |v4.2 |Active (included in ST's Longevity Program) |X-NUCLEO-IDB05A2 (coming soon) | https://www.st.com/en/wireless-transceivers-mcus-and-modules/bluenrg-ms.html |
|Module Name|Processor Name|Bluetooth compliance|Status|Used in shields & boards|Link|
|-------------|-----------|-----|-|-|-|
|SPBTLE-RF |BlueNRG-MS (network processor) |v4.1 |Not recommended for new designs |X-NUCLEO-IDB05A1, DISCO-L475VG-IOT01A, DISCO-L562QE | https://www.st.com/en/wireless-transceivers-mcus-and-modules/spbtle-rf.html |
|BlueNRG-M0 |BlueNRG-MS (network processor) |v4.2 |Active (included in ST's Longevity Program) |X-NUCLEO-IDB05A2 | https://www.st.com/en/wireless-transceivers-mcus-and-modules/bluenrg-m0.html |
|BlueNRG-M2SP |BlueNRG-2 (application processor) |v5.0 |Active (included in ST's Longevity Program) |X-NUCLEO-BNRG2A1 | https://www.st.com/en/wireless-transceivers-mcus-and-modules/bluenrg-m2.html |
It uses ARM Cordio stack instead of the ST BlueNRG stack.