HCI Application is a Host Controller Interface application which provides a serial communication to interface with the KW40/KW41/KW3x/QN9080 BLE radio part.
It enables the user to have a way to control the radio through serial commands.
The format of the HCI Command Packet it’s composed by the following parts.
Figure 1. HCI Command Packet
Each command is assigned a 2 byte Opcode which it’s divided into two fields, called the OpCode Group Field (OGF) and OpCode Command Field (OCF).
The OGF uses the upper 6 bits of the Opcode, while the OCF corresponds to the remaining 10 bits.
The OGF of 0x3F is reserved for vendor-specific debug commands. The organization of the opcodes allows additional information to be inferred without fully decoding the entire Opcode.
For further information regarding this, please check the BLUETOOTH SPECIFICATION Version 5.0 | Vol 2, Part E, 5.4 EXCHANGE OF HCI-SPECIFIC INFORMATION.
This document will guide you through the implementation of custom HCI commands in the KW36, but it can be applied as well for the rest of the NXP Bluetooth LE MCU’s that support HCI.
The following changes were made and tested in the FREEDOM KW38 and will generate a continuous with both channel and power configurable.
You will need to perform the following changes to the HCI black box demo.
Modify the hci_transport.h public constants and macros section by adding:
#define gHciCustomCommandOpcodeUpper (0xFC90)
#define gHciCustomCommandOpcodeLower (0xFC00)
#define gHciInCustomVendorCommandsRange(x) (((x) <= gHciCustomCommandOpcodeUpper) && \
((x) >= gHciCustomCommandOpcodeLower))
In this case, the opcodes 0xFC90 to 0xFC00 are not used by our stack. These opcodes meet the requirements for vendor specific commands (OCF = 3F).
Then you will need to declare the handler for the custom command.
void Hcit_InstallCustomCommandHandler(hciTransportInterface_t mCustomInterfaceHandler);
In the hcit_serial_interface.c modify the following :
Add in the private memory declarations section
static hciTransportInterface_t mCustomTransportInterface = NULL;
Change the Hcit_SendMessage as it is shown:
static inline void Hcit_SendMessage(void)
{
uint16_t opcode = 0;
/* verify if this is an event packet */
if(mHcitData.pktHeader.packetTypeMarker == gHciEventPacket_c)
{
/* verify if this is a command complete event */
if(mHcitData.pPacket->raw[0] == gHciCommandCompleteEvent_c)
{
/* extract the first opcode to verify if it is a custom command */
opcode = mHcitData.pPacket->raw[3] + (mHcitData.pPacket->raw[4] << 8);
}
}
/* verify if command packet */
else if(mHcitData.pktHeader.packetTypeMarker == gHciCommandPacket_c)
{
/* extract opcode */
opcode = mHcitData.pPacket->raw[0] + (mHcitData.pPacket->raw[1] << 8);
}
if(gHciInCustomVendorCommandsRange(opcode))
{
if(mCustomTransportInterface)
{
mCustomTransportInterface( mHcitData.pktHeader.packetTypeMarker,
mHcitData.pPacket,
mHcitData.bytesReceived);
}
}
else
{
/* Send the message to HCI */
mTransportInterface( mHcitData.pktHeader.packetTypeMarker,
mHcitData.pPacket,
mHcitData.bytesReceived);
}
MEM_BufferFree( mHcitData.pPacket );
mHcitData.pPacket = NULL;
mPacketDetectStep = mDetectMarker_c;
}
Implement the registration of the handler
void Hcit_InstallCustomCommandHandler(hciTransportInterface_t mCustomInterfaceHandler)
{
OSA_EXT_InterruptDisable();
mCustomTransportInterface = mCustomInterfaceHandler;
OSA_EXT_InterruptEnable();
return;
}
Once those changes are done, we will need to modify the hci_black_box.c with the following changes.
Add the files to support HCI Custom commands.
#include "hci_transport.h"
#include "fsl_xcvr.h"
Define the custom commands, in this case, we will create some to turn ON/OFF the continuous wave as well as to set up the channel and power.
//@CC custom command
#define CUSTOM_HCI_CW_EVENT_SIZE (0x04)
#define CUSTOM_HCI_EVENT_SUCCESS (0x00)
#define CUSTOM_HCI_EVENT_FAIL (0x01)
Also, adding some app auxiliar variables
static uint16_t channelCC = 2402;
static uint8_t powerCC = 0x3E;
Create the custom event packet
uint8_t eventPacket[6] = {gHciCommandCompleteEvent_c, CUSTOM_HCI_CW_EVENT_SIZE, 1, 0, 0, 0 };
In the main_task() after the BleApp_Init() register the callback for the custom commands.
/* Initialize peripheral drivers specific to the application */
BleApp_Init();
//Register the callback for the custom commands.
Hcit_InstallCustomCommandHandler(BleApp_CustomCommandsHandle);
Once that it’s added, add the handler for the command
bleResult_t BleApp_CustomCommandsHandle(hciPacketType_t packetType, void* pPacket, uint16_t packetSize)
{
uint16_t opcode = 0;
uint8_t error=0;
switch(packetType)
{
case gHciCommandPacket_c:
opcode = ((uint8_t*)pPacket)[0] + (((uint8_t*)pPacket)[1] << 8);
if (opcode >= 0xfc00 & opcode <= 0xfc50)
{
channelCC = 2400+(opcode - 0xfc00);
eventPacket[5] = CUSTOM_HCI_EVENT_SUCCESS;
}
else if (opcode <= 0xfc70)
{
powerCC = (opcode-0xfc50)*2;
if (gXcvrSuccess_c ==XCVR_ForcePAPower(powerCC)
eventPacket[5] = CUSTOM_HCI_EVENT_SUCCESS;
else eventPacket[5] = CUSTOM_HCI_EVENT_FAIL;
}
else if (opcode == 0xfc80)
{
if (gXcvrSuccess_c == XCVR_DftTxCW(channelCC*1000000))
{
eventPacket[5] = CUSTOM_HCI_EVENT_SUCCESS;
}
else eventPacket[5] = CUSTOM_HCI_EVENT_FAIL;
}
else if(opcode == 0xfc90)
{
XCVR_ForceTxWd();
/* Initialize the PHY as BLE */
XCVR_Init(BLE_MODE, DR_1MBPS);
eventPacket[5] = CUSTOM_HCI_EVENT_SUCCESS;
}
else
{
eventPacket[5] = CUSTOM_HCI_EVENT_FAIL;
}
eventPacket[3] = (uint8_t)opcode;
eventPacket[4] = (uint8_t)(opcode >> 8);
Hcit_SendPacket(gHciEventPacket_c, eventPacket, sizeof(eventPacket));
break;
default:
break;
}
}
The format of this HCI command is:
01 XX FC 00
Where:
When 0<=XX<=50: set the transmitting channel. The transmitting frequency is (2400+XX (in hexadecimal) )*1000MHz
When 50<xx<70: set the transmitting power. 51 is the minimum Tx power and 69 corresponds to the maximum Tx power.
When XX = 80: start the CW transmission
When XX = 90: Stop the CW transmission
For example, if you want to transmit a CW on the frequency 2420MHz at maximum frequency, you should send the following command:
01 14 FC 00
01 69 FC 00
01 80 FC 00
whenever you want to change the channel or Tx power, please stop the ongoing Tx first by sending
01 90 FC 00
Then repeat the previous step.
If you want to add some parameter to it, please consider that the fourth byte of the packet will correspond to the number of parameters to enter and you will need to indicate it there.