This is a fork of mbed-os-example-ble-HeartRate maintained for Sequana compatibility. This application transmits a heart rate value using the Bluetooth SIG Heart Rate Profile. The heart rate value is provided by the application itself, not by a sensor, so that you don't have to get a sensor just to run the example. The canonical source for this example lives at https://github.com/ARMmbed/mbed-os-example-ble/tree/master/BLE_HeartRate

Committer:
lru
Date:
Tue Feb 12 14:03:29 2019 +0000
Revision:
0:b283842072f8
Initial version.

Who changed what in which revision?

UserRevisionLine numberNew contents of line
lru 0:b283842072f8 1 #include <stdio.h>
lru 0:b283842072f8 2 #include "CordioBLE.h"
lru 0:b283842072f8 3 #include "CordioHCIDriver.h"
lru 0:b283842072f8 4 #include "CordioHCITransportDriver.h"
lru 0:b283842072f8 5 #include "mbed.h"
lru 0:b283842072f8 6 #include "hci_api.h"
lru 0:b283842072f8 7 #include "hci_cmd.h"
lru 0:b283842072f8 8 #include "hci_core.h"
lru 0:b283842072f8 9 #include "dm_api.h"
lru 0:b283842072f8 10 #include "bstream.h"
lru 0:b283842072f8 11 #include "hci_mbed_os_adaptation.h"
lru 0:b283842072f8 12 #include "bluenrg_targets.h"
lru 0:b283842072f8 13 #include "Thread.h"
lru 0:b283842072f8 14 #include "Semaphore.h"
lru 0:b283842072f8 15 #include "Mutex.h"
lru 0:b283842072f8 16
lru 0:b283842072f8 17 #define HCI_RESET_RAND_CNT 4
lru 0:b283842072f8 18
lru 0:b283842072f8 19 #define VENDOR_SPECIFIC_EVENT 0xFF
lru 0:b283842072f8 20 #define EVT_BLUE_INITIALIZED 0x0001
lru 0:b283842072f8 21 #define ACI_READ_CONFIG_DATA_OPCODE 0xFC0D
lru 0:b283842072f8 22 #define ACI_WRITE_CONFIG_DATA_OPCODE 0xFC0C
lru 0:b283842072f8 23 #define ACI_GATT_INIT_OPCODE 0xFD01
lru 0:b283842072f8 24 #define ACI_GAP_INIT_OPCODE 0xFC8A
lru 0:b283842072f8 25
lru 0:b283842072f8 26 #define PUBLIC_ADDRESS_OFFSET 0x00
lru 0:b283842072f8 27 #define RANDOM_STATIC_ADDRESS_OFFSET 0x80
lru 0:b283842072f8 28 #define LL_WITHOUT_HOST_OFFSET 0x2C
lru 0:b283842072f8 29 #define ROLE_OFFSET 0x2D
lru 0:b283842072f8 30
lru 0:b283842072f8 31 #define SPI_STACK_SIZE 1024
lru 0:b283842072f8 32
lru 0:b283842072f8 33 namespace ble {
lru 0:b283842072f8 34 namespace vendor {
lru 0:b283842072f8 35 namespace bluenrg {
lru 0:b283842072f8 36
lru 0:b283842072f8 37 /**
lru 0:b283842072f8 38 * BlueNRG HCI driver implementation.
lru 0:b283842072f8 39 * @see cordio::CordioHCIDriver
lru 0:b283842072f8 40 */
lru 0:b283842072f8 41 class HCIDriver : public cordio::CordioHCIDriver
lru 0:b283842072f8 42 {
lru 0:b283842072f8 43 public:
lru 0:b283842072f8 44 /**
lru 0:b283842072f8 45 * Construction of the BlueNRG HCIDriver.
lru 0:b283842072f8 46 * @param transport: Transport of the HCI commands.
lru 0:b283842072f8 47 * @param rst: Name of the reset pin
lru 0:b283842072f8 48 */
lru 0:b283842072f8 49 HCIDriver(cordio::CordioHCITransportDriver& transport_driver, PinName rst) :
lru 0:b283842072f8 50 cordio::CordioHCIDriver(transport_driver), rst(rst) { }
lru 0:b283842072f8 51
lru 0:b283842072f8 52 /**
lru 0:b283842072f8 53 * @see CordioHCIDriver::do_initialize
lru 0:b283842072f8 54 */
lru 0:b283842072f8 55 virtual void do_initialize() {
lru 0:b283842072f8 56 bluenrg_reset();
lru 0:b283842072f8 57 }
lru 0:b283842072f8 58
lru 0:b283842072f8 59 /**
lru 0:b283842072f8 60 * @see CordioHCIDriver::get_buffer_pool_description
lru 0:b283842072f8 61 */
lru 0:b283842072f8 62 ble::vendor::cordio::buf_pool_desc_t get_buffer_pool_description()
lru 0:b283842072f8 63 {
lru 0:b283842072f8 64 // Use default buffer pool
lru 0:b283842072f8 65 return ble::vendor::cordio::CordioHCIDriver::get_default_buffer_pool_description();
lru 0:b283842072f8 66 }
lru 0:b283842072f8 67
lru 0:b283842072f8 68 /**
lru 0:b283842072f8 69 * @see CordioHCIDriver::start_reset_sequence
lru 0:b283842072f8 70 */
lru 0:b283842072f8 71 virtual void start_reset_sequence() {
lru 0:b283842072f8 72 reset_received = false;
lru 0:b283842072f8 73 bluenrg_initialized = false;
lru 0:b283842072f8 74 enable_link_layer_mode_ongoing = false;
lru 0:b283842072f8 75 /* send an HCI Reset command to start the sequence */
lru 0:b283842072f8 76 HciResetCmd();
lru 0:b283842072f8 77 }
lru 0:b283842072f8 78
lru 0:b283842072f8 79 /**
lru 0:b283842072f8 80 * @see CordioHCIDriver::do_terminate
lru 0:b283842072f8 81 */
lru 0:b283842072f8 82 virtual void do_terminate() {
lru 0:b283842072f8 83
lru 0:b283842072f8 84 }
lru 0:b283842072f8 85
lru 0:b283842072f8 86 /**
lru 0:b283842072f8 87 * @see CordioHCIDriver::handle_reset_sequence
lru 0:b283842072f8 88 */
lru 0:b283842072f8 89 virtual void handle_reset_sequence(uint8_t *pMsg) {
lru 0:b283842072f8 90 uint16_t opcode;
lru 0:b283842072f8 91 static uint8_t randCnt;
lru 0:b283842072f8 92 //wait_ms(5);
lru 0:b283842072f8 93
lru 0:b283842072f8 94 /* if event is a command complete event */
lru 0:b283842072f8 95 if (*pMsg == HCI_CMD_CMPL_EVT)
lru 0:b283842072f8 96 {
lru 0:b283842072f8 97 /* parse parameters */
lru 0:b283842072f8 98 pMsg += HCI_EVT_HDR_LEN;
lru 0:b283842072f8 99 pMsg++; /* skip num packets */
lru 0:b283842072f8 100 BSTREAM_TO_UINT16(opcode, pMsg);
lru 0:b283842072f8 101 pMsg++; /* skip status */
lru 0:b283842072f8 102
lru 0:b283842072f8 103 /* decode opcode */
lru 0:b283842072f8 104 switch (opcode)
lru 0:b283842072f8 105 {
lru 0:b283842072f8 106 case HCI_OPCODE_RESET: {
lru 0:b283842072f8 107 /* initialize rand command count */
lru 0:b283842072f8 108 randCnt = 0;
lru 0:b283842072f8 109 reset_received = true;
lru 0:b283842072f8 110 // important, the bluenrg_initialized event come after the
lru 0:b283842072f8 111 // hci reset event (not documented)
lru 0:b283842072f8 112 bluenrg_initialized = false;
lru 0:b283842072f8 113 } break;
lru 0:b283842072f8 114
lru 0:b283842072f8 115 // ACL packet ...
lru 0:b283842072f8 116 case ACI_WRITE_CONFIG_DATA_OPCODE:
lru 0:b283842072f8 117 if (enable_link_layer_mode_ongoing) {
lru 0:b283842072f8 118 enable_link_layer_mode_ongoing = false;
lru 0:b283842072f8 119 aciSetRole();
lru 0:b283842072f8 120 } else {
lru 0:b283842072f8 121 aciGattInit();
lru 0:b283842072f8 122 }
lru 0:b283842072f8 123 break;
lru 0:b283842072f8 124
lru 0:b283842072f8 125 case ACI_GATT_INIT_OPCODE:
lru 0:b283842072f8 126 aciGapInit();
lru 0:b283842072f8 127 break;
lru 0:b283842072f8 128
lru 0:b283842072f8 129 case ACI_GAP_INIT_OPCODE:
lru 0:b283842072f8 130 aciReadConfigParameter(RANDOM_STATIC_ADDRESS_OFFSET);
lru 0:b283842072f8 131 break;
lru 0:b283842072f8 132
lru 0:b283842072f8 133 case ACI_READ_CONFIG_DATA_OPCODE:
lru 0:b283842072f8 134 // note: will send the HCI command to send the random address
lru 0:b283842072f8 135 cordio::BLE::deviceInstance().getGap().setAddress(
lru 0:b283842072f8 136 BLEProtocol::AddressType::RANDOM_STATIC,
lru 0:b283842072f8 137 pMsg
lru 0:b283842072f8 138 );
lru 0:b283842072f8 139 break;
lru 0:b283842072f8 140
lru 0:b283842072f8 141 case HCI_OPCODE_LE_SET_RAND_ADDR:
lru 0:b283842072f8 142 HciSetEventMaskCmd((uint8_t *) hciEventMask);
lru 0:b283842072f8 143 break;
lru 0:b283842072f8 144
lru 0:b283842072f8 145 case HCI_OPCODE_SET_EVENT_MASK:
lru 0:b283842072f8 146 /* send next command in sequence */
lru 0:b283842072f8 147 HciLeSetEventMaskCmd((uint8_t *) hciLeEventMask);
lru 0:b283842072f8 148 break;
lru 0:b283842072f8 149
lru 0:b283842072f8 150 case HCI_OPCODE_LE_SET_EVENT_MASK:
lru 0:b283842072f8 151 // Note: the public address is not read because there is no valid public address
lru 0:b283842072f8 152 // provisioned by default on the target
lru 0:b283842072f8 153 // Enable if the
lru 0:b283842072f8 154 #if MBED_CONF_CORDIO_BLUENRG_VALID_PUBLIC_BD_ADDRESS == 1
lru 0:b283842072f8 155 /* send next command in sequence */
lru 0:b283842072f8 156 HciReadBdAddrCmd();
lru 0:b283842072f8 157 break;
lru 0:b283842072f8 158
lru 0:b283842072f8 159 case HCI_OPCODE_READ_BD_ADDR:
lru 0:b283842072f8 160 /* parse and store event parameters */
lru 0:b283842072f8 161 BdaCpy(hciCoreCb.bdAddr, pMsg);
lru 0:b283842072f8 162
lru 0:b283842072f8 163 /* send next command in sequence */
lru 0:b283842072f8 164 #endif
lru 0:b283842072f8 165 HciLeReadBufSizeCmd();
lru 0:b283842072f8 166 break;
lru 0:b283842072f8 167
lru 0:b283842072f8 168 case HCI_OPCODE_LE_READ_BUF_SIZE:
lru 0:b283842072f8 169 /* parse and store event parameters */
lru 0:b283842072f8 170 BSTREAM_TO_UINT16(hciCoreCb.bufSize, pMsg);
lru 0:b283842072f8 171 BSTREAM_TO_UINT8(hciCoreCb.numBufs, pMsg);
lru 0:b283842072f8 172
lru 0:b283842072f8 173 /* initialize ACL buffer accounting */
lru 0:b283842072f8 174 hciCoreCb.availBufs = hciCoreCb.numBufs;
lru 0:b283842072f8 175
lru 0:b283842072f8 176 /* send next command in sequence */
lru 0:b283842072f8 177 HciLeReadSupStatesCmd();
lru 0:b283842072f8 178 break;
lru 0:b283842072f8 179
lru 0:b283842072f8 180 case HCI_OPCODE_LE_READ_SUP_STATES:
lru 0:b283842072f8 181 /* parse and store event parameters */
lru 0:b283842072f8 182 memcpy(hciCoreCb.leStates, pMsg, HCI_LE_STATES_LEN);
lru 0:b283842072f8 183
lru 0:b283842072f8 184 /* send next command in sequence */
lru 0:b283842072f8 185 HciLeReadWhiteListSizeCmd();
lru 0:b283842072f8 186 break;
lru 0:b283842072f8 187
lru 0:b283842072f8 188 case HCI_OPCODE_LE_READ_WHITE_LIST_SIZE:
lru 0:b283842072f8 189 /* parse and store event parameters */
lru 0:b283842072f8 190 BSTREAM_TO_UINT8(hciCoreCb.whiteListSize, pMsg);
lru 0:b283842072f8 191
lru 0:b283842072f8 192 /* send next command in sequence */
lru 0:b283842072f8 193 HciLeReadLocalSupFeatCmd();
lru 0:b283842072f8 194 break;
lru 0:b283842072f8 195
lru 0:b283842072f8 196 case HCI_OPCODE_LE_READ_LOCAL_SUP_FEAT:
lru 0:b283842072f8 197 /* parse and store event parameters */
lru 0:b283842072f8 198 BSTREAM_TO_UINT16(hciCoreCb.leSupFeat, pMsg);
lru 0:b283842072f8 199
lru 0:b283842072f8 200 /* send next command in sequence */
lru 0:b283842072f8 201 hciCoreReadResolvingListSize();
lru 0:b283842072f8 202 break;
lru 0:b283842072f8 203
lru 0:b283842072f8 204 case HCI_OPCODE_LE_READ_RES_LIST_SIZE:
lru 0:b283842072f8 205 /* parse and store event parameters */
lru 0:b283842072f8 206 BSTREAM_TO_UINT8(hciCoreCb.resListSize, pMsg);
lru 0:b283842072f8 207
lru 0:b283842072f8 208 /* send next command in sequence */
lru 0:b283842072f8 209 hciCoreReadMaxDataLen();
lru 0:b283842072f8 210 break;
lru 0:b283842072f8 211
lru 0:b283842072f8 212 case HCI_OPCODE_LE_READ_MAX_DATA_LEN:
lru 0:b283842072f8 213 {
lru 0:b283842072f8 214 uint16_t maxTxOctets;
lru 0:b283842072f8 215 uint16_t maxTxTime;
lru 0:b283842072f8 216
lru 0:b283842072f8 217 BSTREAM_TO_UINT16(maxTxOctets, pMsg);
lru 0:b283842072f8 218 BSTREAM_TO_UINT16(maxTxTime, pMsg);
lru 0:b283842072f8 219
lru 0:b283842072f8 220 /* use Controller's maximum supported payload octets and packet duration times
lru 0:b283842072f8 221 * for transmission as Host's suggested values for maximum transmission number
lru 0:b283842072f8 222 * of payload octets and maximum packet transmission time for new connections.
lru 0:b283842072f8 223 */
lru 0:b283842072f8 224 HciLeWriteDefDataLen(maxTxOctets, maxTxTime);
lru 0:b283842072f8 225 }
lru 0:b283842072f8 226 break;
lru 0:b283842072f8 227
lru 0:b283842072f8 228 case HCI_OPCODE_LE_WRITE_DEF_DATA_LEN:
lru 0:b283842072f8 229 if (hciCoreCb.extResetSeq)
lru 0:b283842072f8 230 {
lru 0:b283842072f8 231 /* send first extended command */
lru 0:b283842072f8 232 (*hciCoreCb.extResetSeq)(pMsg, opcode);
lru 0:b283842072f8 233 }
lru 0:b283842072f8 234 else
lru 0:b283842072f8 235 {
lru 0:b283842072f8 236 /* initialize extended parameters */
lru 0:b283842072f8 237 hciCoreCb.maxAdvDataLen = 0;
lru 0:b283842072f8 238 hciCoreCb.numSupAdvSets = 0;
lru 0:b283842072f8 239 hciCoreCb.perAdvListSize = 0;
lru 0:b283842072f8 240
lru 0:b283842072f8 241 /* send next command in sequence */
lru 0:b283842072f8 242 HciLeRandCmd();
lru 0:b283842072f8 243 }
lru 0:b283842072f8 244 break;
lru 0:b283842072f8 245
lru 0:b283842072f8 246 case HCI_OPCODE_LE_READ_MAX_ADV_DATA_LEN:
lru 0:b283842072f8 247 case HCI_OPCODE_LE_READ_NUM_SUP_ADV_SETS:
lru 0:b283842072f8 248 case HCI_OPCODE_LE_READ_PER_ADV_LIST_SIZE:
lru 0:b283842072f8 249 if (hciCoreCb.extResetSeq)
lru 0:b283842072f8 250 {
lru 0:b283842072f8 251 /* send next extended command in sequence */
lru 0:b283842072f8 252 (*hciCoreCb.extResetSeq)(pMsg, opcode);
lru 0:b283842072f8 253 }
lru 0:b283842072f8 254 break;
lru 0:b283842072f8 255
lru 0:b283842072f8 256 case HCI_OPCODE_LE_RAND:
lru 0:b283842072f8 257 /* check if need to send second rand command */
lru 0:b283842072f8 258 if (randCnt < (HCI_RESET_RAND_CNT-1))
lru 0:b283842072f8 259 {
lru 0:b283842072f8 260 randCnt++;
lru 0:b283842072f8 261 HciLeRandCmd();
lru 0:b283842072f8 262 }
lru 0:b283842072f8 263 else
lru 0:b283842072f8 264 {
lru 0:b283842072f8 265 signal_reset_sequence_done();
lru 0:b283842072f8 266 }
lru 0:b283842072f8 267 break;
lru 0:b283842072f8 268
lru 0:b283842072f8 269 default:
lru 0:b283842072f8 270 break;
lru 0:b283842072f8 271 }
lru 0:b283842072f8 272 } else {
lru 0:b283842072f8 273 /**
lru 0:b283842072f8 274 * vendor specific event
lru 0:b283842072f8 275 */
lru 0:b283842072f8 276 if (pMsg[0] == VENDOR_SPECIFIC_EVENT) {
lru 0:b283842072f8 277 /* parse parameters */
lru 0:b283842072f8 278 pMsg += HCI_EVT_HDR_LEN;
lru 0:b283842072f8 279 BSTREAM_TO_UINT16(opcode, pMsg);
lru 0:b283842072f8 280
lru 0:b283842072f8 281 if (opcode == EVT_BLUE_INITIALIZED) {
lru 0:b283842072f8 282 if (bluenrg_initialized) {
lru 0:b283842072f8 283 return;
lru 0:b283842072f8 284 }
lru 0:b283842072f8 285 bluenrg_initialized = true;
lru 0:b283842072f8 286 if (reset_received) {
lru 0:b283842072f8 287 aciEnableLinkLayerModeOnly();
lru 0:b283842072f8 288 }
lru 0:b283842072f8 289 }
lru 0:b283842072f8 290
lru 0:b283842072f8 291 }
lru 0:b283842072f8 292 }
lru 0:b283842072f8 293 }
lru 0:b283842072f8 294
lru 0:b283842072f8 295 private:
lru 0:b283842072f8 296 void aciEnableLinkLayerModeOnly() {
lru 0:b283842072f8 297 uint8_t data[1] = { 0x01 };
lru 0:b283842072f8 298 enable_link_layer_mode_ongoing = true;
lru 0:b283842072f8 299 aciWriteConfigData(LL_WITHOUT_HOST_OFFSET, data);
lru 0:b283842072f8 300 }
lru 0:b283842072f8 301
lru 0:b283842072f8 302 void aciSetRole() {
lru 0:b283842072f8 303 // master and slave, simultaneous advertising and scanning
lru 0:b283842072f8 304 // (up to 4 connections)
lru 0:b283842072f8 305 uint8_t data[1] = { 0x04 };
lru 0:b283842072f8 306 aciWriteConfigData(ROLE_OFFSET, data);
lru 0:b283842072f8 307 }
lru 0:b283842072f8 308
lru 0:b283842072f8 309 void aciGattInit() {
lru 0:b283842072f8 310 uint8_t *pBuf = hciCmdAlloc(ACI_GATT_INIT_OPCODE, 0);
lru 0:b283842072f8 311 if (!pBuf) {
lru 0:b283842072f8 312 return;
lru 0:b283842072f8 313 }
lru 0:b283842072f8 314 hciCmdSend(pBuf);
lru 0:b283842072f8 315 }
lru 0:b283842072f8 316
lru 0:b283842072f8 317 void aciGapInit() {
lru 0:b283842072f8 318 uint8_t *pBuf = hciCmdAlloc(ACI_GAP_INIT_OPCODE, 3);
lru 0:b283842072f8 319 if (!pBuf) {
lru 0:b283842072f8 320 return;
lru 0:b283842072f8 321 }
lru 0:b283842072f8 322 pBuf[3] = 0xF;
lru 0:b283842072f8 323 pBuf[4] = 0;
lru 0:b283842072f8 324 pBuf[5] = 0;
lru 0:b283842072f8 325 hciCmdSend(pBuf);
lru 0:b283842072f8 326 }
lru 0:b283842072f8 327
lru 0:b283842072f8 328 void aciReadConfigParameter(uint8_t offset) {
lru 0:b283842072f8 329 uint8_t *pBuf = hciCmdAlloc(ACI_READ_CONFIG_DATA_OPCODE, 1);
lru 0:b283842072f8 330 if (!pBuf) {
lru 0:b283842072f8 331 return;
lru 0:b283842072f8 332 }
lru 0:b283842072f8 333
lru 0:b283842072f8 334 pBuf[3] = offset;
lru 0:b283842072f8 335 hciCmdSend(pBuf);
lru 0:b283842072f8 336 }
lru 0:b283842072f8 337
lru 0:b283842072f8 338 template<size_t N>
lru 0:b283842072f8 339 void aciWriteConfigData(uint8_t offset, uint8_t (&buf)[N]) {
lru 0:b283842072f8 340 uint8_t *pBuf = hciCmdAlloc(ACI_WRITE_CONFIG_DATA_OPCODE, 2 + N);
lru 0:b283842072f8 341 if (!pBuf) {
lru 0:b283842072f8 342 return;
lru 0:b283842072f8 343 }
lru 0:b283842072f8 344
lru 0:b283842072f8 345 pBuf[3] = offset;
lru 0:b283842072f8 346 pBuf[4] = N;
lru 0:b283842072f8 347 memcpy(pBuf + 5, buf, N);
lru 0:b283842072f8 348 hciCmdSend(pBuf);
lru 0:b283842072f8 349 }
lru 0:b283842072f8 350
lru 0:b283842072f8 351 void hciCoreReadResolvingListSize(void)
lru 0:b283842072f8 352 {
lru 0:b283842072f8 353 /* if LL Privacy is supported by Controller and included */
lru 0:b283842072f8 354 if ((hciCoreCb.leSupFeat & HCI_LE_SUP_FEAT_PRIVACY) &&
lru 0:b283842072f8 355 (hciLeSupFeatCfg & HCI_LE_SUP_FEAT_PRIVACY))
lru 0:b283842072f8 356 {
lru 0:b283842072f8 357 /* send next command in sequence */
lru 0:b283842072f8 358 HciLeReadResolvingListSize();
lru 0:b283842072f8 359 }
lru 0:b283842072f8 360 else
lru 0:b283842072f8 361 {
lru 0:b283842072f8 362 hciCoreCb.resListSize = 0;
lru 0:b283842072f8 363
lru 0:b283842072f8 364 /* send next command in sequence */
lru 0:b283842072f8 365 hciCoreReadMaxDataLen();
lru 0:b283842072f8 366 }
lru 0:b283842072f8 367 }
lru 0:b283842072f8 368
lru 0:b283842072f8 369 void hciCoreReadMaxDataLen(void)
lru 0:b283842072f8 370 {
lru 0:b283842072f8 371 /* if LE Data Packet Length Extensions is supported by Controller and included */
lru 0:b283842072f8 372 if ((hciCoreCb.leSupFeat & HCI_LE_SUP_FEAT_DATA_LEN_EXT) &&
lru 0:b283842072f8 373 (hciLeSupFeatCfg & HCI_LE_SUP_FEAT_DATA_LEN_EXT))
lru 0:b283842072f8 374 {
lru 0:b283842072f8 375 /* send next command in sequence */
lru 0:b283842072f8 376 HciLeReadMaxDataLen();
lru 0:b283842072f8 377 }
lru 0:b283842072f8 378 else
lru 0:b283842072f8 379 {
lru 0:b283842072f8 380 /* send next command in sequence */
lru 0:b283842072f8 381 HciLeRandCmd();
lru 0:b283842072f8 382 }
lru 0:b283842072f8 383 }
lru 0:b283842072f8 384
lru 0:b283842072f8 385 void bluenrg_reset() {
lru 0:b283842072f8 386 /* Reset BlueNRG SPI interface. Hold reset line to 0 for 1500ms */
lru 0:b283842072f8 387 rst = 0;
lru 0:b283842072f8 388 wait_us(1500);
lru 0:b283842072f8 389 rst = 1;
lru 0:b283842072f8 390
lru 0:b283842072f8 391 /* Wait for the radio to come back up */
lru 0:b283842072f8 392 wait_us(100000);
lru 0:b283842072f8 393 }
lru 0:b283842072f8 394
lru 0:b283842072f8 395 DigitalOut rst;
lru 0:b283842072f8 396 bool reset_received;
lru 0:b283842072f8 397 bool bluenrg_initialized;
lru 0:b283842072f8 398 bool enable_link_layer_mode_ongoing;
lru 0:b283842072f8 399 };
lru 0:b283842072f8 400
lru 0:b283842072f8 401 /**
lru 0:b283842072f8 402 * Transport driver of the ST BlueNRG shield.
lru 0:b283842072f8 403 * @important: With that driver, it is assumed that the SPI bus used is not shared
lru 0:b283842072f8 404 * with other SPI peripherals. The reasons behind this choice are simplicity and
lru 0:b283842072f8 405 * performance:
lru 0:b283842072f8 406 * - Reading from the peripheral SPI can be challenging especially if other
lru 0:b283842072f8 407 * threads access the same SPI bus. Indeed it is common that the function
lru 0:b283842072f8 408 * spiRead yield nothings even if the chip has signaled data with the irq
lru 0:b283842072f8 409 * line. Sharing would make the situation worse and increase the risk of
lru 0:b283842072f8 410 * timeout of HCI commands / response.
lru 0:b283842072f8 411 * - This driver can be used even if the RTOS is disabled or not present it may
lru 0:b283842072f8 412 * may be usefull for some targets.
lru 0:b283842072f8 413 *
lru 0:b283842072f8 414 * If The SPI is shared with other peripherals then the best option would be to
lru 0:b283842072f8 415 * handle SPI read in a real time thread woken up by an event flag.
lru 0:b283842072f8 416 *
lru 0:b283842072f8 417 * Other mechanisms might also be added in the future to handle data read as an
lru 0:b283842072f8 418 * event from the stack. This might not be the best solution for all BLE chip;
lru 0:b283842072f8 419 * especially this one.
lru 0:b283842072f8 420 */
lru 0:b283842072f8 421 class TransportDriver : public cordio::CordioHCITransportDriver {
lru 0:b283842072f8 422 public:
lru 0:b283842072f8 423 /**
lru 0:b283842072f8 424 * Construct the transport driver required by a BlueNRG module.
lru 0:b283842072f8 425 * @param mosi Pin of the SPI mosi
lru 0:b283842072f8 426 * @param miso Pin of the SPI miso
lru 0:b283842072f8 427 * @param sclk Pin of the SPI clock
lru 0:b283842072f8 428 * @param irq Pin used by the module to signal data are available.
lru 0:b283842072f8 429 */
lru 0:b283842072f8 430 TransportDriver(PinName mosi, PinName miso, PinName sclk, PinName ncs, PinName irq)
lru 0:b283842072f8 431 : spi(mosi, miso, sclk), nCS(ncs), irq(irq), _spi_thread(osPriorityNormal, SPI_STACK_SIZE, _spi_thread_stack) {
lru 0:b283842072f8 432 _spi_thread.start(callback(this, &TransportDriver::spi_read_cb));
lru 0:b283842072f8 433 }
lru 0:b283842072f8 434
lru 0:b283842072f8 435 virtual ~TransportDriver() { }
lru 0:b283842072f8 436
lru 0:b283842072f8 437 /**
lru 0:b283842072f8 438 * @see CordioHCITransportDriver::initialize
lru 0:b283842072f8 439 */
lru 0:b283842072f8 440 virtual void initialize() {
lru 0:b283842072f8 441 // Setup the spi for 8 bit data, low clock polarity,
lru 0:b283842072f8 442 // 1-edge phase, with an 8MHz clock rate
lru 0:b283842072f8 443 spi.format(8, 0);
lru 0:b283842072f8 444 spi.frequency(8000000);
lru 0:b283842072f8 445
lru 0:b283842072f8 446 // Deselect the BlueNRG chip by keeping its nCS signal high
lru 0:b283842072f8 447 nCS = 1;
lru 0:b283842072f8 448
lru 0:b283842072f8 449 wait_us(500);
lru 0:b283842072f8 450
lru 0:b283842072f8 451 // Set the interrupt handler for the device
lru 0:b283842072f8 452 irq.mode(PullDown); // set irq mode
lru 0:b283842072f8 453 irq.rise(callback(this, &TransportDriver::HCI_Isr));
lru 0:b283842072f8 454 }
lru 0:b283842072f8 455
lru 0:b283842072f8 456 /**
lru 0:b283842072f8 457 * @see CordioHCITransportDriver::terminate
lru 0:b283842072f8 458 */
lru 0:b283842072f8 459 virtual void terminate() { }
lru 0:b283842072f8 460
lru 0:b283842072f8 461 /**
lru 0:b283842072f8 462 * @see CordioHCITransportDriver::write
lru 0:b283842072f8 463 */
lru 0:b283842072f8 464 virtual uint16_t write(uint8_t type, uint16_t len, uint8_t *pData) {
lru 0:b283842072f8 465 // repeat write until successfull. A number of attempt or timeout might
lru 0:b283842072f8 466 // be useful
lru 0:b283842072f8 467 while (spiWrite(type, pData, len) == 0) { }
lru 0:b283842072f8 468 return len;
lru 0:b283842072f8 469 }
lru 0:b283842072f8 470
lru 0:b283842072f8 471 private:
lru 0:b283842072f8 472 uint16_t spiWrite(uint8_t type, const uint8_t* data, uint16_t data_length) {
lru 0:b283842072f8 473 static const uint8_t header_master[] = {
lru 0:b283842072f8 474 0x0A, 0x00, 0x00, 0x00, 0x00
lru 0:b283842072f8 475 };
lru 0:b283842072f8 476 uint8_t header_slave[] = { 0xaa, 0x00, 0x00, 0x00, 0x00 };
lru 0:b283842072f8 477 uint16_t data_written = 0;
lru 0:b283842072f8 478 uint16_t write_buffer_size = 0;
lru 0:b283842072f8 479
lru 0:b283842072f8 480 _spi_mutex.lock();
lru 0:b283842072f8 481
lru 0:b283842072f8 482 /* CS reset */
lru 0:b283842072f8 483 nCS = 0;
lru 0:b283842072f8 484
lru 0:b283842072f8 485 /* Exchange header */
lru 0:b283842072f8 486 for (uint8_t i = 0; i < sizeof(header_master); ++i) {
lru 0:b283842072f8 487 header_slave[i] = spi.write(header_master[i]);
lru 0:b283842072f8 488 }
lru 0:b283842072f8 489
lru 0:b283842072f8 490 if (header_slave[0] != 0x02) {
lru 0:b283842072f8 491 goto exit;
lru 0:b283842072f8 492 }
lru 0:b283842072f8 493
lru 0:b283842072f8 494 write_buffer_size = header_slave[2] << 8 | header_slave[1];
lru 0:b283842072f8 495
lru 0:b283842072f8 496 if (write_buffer_size == 0 || write_buffer_size < (data_length + 1)) {
lru 0:b283842072f8 497 goto exit;
lru 0:b283842072f8 498 }
lru 0:b283842072f8 499
lru 0:b283842072f8 500 spi.write(type);
lru 0:b283842072f8 501
lru 0:b283842072f8 502 data_written = data_length;
lru 0:b283842072f8 503 for (uint16_t i = 0; i < data_length; ++i) {
lru 0:b283842072f8 504 spi.write(data[i]);
lru 0:b283842072f8 505 }
lru 0:b283842072f8 506
lru 0:b283842072f8 507 exit:
lru 0:b283842072f8 508 nCS = 1;
lru 0:b283842072f8 509
lru 0:b283842072f8 510 _spi_mutex.unlock();
lru 0:b283842072f8 511
lru 0:b283842072f8 512 return data_written;
lru 0:b283842072f8 513 }
lru 0:b283842072f8 514
lru 0:b283842072f8 515 uint16_t spiRead(uint8_t* data_buffer, const uint16_t buffer_size)
lru 0:b283842072f8 516 {
lru 0:b283842072f8 517 static const uint8_t header_master[] = {0x0b, 0x00, 0x00, 0x00, 0x00};
lru 0:b283842072f8 518 uint8_t header_slave[5] = { 0xaa, 0x00, 0x00, 0x00, 0x00};
lru 0:b283842072f8 519 uint16_t read_length = 0;
lru 0:b283842072f8 520 uint16_t data_available = 0;
lru 0:b283842072f8 521
lru 0:b283842072f8 522 nCS = 0;
lru 0:b283842072f8 523
lru 0:b283842072f8 524 /* Read the header */
lru 0:b283842072f8 525 for (size_t i = 0; i < sizeof(header_master); i++) {
lru 0:b283842072f8 526 header_slave[i] = spi.write(header_master[i]);
lru 0:b283842072f8 527 }
lru 0:b283842072f8 528
lru 0:b283842072f8 529 if (header_slave[0] != 0x02) {
lru 0:b283842072f8 530 goto exit;
lru 0:b283842072f8 531 }
lru 0:b283842072f8 532
lru 0:b283842072f8 533 data_available = (header_slave[4] << 8) | header_slave[3];
lru 0:b283842072f8 534 read_length = data_available > buffer_size ? buffer_size : data_available;
lru 0:b283842072f8 535
lru 0:b283842072f8 536 for (uint16_t i = 0; i < read_length; ++i) {
lru 0:b283842072f8 537 data_buffer[i] = spi.write(0xFF);
lru 0:b283842072f8 538 }
lru 0:b283842072f8 539
lru 0:b283842072f8 540 exit:
lru 0:b283842072f8 541 nCS = 1;
lru 0:b283842072f8 542
lru 0:b283842072f8 543 return read_length;
lru 0:b283842072f8 544 }
lru 0:b283842072f8 545
lru 0:b283842072f8 546 /*
lru 0:b283842072f8 547 * might be split into two parts: the IRQ signaling a real time thread and
lru 0:b283842072f8 548 * the real time thread reading data from the SPI.
lru 0:b283842072f8 549 */
lru 0:b283842072f8 550 void HCI_Isr(void)
lru 0:b283842072f8 551 {
lru 0:b283842072f8 552 _spi_read_sem.release();
lru 0:b283842072f8 553 }
lru 0:b283842072f8 554
lru 0:b283842072f8 555 void spi_read_cb() {
lru 0:b283842072f8 556 uint8_t data_buffer[256];
lru 0:b283842072f8 557 while(true) {
lru 0:b283842072f8 558 _spi_read_sem.wait();
lru 0:b283842072f8 559
lru 0:b283842072f8 560 _spi_mutex.lock();
lru 0:b283842072f8 561 while(irq == 1) {
lru 0:b283842072f8 562 uint16_t data_read = spiRead(data_buffer, sizeof(data_buffer));
lru 0:b283842072f8 563 on_data_received(data_buffer, data_read);
lru 0:b283842072f8 564 }
lru 0:b283842072f8 565 _spi_mutex.unlock();
lru 0:b283842072f8 566 }
lru 0:b283842072f8 567 }
lru 0:b283842072f8 568
lru 0:b283842072f8 569 /**
lru 0:b283842072f8 570 * Unsafe SPI, does not lock when SPI access happens.
lru 0:b283842072f8 571 */
lru 0:b283842072f8 572 ::mbed::SPI spi;
lru 0:b283842072f8 573 DigitalOut nCS;
lru 0:b283842072f8 574 InterruptIn irq;
lru 0:b283842072f8 575 rtos::Thread _spi_thread;
lru 0:b283842072f8 576 uint8_t _spi_thread_stack[SPI_STACK_SIZE];
lru 0:b283842072f8 577 rtos::Semaphore _spi_read_sem;
lru 0:b283842072f8 578 rtos::Mutex _spi_mutex;
lru 0:b283842072f8 579 };
lru 0:b283842072f8 580
lru 0:b283842072f8 581 } // namespace bluenrg
lru 0:b283842072f8 582 } // namespace vendor
lru 0:b283842072f8 583 } // namespace ble
lru 0:b283842072f8 584
lru 0:b283842072f8 585 /**
lru 0:b283842072f8 586 * Cordio HCI driver factory
lru 0:b283842072f8 587 */
lru 0:b283842072f8 588 ble::vendor::cordio::CordioHCIDriver& ble_cordio_get_hci_driver() {
lru 0:b283842072f8 589 static ble::vendor::bluenrg::TransportDriver transport_driver(
lru 0:b283842072f8 590 BLUENRG_PIN_SPI_MOSI,
lru 0:b283842072f8 591 BLUENRG_PIN_SPI_MISO,
lru 0:b283842072f8 592 BLUENRG_PIN_SPI_SCK,
lru 0:b283842072f8 593 BLUENRG_PIN_SPI_nCS,
lru 0:b283842072f8 594 BLUENRG_PIN_SPI_IRQ
lru 0:b283842072f8 595 );
lru 0:b283842072f8 596 static ble::vendor::bluenrg::HCIDriver hci_driver(
lru 0:b283842072f8 597 transport_driver,
lru 0:b283842072f8 598 BLUENRG_PIN_SPI_RESET
lru 0:b283842072f8 599 );
lru 0:b283842072f8 600 return hci_driver;
lru 0:b283842072f8 601 }