Apollo 3 nimBLE support

This commit is contained in:
Tian Zeng
2022-03-23 11:56:33 +01:00
committed by Andrzej Kaczmarek
parent adc6b03646
commit 31c2e74807
5 changed files with 437 additions and 0 deletions
+46
View File
@@ -0,0 +1,46 @@
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you 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.
#
pkg.name: nimble/transport/apollo3
pkg.description: >
HCI transport for Ambiq Apollo3
The BLE subsystem implemented on Apollo3 is a fully integrated system which is accessed
via the BLE interface block, and provides autonomous clock and power management.
The subsystem has a 32-bit core which runs from a 32 MHz oscillator, and is dedicated
to running the lower portion of the BLE stack with a standard Host Controller interface (HCI).
This core does not support user programming, as all user code runs on the main Cortex M4 processor.
Software leverages the fully HCI compliant interface for Bluetooth operation.
A series of proprietary HCI commands are also leveraged to provide additional performance and low power operation.
The BLE controller and host can be configured to support up to seven simultaneous connections on chip revision A1 (4 on chip revision B0).
Secure connections and extended packet length are also supported.
See the Apollo3 Datasheet for a full description of the BLE subsystem.
pkg.author: "Apache Mynewt <[email protected]>"
pkg.homepage: "http://mynewt.apache.org/"
pkg.keywords:
- ble
- bluetooth
- apollo3
pkg.deps:
- nimble
- nimble/transport/common/hci_h4
- "@apache-mynewt-core/kernel/os"
pkg.apis:
- ble_transport
@@ -0,0 +1,351 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 <assert.h>
#include <string.h>
#include <os/mynewt.h>
#include <nimble/hci_common.h>
#include <nimble/transport.h>
#include <nimble/transport/hci_h4.h>
#include "am_mcu_apollo.h"
#define HCI_CMD_HDR_LEN (3) /*!< \brief Command packet header length */
/* Tx power level in dBm. */
typedef enum {
TX_POWER_LEVEL_MINUS_10P0_dBm = 0x4,
TX_POWER_LEVEL_MINUS_5P0_dBm = 0x5,
TX_POWER_LEVEL_0P0_dBm = 0x8,
TX_POWER_LEVEL_PLUS_3P0_dBm = 0xF,
TX_POWER_LEVEL_INVALID = 0x10,
} txPowerLevel_t;
/* Structure for holding outgoing HCI packets. */
typedef struct {
uint32_t len;
uint32_t data[MYNEWT_VAL(BLE_TRANSPORT_APOLLO3_MAX_TX_PACKET) / sizeof(uint32_t)];
} hci_drv_write_t;
uint32_t g_read_buf[MYNEWT_VAL(BLE_TRANSPORT_APOLLO3_MAX_RX_PACKET) / sizeof(uint32_t)];
/* BLE module handle for Ambiq HAL functions */
void *ble_handle;
uint8_t g_ble_mac_address[6] = {0};
static struct hci_h4_sm hci_apollo3_h4sm;
static void
apollo3_ble_hci_trans_rx_process(void)
{
uint32_t len;
int rlen;
uint8_t *buf = (uint8_t *)g_read_buf;
am_hal_ble_blocking_hci_read(ble_handle, (uint32_t *)buf, &len);
/* NOTE: Ambiq Apollo3 controller does not have local supported lmp features implemented
* The command will always return 0 so we overwrite the buffer here
*/
if(buf[4] == 0x03 && buf[5] == 0x10 && len == 15) {
memset(&buf[11], 0x60, sizeof(uint8_t));
}
rlen = hci_h4_sm_rx(&hci_apollo3_h4sm, buf, len);
assert(rlen >= 0);
}
/* Interrupt handler that looks for BLECIRQ. This gets set by BLE core when there is something to read */
static void
apollo3_hci_int(void)
{
uint32_t int_status;
/* Read and clear the interrupt status. */
int_status = am_hal_ble_int_status(ble_handle, true);
am_hal_ble_int_clear(ble_handle, int_status);
/* Handle any DMA or Command Complete interrupts. */
am_hal_ble_int_service(ble_handle, int_status);
/* If this was a BLEIRQ interrupt, attempt to start a read operation. */
if (int_status & AM_HAL_BLE_INT_BLECIRQ)
{
/* Lower WAKE */
am_hal_ble_wakeup_set(ble_handle, 0);
/* Call read function to pull in data from controller */
apollo3_ble_hci_trans_rx_process();
}
else {
assert(0);
}
}
/* Boot the radio. */
uint32_t
apollo3_hci_radio_boot(bool is_cold_boot)
{
uint32_t xtal_retry_cnt = 0;
uint32_t am_boot_status;
am_hal_mcuctrl_device_t device_info;
am_hal_ble_config_t ble_config =
{
/* Configure the HCI interface clock for 6 MHz */
.ui32SpiClkCfg = AM_HAL_BLE_HCI_CLK_DIV8,
/* Set HCI read and write thresholds to 32 bytes each. */
.ui32ReadThreshold = 32,
.ui32WriteThreshold = 32,
/* The MCU will supply the clock to the BLE core. */
.ui32BleClockConfig = AM_HAL_BLE_CORE_MCU_CLK,
/* Note: These settings only apply to Apollo3 A1/A2 silicon, not B0 silicon.
* Default settings for expected BLE clock drift (measured in PPM).
*/
.ui32ClockDrift = 0,
.ui32SleepClockDrift = 50,
/* Default setting - AGC Enabled */
.bAgcEnabled = true,
/* Default setting - Sleep Algo enabled */
.bSleepEnabled = true,
/* Apply the default patches when am_hal_ble_boot() is called. */
.bUseDefaultPatches = true,
};
/* Configure and enable the BLE interface. */
am_boot_status = AM_HAL_STATUS_FAIL;
while (am_boot_status != AM_HAL_STATUS_SUCCESS) {
am_hal_pwrctrl_low_power_init();
am_hal_ble_initialize(0, &ble_handle);
am_hal_ble_power_control(ble_handle, AM_HAL_BLE_POWER_ACTIVE);
am_hal_ble_config(ble_handle, &ble_config);
/*
* Delay 1s for 32768Hz clock stability. This isn't required unless this is
* our first run immediately after a power-up.
*/
if (is_cold_boot) {
os_time_delay(OS_TICKS_PER_SEC);
}
/* Attempt to boot the radio. */
am_boot_status = am_hal_ble_boot(ble_handle);
/* Check our status, exit if radio is running */
if (am_boot_status == AM_HAL_STATUS_SUCCESS) {
break;
}
else if (am_boot_status == AM_HAL_BLE_32K_CLOCK_UNSTABLE) {
/* If the radio is running, but the clock looks bad, we can try to restart. */
am_hal_ble_power_control(ble_handle, AM_HAL_BLE_POWER_OFF);
am_hal_ble_deinitialize(ble_handle);
/* We won't restart forever. After we hit the maximum number of retries, we'll just return with failure. */
if (xtal_retry_cnt++ < MYNEWT_VAL(BLE_TRANSPORT_APOLLO3_MAX_XTAL_RETRIES)) {
os_time_delay(OS_TICKS_PER_SEC);
}
else {
return SYS_EUNKNOWN;
}
}
else {
am_hal_ble_power_control(ble_handle, AM_HAL_BLE_POWER_OFF);
am_hal_ble_deinitialize(ble_handle);
/*
* If the radio failed for some reason other than 32K Clock
* instability, we should just report the failure and return.
*/
return SYS_EUNKNOWN;
}
}
/* Set the BLE TX Output power to 0dBm. */
am_hal_ble_tx_power_set(ble_handle, TX_POWER_LEVEL_0P0_dBm);
/* Enable interrupts for the BLE module. */
am_hal_ble_int_clear(ble_handle, (AM_HAL_BLE_INT_CMDCMP |
AM_HAL_BLE_INT_DCMP |
AM_HAL_BLE_INT_BLECIRQ));
am_hal_ble_int_enable(ble_handle, (AM_HAL_BLE_INT_CMDCMP |
AM_HAL_BLE_INT_DCMP |
AM_HAL_BLE_INT_BLECIRQ));
/* When it's is_cold_boot, it will use Apollo's Device ID to form Bluetooth address. */
if (is_cold_boot) {
am_hal_mcuctrl_info_get(AM_HAL_MCUCTRL_INFO_DEVICEID, &device_info);
/* Bluetooth address formed by ChipID1 (32 bits) and ChipID0 (8-23 bits). */
memcpy(g_ble_mac_address, &device_info.ui32ChipID1, sizeof(device_info.ui32ChipID1));
/* ui32ChipID0 bit 8-31 is test time during chip manufacturing */
g_ble_mac_address[4] = (device_info.ui32ChipID0 >> 8) & 0xFF;
g_ble_mac_address[5] = (device_info.ui32ChipID0 >> 16) & 0xFF;
}
NVIC_EnableIRQ(BLE_IRQn);
return 0;
}
/* Wake update helper function */
static void
apollo3_update_wake(void)
{
AM_CRITICAL_BEGIN;
/* Set WAKE if there's something in the write queue, but not if SPISTATUS or IRQ is high. */
if ((BLEIFn(0)->BSTATUS_b.SPISTATUS == 0) && (BLEIF->BSTATUS_b.BLEIRQ == false)) {
am_hal_ble_wakeup_set(ble_handle, 1);
/* If we've set wakeup, but IRQ came up at the same time, we should just lower WAKE again. */
if (BLEIF->BSTATUS_b.BLEIRQ == true) {
am_hal_ble_wakeup_set(ble_handle, 0);
}
}
AM_CRITICAL_END;
}
/*
* Function used by the BLE stack to send HCI messages to the BLE controller.
* The payload is placed into a queue and the controller is turned on. When it is ready
* an interrupt will fire to handle sending a message
*/
static uint8_t
apollo3_hci_write(uint8_t type, uint16_t len, uint8_t *data)
{
uint8_t *write_ptr;
hci_drv_write_t write_buf;
/* comparison compensates for the type byte at index 0. */
if (len > (MYNEWT_VAL(BLE_TRANSPORT_APOLLO3_MAX_TX_PACKET)-1)) {
return 0;
}
/* Set all of the fields in the hci write structure. */
write_buf.len = len + 1;
write_ptr = (uint8_t *) write_buf.data;
*write_ptr++ = type;
for (uint32_t i = 0; i < len; i++) {
write_ptr[i] = data[i];
}
/* Wake up the BLE controller. */
apollo3_update_wake();
/* Wait on SPI status before writing */
while (BLEIFn(0)->BSTATUS_b.SPISTATUS) {
os_time_delay(1);
}
am_hal_ble_blocking_hci_write(ble_handle, AM_HAL_BLE_RAW, write_buf.data, write_buf.len);
return 0;
}
static int
apollo3_ble_hci_acl_tx(struct os_mbuf *om)
{
struct os_mbuf *x;
int rc = 0;
x = om;
while (x) {
rc = apollo3_hci_write(HCI_H4_ACL, x->om_len, x->om_data);
if (rc < 0) {
break;
}
x = SLIST_NEXT(x, om_next);
}
os_mbuf_free_chain(om);
return (rc < 0) ? BLE_ERR_MEM_CAPACITY : 0;
}
static int
apollo3_ble_hci_frame_cb(uint8_t pkt_type, void *data)
{
int rc;
switch (pkt_type) {
case HCI_H4_ACL:
rc = ble_transport_to_hs_acl(data);
break;
case HCI_H4_EVT:
rc = ble_transport_to_hs_evt(data);
break;
default:
assert(0);
break;
}
return rc;
}
static void
apollo3_ble_hci_init(void)
{
SYSINIT_ASSERT_ACTIVE();
/* Enable interrupt to handle read based on BLECIRQ */
NVIC_SetVector(BLE_IRQn, (uint32_t)apollo3_hci_int);
/* Initial coldboot configuration */
apollo3_hci_radio_boot(1);
}
int
ble_transport_to_ll_cmd_impl(void *buf)
{
int rc;
uint8_t *cmd = buf;
int len = HCI_CMD_HDR_LEN + cmd[2];
rc = apollo3_hci_write(HCI_H4_CMD, len, cmd);
ble_transport_free(cmd);
return (rc < 0) ? BLE_ERR_MEM_CAPACITY : 0;
}
int
ble_transport_to_ll_acl_impl(struct os_mbuf *om)
{
return apollo3_ble_hci_acl_tx(om);
}
void
ble_transport_ll_init(void)
{
hci_h4_sm_init(&hci_apollo3_h4sm, &hci_h4_allocs_from_ll,
apollo3_ble_hci_frame_cb);
apollo3_ble_hci_init();
}
+37
View File
@@ -0,0 +1,37 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you 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.
#
syscfg.defs:
BLE_TRANSPORT_APOLLO3_MAX_TX_PACKET:
description: >
Maximum TX packet size to controller in bytes.
value: 256
BLE_TRANSPORT_APOLLO3_MAX_RX_PACKET:
description: >
Maximum RX packet size from controller in bytes.
value: 256
BLE_TRANSPORT_APOLLO3_MAX_XTAL_RETRIES:
description: >
Maximum number of retries to boot radio when clock is found to be unstable.
value: 10
syscfg.restrictions:
- '!BLE_CONTROLLER'
- '!BLE_HS_FLOW_CTRL'
+2
View File
@@ -44,6 +44,8 @@ pkg.deps.'BLE_TRANSPORT_HS == "uart"':
- nimble/transport/uart
pkg.deps.'BLE_TRANSPORT_HS == "usb"':
- nimble/transport/usb
pkg.deps.'BLE_TRANSPORT_LL == "apollo3"':
- nimble/transport/apollo3
pkg.deps.BLE_MONITOR_RTT:
- "@apache-mynewt-core/hw/drivers/rtt"
+1
View File
@@ -44,6 +44,7 @@ syscfg.defs:
- dialog_cmac
- nrf5340
- socket
- apollo3
- custom
BLE_TRANSPORT_ACL_COUNT: