Telink 2.4GHz Proprietary SDK Developer Handbook
SDK Overview
The Telink 2.4GHz SDK is applicable for Telink TLSR8208, TLSR8373, TLSR825x,TLSR8359, TLSR827x,TLSR8355, TLSR921x, TLSR951x series chips. It is used for the development of engineering applications related to the 2.4GHz proprietary protocol. Users can develop their own applications based on the 2.4GHz SDK by referring to the technical handbook.
Hardware Platform
The 2.4GHz proprietary SDK currently has four basic SDKs for different hardware platforms.
| chip | chip details | sdk |
|---|---|---|
| B80 | TLSR8208, TLSR8373 | telink_b80_2.4g_proprietary_sdk |
| B85 | TLSR825x,TLSR8359 | telink_b85_2.4g_proprietary_sdk |
| B87 | TLSR827x,TLSR8355 | telink_b87_2.4g_proprietary_sdk |
| B91 | TLSR921x, TLSR951x | telink_b91_2.4g_proprietary_sdk |
Different chips may have different peripheral functions, but the principle of proprietary protocols is similar.
Software Development Environment
Software tool:
(1) Integrated Development Environment
TLSR8 Chips: Telink IDE for TC32

TLSR9 Chips: Telink RDS IDE for RISC-V

(2) Burning and Debugging Tool
Telink Burning and Debugging Tools.

Software development kit:
Select the SDK that the chip type requires.
Directory Structure
The directory structure is as follows (taking B87 as an example):

Besides boot, link, common, drivers, vendor, div_mod.S as the basic part of the driver SDK, 2.4GHz SDK also has ble_adv_tx, docs, epd, fw_update, genfsk_ll, ota, script, tpll, tpsll.
The following will briefly introduce the part similar with driver SDK, more detailed introduction please refer to the "Telink Driver SDK Developer Handbook".
boot:
This folder contains boot files.

link:
The link file.

div_mod.S:
The div_mod.S file is used to process the assembly operations of division and remainder.

common:
This folder contains some intermediate files across files, where sdk_version is used to store the version information of the SDK into the flash segment.

vendor:
This folder contains the routine code for all modules.
ble_adv_tx:
This folder contains the BLE broadcast tx interface, corresponding to the ble_beacon_tx routine.

docs:
This folder contains the relevant handbook documents.
epd:
This folder is the interface to the corresponding epd_driver_demo routine, which acts as an ink screen routine to operate the ESL tag.

fw_update:
This folder is the interface to the corresponding uart_fw_update routine, which is used for serial communication to upgrade the firmware.


genfsk_ll:
This folder is the Genfsk_ll interface for the Genfsk_ll communication routines.


ota:
This folder is the interface to the corresponding ota routine, which is used for wireless communication to upgrade the firmware.


script:
This file is used to place text files, such as adding crc calibration information to the end of the bin file to check whether the firmware upgrade is successful.

tpll:
This folder is the TPLL (Telink proprietary link layer) interface and is used as the TPLL communication routine.


tpsll:
This folder is the TPSLL (Telink proprietary stack link layer) interface used as the TPSLL communication routine.


2.4GHz Proprietary Protocol
The 2.4GHz proprietary protocol code is the main component of 2.4GHz proprietary SDK. The detailed protocol information can be found in the relevant chapters. Due to the introduction of flexible packet formats with subsequent chip updates, some modifications have been made to the related examples. Using the B87 and TL321x as examples, this section will reference the examples and primarily introduce their functionality, usage, and test phenomena to help users get started easily. Based on the different packet formats, the 2.4GHz proprietary protocol code is classified into three categories as shown in the table below:
| Type | Mode |
|---|---|
| tpll (Telink Primary Link Layer) | ptx, prx |
| tpsll (Telink Proprietary Stack Link Layer) | stx, srx, stx2rx, srx2tx |
| genfsk_ll (Telink Generic Frequency Shift Keying Link Layer) | tx, rx, stx, srx, stx2rx, srx2tx |
Among them, tpll and genfsk_ll are 2.4GHz proprietary packet formats, and tpsll is a Bluetooth LE packet format.
TPLL (Telink Primary Link Layer)
The related routines for Telink 2.4GHz proprietary protocol TPLL (Telink Primary Link Layer) is shown in the following table.
| Routine | Function introduction |
|---|---|
| tpll_ptx | Sending packets through the ptx state machine and selecting whether a responsive packet is required or not |
| tpll_prx | Receive packets and select whether to send a responsive packet via the prx state machine |
| tpll_fast_ptx | Quickly get ready to send packets and select whether a responsive packet is needed via the ptx state machine |
| tpll_fast_prx | Quickly get ready to receive packets and choose whether to send a responsive packet via the prx state machine |
tpll_ptx/prx
The usage steps for B87 chips:
- tpll_ptx:
//1. Initialization configuration
//Set the data transfer rate
TPLL_Init(TPLL_BITRATE_2MBPS);
//Set the signal strength
TPLL_(TPLL_RF_POWER_0DBM);
//Set the synchronization word length
TPLL_SetAddressWidth(ADDRESS_WIDTH_5BYTES);
//Close all channels
TPLL_ClosePipe(TPLL_PIPE_ALL);
//Set the transmission channel and synchronization word
TPLL_SetAddress(TPLL_PIPE0, tx_address);
//Open the channel
TPLL_OpenPipe(TPLL_PIPE0);
//Open tx channel
TPLL_SetTXPipe(TPLL_PIPE0);
//Set the ptx mode
TPLL_ModeSet(TPLL_MODE_PTX);
//Set the communication frequency
TPLL_SetRFChannel(chn);
//Set the number of active retransmissions and the delayed retransmission time, and set to 0 to disable active retransmission
TPLL_SetAutoRetry(0,150);
//Set the rx timeout time, when the transmission rate is 250kbps, the rx timeout time needs to be set to 1000us for reasonable communication
TPLL_RxTimeoutSet(500);
//Set the rx settle time
TPLL_RxSettleSet(80);
//Set the tx settle time
TPLL_TxSettleSet(149);
//This delay is unnecessary
WaitUs(150);
//Clear all interrupt sources
irq_clr_src();
//Clear all interrupt flag bits
rf_irq_clr_src(FLD_RF_IRQ_ALL);
//Disable all interrupts of rf communication
rf_irq_disable(FLD_RF_IRQ_ALL);
//Enable rf communication interrupt
irq_enable_type(FLD_IRQ_ZB_RT_EN);
//Enable specific rf communication interrupt
rf_irq_enable(FLD_RF_IRQ_TX|FLD_RF_IRQ_TX_DS|FLD_RF_IRQ_RETRY_HIT|FLD_RF_IRQ_RX_DR);
//disable interrupt
irq_enable();
//Write the data to be sent
tmp = TPLL_WriteTxPayload(PTX_CHANNEL, tx_data, tx_len);
//Set the leading code to 8byte
TPLL_Preamble_Set(8);
//Start ptx
TPLL_PTXTrig();
//2. Interrupt handling
unsigned short src_rf = rf_irq_src_get();
unsigned char pipe = TPLL_GetTXPipe();
if (src_rf & FLD_RF_IRQ_TX)
{
rf_irq_clr_src(FLD_RF_IRQ_TX);
tx_irq_cnt_tx++;
}
if (src_rf & FLD_RF_IRQ_INVALID_PID)
{
rf_irq_clr_src(FLD_RF_IRQ_INVALID_PID);
tx_irq_cnt_invalid_pid++;
}
if (src_rf & FLD_RF_IRQ_RETRY_HIT)
{
rf_irq_clr_src(FLD_RF_IRQ_RETRY_HIT);
tx_irq_cnt_max_retry++;
maxretry_flag = 1;
//Update send fifo pointer, send new packet
TPLL_UpdateTXFifoRptr(pipe);
}
if (src_rf & FLD_RF_IRQ_TX_DS)
{
rf_irq_clr_src(FLD_RF_IRQ_TX_DS);
tx_irq_cnt_tx_ds++;
ds_flag = 1;
}
if (src_rf & FLD_RF_IRQ_RX_DR)
{
rf_irq_clr_src(FLD_RF_IRQ_RX_DR);
tx_irq_cnt_rx_dr++;
rx_flag = 1;
}
irq_clr_src();
rf_irq_clr_src(FLD_RF_IRQ_ALL);
//3. main_loop handles sending
//Non-empty packets received or maximum number of retransmissions reached
if (ds_flag ||maxretry_flag)
{
if(ds_flag)
gpio_toggle(DEBUG_PIN);
ds_flag = 0;
maxretry_flag = 0;
WaitMs(500);
//Write the data to be sent
tmp = TPLL_WriteTxPayload(PTX_CHANNEL, tx_data, tx_len);
if(tmp)
{
//Start ptx after successful write
TPLL_PTXTrig();
}
}
- tpll_prx:
//1. Initialization configuration
//Set the data transfer rate
TPLL_Init(TPLL_BITRATE_2MBPS);
//Set the signal strength
TPLL_SetOutputPower(TPLL_RF_POWER_0DBM);
//Set the synchronization word length
TPLL_SetAddressWidth(ADDRESS_WIDTH_5BYTES);
//Close all channels
TPLL_ClosePipe(TPLL_PIPE_ALL);
//Set the transmission channel and synchronization word
TPLL_SetAddress(TPLL_PIPE0, tx_address);
//Open the channel
TPLL_OpenPipe(TPLL_PIPE0);
//Set the prx mode
TPLL_ModeSet(TPLL_MODE_PRX);
//Set the communication frequency
TPLL_SetRFChannel(chn);
//Set the rx settle time
TPLL_RxSettleSet(80);
//Set the tx settle time
TPLL_TxSettleSet(149);
//Clear all interrupt sources
irq_clr_src();
//Clear all interrupt flag bits
rf_irq_clr_src(FLD_RF_IRQ_ALL);
//Disable all interrupts of rf communication
rf_irq_disable(FLD_RF_IRQ_ALL);
//Enable rf communication interrupt
irq_enable_type(FLD_IRQ_ZB_RT_EN);
//Enable specific rf communication interrupt
rf_irq_enable(FLD_RF_IRQ_TX|FLD_RF_IRQ_TX_DS|FLD_RF_IRQ_RX_DR);
//Enable interrupt
irq_enable();
//Start prx
TPLL_PRXTrig();
Timestamp_value = clock_time();
//2. Interrupt handling
unsigned short src_rf = rf_irq_src_get();
if (src_rf & FLD_RF_IRQ_RX_DR) {
rf_irq_clr_src(FLD_RF_IRQ_RX_DR);
rx_irq_cnt_rx_dr++;
rx_flag = 1;
}
if (src_rf & FLD_RF_IRQ_INVALID_PID) {
rf_irq_clr_src(FLD_RF_IRQ_INVALID_PID);
rx_irq_cnt_invalid_pid++;
}
if (src_rf & FLD_RF_IRQ_TX) {
rf_irq_clr_src(FLD_RF_IRQ_TX);
rx_irq_cnt_tx++;
}
if (src_rf & FLD_RF_IRQ_TX_DS) {
rf_irq_clr_src(FLD_RF_IRQ_TX_DS);
rx_irq_cnt_tx_ds++;
}
irq_clr_src();
rf_irq_clr_src(FLD_RF_IRQ_ALL);
//3. main_loop handles sending
//When the correct packet is received
if(1 == rx_flag)
{
rx_flag = 0;
//Get payload length and pipe value
length_pip_ret = TPLL_ReadRxPayload(&rx_data);
//Calculation of reception time
Rx_interval_us = (TPLL_GetTimestamp() - Timestamp_value) >> 4;
//Get the instantaneous clock when the synchronization word is successfully correlated
Timestamp_value = TPLL_GetTimestamp();
//RSSI(Received Signal Strength Indicator)Getting Received Signal Strength
Rssi_value = TPLL_GetRxRssiValue();
//If tx fifo is empty
while(!TPLL_TxFifoEmpty(0));
//Write responsive packet
TPLL_WriteAckPayload(TPLL_PIPE0, ack_payload, ack_payload_length);
}
Test phenomena:
The ptx and prx routines were burned on both boards to communicate. Using the BDT tool, you can see the individual interrupt counts of the prx and ptx, as well as the value of the packets received. Normally the interrupt counts for rx_dr\rx_ds\tx should be very close.


The usage steps for TL321x chips:
General structure configuration:
//Configure the parameters of the CRC structure.
TPLL_CrcConfig_t TPLL_CrcConfig = {
.init_value = 0xffffffff,
.poly = 0x00001021,
.xor_out = 0,
.byte_order = 1,
.start_cal_pos = 0,
.len = 2,
};
//If packet filtering is enabled, you need to set the field range for packet filtering, the data fields to be matched, and the matching threshold.
rf_pkt_flt_t TPLL_PktFlt = {
.rf_pkt_flt_start = 4,//start at h0
.rf_pkt_flt_end = 11,
.rf_pkt_match_threshold = 24,//max 64 bit
.rf_pkt_match_low=0x00000000,
.rf_pkt_match_high=0x00030201,
.rf_pkt_mask_low=0x00000000,
.rf_pkt_mask_high=0x00ffffff,//if bit is 1 means this bit should matched
};
//If you choose the flexible packet format, you need to configure the header content, including h0, h1, length, and PID, as well as the no-ack position and initial value. Note that the size of h0, h1, and length is in bits, and their sum must be a multiple of 8.
TPLL_GenericHeader_t TPLL_GenericHeader = {
.h0_size = 5,
.h1_size = 6,
.length_size = 5,
.h0_val = 26,//no ack 0x22
.h1_val = 14,
.length_val = TX_PAYLOAD_LEN,
.pid_start_bit = 0,//start at h0
.noack_start_bit = 2,
};
- tpll_ptx:
//1. Initialization configuration
//Set the packet format
TPLL_SetFormatMode(TPLL_MODE_GENERIC_FORMAT);
//Set the data transfer rate
TPLL_SetBitrate(TPLL_BITRATE_2MBPS);
//Set the signal strength
TPLL_SetOutputPower(RF_POWER_INDEX_N0p07dBm);
//Set the synchronization word length
TPLL_SetAddressWidth(ADDRESS_WIDTH_5BYTES);
//Set packet filtering conditions
#if(PRI_FLT_MODE_EN)
TPLL_PktFilter(TPLL_PktFlt);
#endif
//Close all channels
TPLL_ClosePipe(TPLL_PIPE_ALL);
//Set the transmission channel and synchronization word
TPLL_SetAddress(TPLL_PIPE0, tx_address);
//Open the channel
TPLL_OpenPipe(TPLL_PIPE0);
//Open tx channel
TPLL_SetTXPipe(TPLL_PIPE0);
//Set dma tx/rx buffer
TPLL_DmaInit(rx_buf,ptx_buffer);
//Set the prx mode
TPLL_ModeSet(TPLL_MODE_PTX);
//Set the communication frequency
TPLL_SetRFChannel(chn);
//Set the number of active retransmissions and the delayed retransmission time, and set to 0 to disable active retransmission
TPLL_SetAutoRetry(0,150);
//Set the rx timeout time, when the transmission rate is 250kbps, the rx timeout time needs to be set to 1000us for reasonable communication
TPLL_RxTimeoutSet(500);
//Set the rx settle time
TPLL_RxSettleSet(80);
//Set the tx settle time
TPLL_TxSettleSet(149);
//Set the preamble length
TPLL_Preamble_Set(8);
//Set crc
TPLL_CrcSet(TPLL_CrcConfig);
//This delay is unnecessary
WaitUs(150);
//Enable interrupt
core_interrupt_enable()
//Enable rf interrupt
plic_interrupt_enable(IRQ_ZB_RT);
//Clear all interrupt flags
rf_clr_irq_mask(FLD_RF_IRQ_ALL);
//Clear all interrupt flags
rf_irq_clr_src(FLD_RF_IRQ_ALL);
//Enable specific rf communication interrupt
rf_set_irq_mask(FLD_RF_IRQ_TX | FLD_RF_IRQ_TX_DS | FLD_RF_IRQ_TX_RETRYCNT|FLD_RF_IRQ_RX_DR | FLD_RF_IRQ_PKT_UNMATCH | FLD_RF_IRQ_PKT_MATCH);
//Write the data to be sent
TPLL_WriteTxPayload(PTX_PIPE, ptx_buffer, (unsigned char *)tx_data, 16);
//Start ptx
TPLL_PTXTrig();
//2. Interrupt handling
unsigned char pipe = TPLL_GetTXPipe();
if (rf_get_irq_status(FLD_RF_IRQ_TX))
{
reg_rf_irq_status = FLD_RF_IRQ_TX;
tx_irq_cnt_tx++;
tx_done_flag = 1;
}
if (rf_get_irq_status(FLD_RF_IRQ_INVALID_PID))
{
reg_rf_irq_status = FLD_RF_IRQ_INVALID_PID;
tx_irq_cnt_invalid_pid++;
}
if (rf_get_irq_status(FLD_RF_IRQ_TX_RETRYCNT))
{
//tx_retrycnt_irq:Maximum number of TX retransmits interrupt.
//Asserted when retry counter reaches the max numbers.
reg_rf_irq_status = FLD_RF_IRQ_TX_RETRYCNT;
tx_irq_cnt_max_retry++;
maxretry_flag = 1;
//adjust rptr
TPLL_UpdateTXFifoRptr(pipe);
}
if (rf_get_irq_status(FLD_RF_IRQ_TX_DS))
{
//Data Sent TX FIFO interrupt. Asserted when packet transmitted on TX.
//If AUTO_ACK is activated, this bit is set high only when ACK is received.
reg_rf_irq_status = FLD_RF_IRQ_TX_DS;
tx_irq_cnt_tx_ds++;
ds_flag = 1;
}
if (rf_get_irq_status(FLD_RF_IRQ_RX_DR))
{
//rx_dr_irq:Data Ready RX FIFO interrupt. Asserted when new data arrives RX FIFO.
//The RX_DR IRQ is asserted by a new packet arrival even
reg_rf_irq_status = FLD_RF_IRQ_RX_DR;
tx_irq_cnt_rx_dr++;
rx_flag = 1;
}
if(rf_get_irq_status(FLD_RF_IRQ_PKT_MATCH))
{
math_cnt++;
rf_clr_irq_status(FLD_RF_IRQ_PKT_MATCH);
}
if(rf_get_irq_status(FLD_RF_IRQ_PKT_UNMATCH))
{
unmath_cnt++;
rf_clr_irq_status(FLD_RF_IRQ_PKT_UNMATCH);
}
else
{
rf_clr_irq_status(FLD_RF_IRQ_ALL);
}
//3. main_loop handles sending
//Non-empty packet received or maximum retransmission count reached
if (ds_flag || maxretry_flag)
{
if(ds_flag){
#if UI_LED_ENABLE
gpio_toggle(GPIO_LED_GREEN);
#endif
}
ds_flag = 0;
maxretry_flag = 0;
delay_ms(50);
tx_data[4]++;
//Write the data to be sent
tmp = TPLL_WriteTxPayload(PTX_PIPE, ptx_buffer, (unsigned char *)tx_data, TX_PAYLOAD_LEN);
if(!tmp)
{
//Start ptx after successful write
TPLL_PTXTrig();
}
}
- tpll_prx:
//1. Initialization configuration
//Set the packet format
TPLL_SetFormatMode(TPLL_MODE_GENERIC_FORMAT);
//Set the data transfer rate
TPLL_SetBitrate(TPLL_BITRATE_2MBPS);
//Set the signal strength
TPLL_SetOutputPower(RF_POWER_N0p07dBm);
//Set the synchronization word length
TPLL_SetAddressWidth(ADDRESS_WIDTH_5BYTES);
//Set packet filtering conditions
#if(PRI_FLT_MODE_EN)
TPLL_PktFilter(TPLL_PktFlt);
#endif
//Close all channels
TPLL_ClosePipe(TPLL_PIPE_ALL);
//Set the transmission channel and synchronization word
TPLL_SetAddress(PRX_PIPE,rx_address);
//Open the channel
TPLL_OpenPipe(TPLL_PIPE0);
//Set dma tx/rx buffer
TPLL_DmaInit(rx_buf,ptx_buffer);
//Set the prx mode
TPLL_ModeSet(TPLL_MODE_PRX);
//Set the communication frequency
TPLL_SetRFChannel(4);
//Set the tx settle time
TPLL_TxSettleSet(149);
//Set the rx settle time
TPLL_RxSettleSet(85);
//Set the preamble length
TPLL_Preamble_Set(8);
//Set crc
TPLL_CrcSet(TPLL_CrcConfig);
//Enable interrupt
core_interrupt_enable()
//Enable rf interrupt
plic_interrupt_enable(IRQ_ZB_RT);
//Clear all interrupt flags
rf_clr_irq_mask(FLD_RF_IRQ_ALL);
//Clear all interrupt flags
rf_irq_clr_src(FLD_RF_IRQ_ALL);
//Enable specific rf communication interrupt
rf_set_irq_mask(FLD_RF_IRQ_TX | FLD_RF_IRQ_TX_DS | FLD_RF_IRQ_RX_DR | FLD_RF_IRQ_RX | FLD_RF_IRQ_PKT_UNMATCH | FLD_RF_IRQ_PKT_MATCH);
//Write ack payload
TPLL_WriteAckPayload(PRX_PIPE, ptx_buffer, ack_payload, TX_PAYLOAD_LEN);
//Start prx
TPLL_PRXTrig();
rx_timestamp = reg_bb_timer_tick;
//2. Interrupt handling
if (rf_get_irq_status(FLD_RF_IRQ_RX_DR))
{
//rx_dr_irq:Data Ready RX FIFO interrupt. Asserted when new data arrives RX FIFO.
//The RX_DR IRQ is asserted by a new packet arrival even
reg_rf_irq_status = FLD_RF_IRQ_RX_DR;
rx_irq_cnt_rx_dr++;
}
if (rf_get_irq_status(FLD_RF_IRQ_INVALID_PID))
{
//rx_invld_pid_irq:received PID invalid interrupt.
//Asserted when received PID !== expect_pid
reg_rf_irq_status = FLD_RF_IRQ_INVALID_PID;
rx_irq_cnt_invalid_pid++;
}
if (rf_get_irq_status(FLD_RF_IRQ_TX))
{
reg_rf_irq_status = FLD_RF_IRQ_TX;
rx_irq_cnt_tx++;
}
if (rf_get_irq_status(FLD_RF_IRQ_RX))
{
reg_rf_irq_status = FLD_RF_IRQ_RX;
rx_irq_cnt_rx++;
rx_flag = 1;
}
if (rf_get_irq_status(FLD_RF_IRQ_TX_DS))
{
//Data Sent TX FIFO interrupt. Asserted when packet transmitted on TX.
//If AUTO_ACK is activated, this bit is set high only when ACK is received.
reg_rf_irq_status = FLD_RF_IRQ_TX_DS;
rx_irq_cnt_tx_ds++;
}
if(rf_get_irq_status(FLD_RF_IRQ_PKT_MATCH))
{
math_cnt++;
rf_clr_irq_status(FLD_RF_IRQ_PKT_MATCH);
}
if(rf_get_irq_status(FLD_RF_IRQ_PKT_UNMATCH))
{
unmath_cnt++;
rf_clr_irq_status(FLD_RF_IRQ_PKT_UNMATCH);
}
else
{
rf_clr_irq_status(FLD_RF_IRQ_ALL);
}
//3. main_loop handles sending
//When the correct packet is received
if(1 == rx_flag)
{
#if UI_LED_ENABLE
gpio_toggle(GPIO_LED_GREEN);
#endif
rx_flag = 0;
//Get the address of the currently received packet
rx_packet = TPLL_GetRxPacket(rx_buf);
//Get the CRC of the received packet
TPLL_GetRxPacketCrc(rx_packet,crc);
//Retrieve the payload address of the received packet, along with payload length, timestamp, RSSI, and other variable values in the ReadRxPayload structure
ReadRxPayload = TPLL_ReadRxPayload((unsigned char *)&rx_payload, rx_buf);
rx_interval_us = (TPLL_GetTimestamp(rx_packet) - rx_timestamp)/SYSTEM_TIMER_TICK_1US;//bb timer 8M
rx_payload_len = ReadRxPayload.rx_payload_len;
rx_timestamp = ReadRxPayload.rx_timestamp;
rx_header_len = ReadRxPayload.header_len;
rx_rssi = ReadRxPayload.rx_rssi;
rx_pipe = ReadRxPayload.rx_pipe;
rx_crc_len = ReadRxPayload.crc_len;
//Wait for the TX FIFO to be empty
while(!TPLL_TxFifoEmpty(0));
ack_payload[4]++;
//Prepare to send the ACK content
TPLL_WriteAckPayload(PRX_PIPE, ptx_buffer, ack_payload, TX_PAYLOAD_LEN);
}
Test phenomena:
The ptx and prx routines were burned on both boards to communicate. Using the BDT tool, you can see the individual interrupt counts of the prx and ptx, as well as the value of the packets received. Normally the interrupt counts for rx_dr\rx_ds\tx should be very close.


fast_tpll_ptx/prx
The usage steps for B87 chips:
- tpll_fast_ptx:
//1. Initialization settings
//Set the data transfer rate
TPLL_Init(TPLL_BITRATE_2MBPS);
//Set the signal strength
TPLL_SetOutputPower(TPLL_RF_POWER_0DBM);
//Set the synchronization word length
TPLL_SetAddressWidth(ADDRESS_WIDTH_5BYTES);
//Close all channels
TPLL_ClosePipe(TPLL_PIPE_ALL);
//Set the transmission channel and synchronization word
TPLL_SetAddress(TPLL_PIPE0, tx_address);
//Open the channel
TPLL_OpenPipe(TPLL_PIPE0);
//Set the tx channel
TPLL_SetTXPipe(TPLL_PIPE0);
//Unnecessary delay
WaitUs(150);
//Clear all interrupt sources
irq_clr_src();
//Clear all rf interrupt sources
rf_irq_clr_src(FLD_RF_IRQ_ALL);
//Enable rf interrupt
irq_enable_type(FLD_IRQ_ZB_RT_EN);
//Clear all rf interrupt flag bits
rf_irq_disable(FLD_RF_IRQ_ALL);
//Enable the corresponding interrupt flag bit
rf_irq_enable(FLD_RF_IRQ_TX|FLD_RF_IRQ_TX_DS|FLD_RF_IRQ_RETRY_HIT|FLD_RF_IRQ_RX_DR|FLD_RF_IRQ_RX|FLD_RF_IRQ_INVALID_PID);
//Enable interrupt
irq_enable();
//Set the PTX Mode
TPLL_ModeSet(TPLL_MODE_PTX);
//Set fast rx settle time to 44us
TPLL_Fast_RxSettleInit(TPLL_RX_SETTLE_TIME_44US);
//Set fast tx settle time to 53us
TPLL_Fast_TxSettleInit(TPLL_TX_SETTLE_TIME_53US);
//Set the number of active retransmissions and retransmission delay time
TPLL_SetAutoRetry(0,150);
//Set the rx timeout time, when the transmission rate is 250kbps, the rx timeout time needs to be set to 1000us for reasonable communication
TPLL_RxTimeoutSet(500);
//Enable to get the calibration hpmc value, this interface needs to be placed before the setup frequency interface
TPLL_EnableHpmc();
//Set the communication frequency
TPLL_SetRFChannel(4);
//Set fast rx settle time to 85us
TPLL_Fast_RxSettleSet(85);
//Set fast tx settle time 118us
TPLL_Fast_TxSettleSet(118);
//Set fast tx wait time to 5us
TPLL_Fast_TxWaitSet(5);
//Set the leading code length
TPLL_Preamble_Set(2);
//If calibration is turned on
#if(FAST_CALIBRATION_ENABLE)
//Send some packets via PTX to get calibration values
TPLL_Fast_SettleCalib();
//Set the calibration value
TPLL_SetHpmcCalVal(hpmc[4]);
#endif
//Set the communication frequency
TPLL_SetRFChannel(chn);
//Set fast rx settle time to 50us
TPLL_Fast_RxSettleSet(50);
//Set fast tx settle time to 50us
TPLL_Fast_TxSettleSet(50);
//Set fast rx wait time to 0us
TPLL_Fast_TxWaitSet(0);
//Set fast rx wait time to 0us
TPLL_Fast_RxWaitSet(0);
//Enable fast rx settle
TPLL_Fast_RxSettleEnable();
//Enable fast tx settle
TPLL_Fast_TxSettleEnable();
//Reset buffer
TPLL_ResetBuf();
//Write sending packet payload
tmp = TPLL_WriteTxPayload(PTX_CHANNEL, (const unsigned char *)tx_data, tx_len);
//Start sending packets
TPLL_PTXTrig();
//2. Refer to tpll_ptx routine for interrupt handling
//3. main_loop processing refer to tpll_ptx routine
- tpll_fast_prx:
//1. Initialization settings
//Set the data transfer rate
TPLL_Init(TPLL_BITRATE_2MBPS);
//Set the signal strength
TPLL_SetOutputPower(TPLL_RF_POWER_0DBM);
//Set the synchronization word length
TPLL_SetAddressWidth(ADDRESS_WIDTH_5BYTES);
//Close all channels
TPLL_ClosePipe(TPLL_PIPE_ALL);
//Set the transmission channel and synchronization word
TPLL_SetAddress(TPLL_PIPE0, tx_address);
//Open the channel
TPLL_OpenPipe(TPLL_PIPE0);
//Unnecessary delay
WaitUs(150);
//Clear all interrupt sources
irq_clr_src();
//Clear all rf interrupt sources
rf_irq_clr_src(FLD_RF_IRQ_ALL);
//Enable rf interrupt
irq_enable_type(FLD_IRQ_ZB_RT_EN);
//Clear all rf interrupt flag bits
rf_irq_disable(FLD_RF_IRQ_ALL);
//Enable the corresponding interrupt flag bit
rf_irq_enable(FLD_RF_IRQ_TX|FLD_RF_IRQ_TX_DS|FLD_RF_IRQ_RETRY_HIT|FLD_RF_IRQ_RX_DR|FLD_RF_IRQ_RX|FLD_RF_IRQ_INVALID_PID);
//Enable interrupt
irq_enable();
//Set the PTX Mode
TPLL_ModeSet(TPLL_MODE_PTX);
//Enable to get the calibration hpmc value, this interface needs to be placed before the setup frequency interface
TPLL_EnableHpmc();
//Set the communication frequency
TPLL_SetRFChannel(4);
//Set the number of active retransmissions and retransmission delay time
TPLL_SetAutoRetry(0,150);
//Set the rx timeout time, when the transmission rate is 250kbps, the rx timeout time needs to be set to 1000us for reasonable communication
TPLL_RxTimeoutSet(500);
//Set fast rx settle time to 44us
TPLL_Fast_RxSettleInit(TPLL_RX_SETTLE_TIME_44US);
//Set fast tx settle time to 53us
TPLL_Fast_TxSettleInit(TPLL_TX_SETTLE_TIME_53US);
//Set fast rx settle time to 85us
TPLL_Fast_RxSettleSet(85);
//Set fast tx settle time 118us
TPLL_Fast_TxSettleSet(118);
//Set fast tx wait time to 5us
TPLL_Fast_TxWaitSet(5);
//Set the leading code length
TPLL_Preamble_Set(2);
//If calibration is turned on
#if(FAST_CALIBRATION_ENABLE)
//Send some packets via PTX to get calibration values
TPLL_Fast_SettleCalib();
//Set the calibration value
TPLL_SetHpmcCalVal(hpmc[4]);
#endif
//Set the PRX Mode
TPLL_ModeSet(TPLL_MODE_PRX);
//Set the communication frequency
TPLL_SetRFChannel(chn);
//Set fast rx settle time to 50us
TPLL_Fast_RxSettleSet(50);
//Set fast tx settle time to 50us
TPLL_Fast_TxSettleSet(50);
//Set fast rx wait time to 0us
TPLL_Fast_TxWaitSet(0);
//Set fast rx wait time to 0us
TPLL_Fast_RxWaitSet(0);
//Enable fast rx settle
TPLL_Fast_RxSettleEnable();
//Enable fast tx settle
TPLL_Fast_TxSettleEnable();
//Reset buffer
TPLL_ResetBuf();
//Start PRX
TPLL_PRXTrig();
Timestamp_value = clock_time();
//2. Refer to the tpll_prx routine for interrupt handling
//3. main_loop processing reference tpll_prx routine
Test phenomena:
The fast ptx vs prx routine is phenomenally the same as the ptx vs prx routine test method.
genfsk_ll (Telink Generic Frequency Shift Keying Link Layer)
The routines associated with the 2.4GHz proprietary protocol genfsk_ll are listed in the table below:
| Routine | Function introduction |
|---|---|
| gen_fsk_rx | Send packets via manual control |
| gen_fsk_tx | Receive packets via manual control |
| gen_fsk_srx | Receive packets via single rx state machine |
| gen_fsk_stx | Send packets through single tx state machine |
| gen_fsk_srx2tx | Receive packets and send answer packets via single rx to tx state machine |
| gen_fsk_stx2rx | Send packets and receive answer packets via single tx to rx state machine |
| gen_fsk_stx_dpl_packet | Sending variable-length packets through a single tx state machine |
gen_fsk_rx/tx
The usage steps for B87 chips:
- gen_fsk_rx:
//1. Initialization configuration
//Set the data transfer rate, note that this interface should be placed before other rf configuration interfaces
gen_fsk_datarate_set(GEN_FSK_DATARATE_2MBPS);
//Set the length of the leading code
gen_fsk_preamble_len_set(4);
//Set the length of the synchronization word
gen_fsk_sync_word_len_set(SYNC_WORD_LEN_4BYTE);
//Set the transceiver channel and synchronization word
gen_fsk_sync_word_set(GEN_FSK_PIPE0, sync_word);
//Open send/receive channel
gen_fsk_pipe_open(GEN_FSK_PIPE0);
//Set the packet length type and packet length
gen_fsk_packet_format_set(GEN_FSK_PACKET_FORMAT_FIXED_PAYLOAD, 8);
//Set the signal strength
gen_fsk_radio_power_set(GEN_FSK_RADIO_POWER_0DBM);
//Set the rx buffer and size
gen_fsk_rx_buffer_set(rx_buf + rx_ptr*RX_BUF_LEN, RX_BUF_LEN);
//Set the rf frequency point, set to 7 then the frequency point is 2403.5
gen_fsk_channel_set(7);
//Set to receive state
gen_fsk_radio_state_set(GEN_FSK_STATE_RX);
//Even without a state machine it needs to be set, according to the timing rx settle time is 89us
gen_fsk_rx_settle_set(89);
//Wait for rx settle time 90us
WaitUs(90);
//2. Interrupt handling reception
if (irq_src & FLD_IRQ_ZB_RT_EN) {
if (rf_irq_src & FLD_RF_IRQ_RX) {
rx_test_cnt++;
//Get the address of the receiving packet
rx_packet = rx_buf + rx_ptr*RX_BUF_LEN;
//set to next rx_buf
//Circular packet address
rx_ptr = (rx_ptr + 1) % RX_BUF_NUM;
//Set the next packet receiving address
gen_fsk_rx_buffer_set((unsigned char *)(rx_buf + rx_ptr*RX_BUF_LEN), RX_BUF_LEN);
//If the crc checksum of the received packet is correct, i.e., a normal packet is received, then rx_flag is set to 1.
if (gen_fsk_is_rx_crc_ok(rx_packet)) {
rx_flag = 1;
}
}
rf_irq_clr_src(FLD_RF_IRQ_ALL);
}
//3. main_loop handles reception
//If the packet is accepted
if (rx_flag) {
rx_flag = 0;
//Get the payload address
rx_payload = gen_fsk_rx_payload_get(rx_packet, &rx_payload_len);
//RSSI (Received Signal Strength Indicator) to get the received signal strength
rssi = (gen_fsk_rx_packet_rssi_get(rx_packet) + 110);
//Get the instantaneous clock when the synchronization word is successfully correlated
rx_timestamp = gen_fsk_rx_timestamp_get(rx_packet);
gpio_toggle(GREEN_LED_PIN);
}
- gen_fsk_tx:
//1. Initialization configuration
//Set the data transfer rate, note that this interface should be placed before other rf configuration interfaces
gen_fsk_datarate_set(GEN_FSK_DATARATE_2MBPS);
//Set the length of the leading code
gen_fsk_preamble_len_set(4);
//Set the length of the synchronization word
gen_fsk_sync_word_len_set(SYNC_WORD_LEN_4BYTE);
//Set the transceiver channel and synchronization word
gen_fsk_sync_word_set(GEN_FSK_PIPE0, sync_word);
//Open send/receive channel
gen_fsk_pipe_open(GEN_FSK_PIPE0);
//Set the tx channel
gen_fsk_tx_pipe_set(GEN_FSK_PIPE0);
//Set the fixed-length packet type and packet length
gen_fsk_packet_format_set(GEN_FSK_PACKET_FORMAT_FIXED_PAYLOAD, 8);
//Set the signal strength
gen_fsk_radio_power_set(GEN_FSK_RADIO_POWER_0DBM);
//Set the rf frequency point, set to 7 then the frequency point is 2403.5
gen_fsk_channel_set(7);
//Set to send status
gen_fsk_radio_state_set(GEN_FSK_STATE_TX);
//Even if there is no state machine, it needs to be set, according to the timing tx settle time is 149us
gen_fsk_tx_settle_set(149);
//Wait for tx settle time 130us
WaitUs(130);
//irq setting
//Enable rf tx interrupt
rf_irq_enable(FLD_RF_IRQ_TX);
//Enable rf interrupt
irq_enable_type(FLD_IRQ_ZB_RT_EN);
//Enable interrupt
irq_enable();
//Write tx_buffer
tx_buffer[0] = sizeof(tx_payload);
tx_buffer[1] = 0x00;
tx_buffer[2] = 0x00;
tx_buffer[3] = 0x00;
memcpy(&tx_buffer[4], tx_payload, sizeof(tx_payload));
//2. Interrupt handling reception
if (irq_src & FLD_IRQ_ZB_RT_EN) {
if (rf_irq_src & FLD_RF_IRQ_TX) {
tx_done_flag = 1;
}
rf_irq_clr_src(FLD_RF_IRQ_ALL);
}
irq_clr_src2(FLD_IRQ_ALL);
//3. main_loop handles reception
tx_done_flag = 0;
//Start sending data
gen_fsk_tx_start(tx_buffer);
cnt ++;
//When sending is complete
while (tx_done_flag == 0);
gpio_toggle(GREEN_LED_PIN);
WaitMs(1000); //delay 500 ms
tx_buffer[4]++;
Test phenomena:
Burn gen_fsk_tx and gen_fsk_rx programs into the two development boards, you can see that the green lights of the two development boards are blinking, and use the BDT tool to check the rx, you will find that the rx_buf has received a packet, and the count of rx interrupts is also increasing.

The usage steps for TL321x chips:
General structure configuration:
//Configure the parameters of the CRC structure
rf_crc_config_t crc_config = {
.init_value = 0xffffffff,
.poly = 0x00001021,
.xor_out = 0,
.byte_order = 1,
.start_cal_pos = 0,
.len = 2,
};
//If you choose the flexible packet format, you need to configure the header content, including h0, h1, length. Note that the size of h0, h1, and length is in bits, and their sum must be a multiple of 8.
gen_fsk_generic_header_t GEN_FSK_GenericHeader = {
.h0_size = 0,
.length_size = 8,
.h1_size = 0,
.h0_val = 0,
.length_val = 8,
.h1_val = 0,
};
//If packet filtering is enabled, you need to set the field range for packet filtering, the data fields to be matched, and the matching threshold.
rf_pkt_flt_t GEN_FSK_PktFlt = {
.rf_pkt_flt_start = 0,//start at h0
.rf_pkt_flt_end = 9,
.rf_pkt_match_threshold = 24,//max 64 bit
.rf_pkt_match_low=0x00000000,
.rf_pkt_match_high=0x00030201,
.rf_pkt_mask_low=0x00000000,
.rf_pkt_mask_high=0x00ffffff,//if bit is 1 means this bit should matched
};
- gen_fsk_rx:
//1. Initialization configuration
//Select the packet format. If it is a fixed-length packet, the second parameter will define the packet length
gen_fsk_packet_format_set(GEN_FSK_MODE_FIXED_FORMAT,8);
//Set the data transmission rate, noting that this interface should be called before other RF configuration interfaces
gen_fsk_datarate_set(GEN_FSK_DATARATE_1MBPS);
//Set the preamble length
gen_fsk_preamble_len_set(4);
//Set the synchronization word length
gen_fsk_sync_word_len_set(SYNC_WORD_LEN_4BYTE);
//Set the transmission channel and synchronization word
gen_fsk_sync_word_set(GEN_FSK_PIPE0, sync_word);
//Open the channel
gen_fsk_pipe_open(GEN_FSK_PIPE0);
//Set crc
gen_fsk_set_crc_config(&crc_config);
//Set the signal strength
gen_fsk_radio_power_set(RF_POWER_INDEX_N0p07dBm);
//Set RF frequency
gen_fsk_channel_set(8); //set rf freq as 2408MHz,1M step
//Set to receive state
gen_fsk_radio_state_set(GEN_FSK_STATE_RX);
//Set the rx buffer and size
gen_fsk_rx_buffer_set(rx_buf, RX_BUF_NUM, RX_BUF_LEN);
//Set packet filtering conditions
#if(PRI_FLT_MODE_EN)
gen_fsk_set_pkt_filter(GEN_FSK_PktFlt);
#endif
//Even without a state machine it needs to be set, according to the timing rx settle time is 89us
gen_fsk_rx_settle_set(89);
//Wait for rx settle time 90us
WaitUs(90);
//2. Interrupt handling reception
if (rf_get_irq_status(FLD_RF_IRQ_RX)){
irq_cnt_rx++;
if(!rf_get_crc_err()){
#if UI_LED_ENABLE
gpio_toggle(GPIO_LED_BLUE);
#endif
irq_cnt_rx_crc_ok++;
}
rx_flag = 1;
rf_clr_irq_status(FLD_RF_IRQ_RX );
}else if(rf_get_irq_status(FLD_RF_IRQ_PKT_UNMATCH )){
irq_cnt_unmatch++;
rf_clr_irq_status(FLD_RF_IRQ_PKT_UNMATCH);
}else if(rf_get_irq_status(FLD_RF_IRQ_PKT_MATCH )){
irq_cnt_match++;
rf_clr_irq_status(FLD_RF_IRQ_PKT_MATCH);
}else{
rf_clr_irq_status(FLD_RF_IRQ_ALL);
}
//3. main_loop handles reception
//If the packet is accepted
if (rx_flag) {
rx_flag = 0;
//Get the payload address
rx_payload = gen_fsk_rx_payload_get(rx_buf, &rx_payload_len);
//RSSI (Received Signal Strength Indicator) to get the received signal strength
rssi = (gen_fsk_rx_packet_rssi_get(rx_buf) + 110);
//Get the instantaneous clock when the synchronization word is successfully correlated
rx_timestamp = gen_fsk_rx_timestamp_get(rx_buf);
gpio_toggle(GREEN_LED_PIN);
}
- gen_fsk_tx:
//1. Initialization configuration
//Set the packet format
gen_fsk_packet_format_set(GEN_FSK_MODE_FIXED_FORMAT,TX_PAYLOAD_LEN);
//Set the data transmission rate, noting that this interface should be called before other RF configuration interfaces
gen_fsk_datarate_set(GEN_FSK_DATARATE_1MBPS);
//Set the preamble length
gen_fsk_preamble_len_set(4);
//Set the synchronization word length
gen_fsk_sync_word_len_set(SYNC_WORD_LEN_4BYTE);
//Set the transmission channel and synchronization word
gen_fsk_sync_word_set(GEN_FSK_PIPE0, sync_word);
//Open the channel
gen_fsk_pipe_open(GEN_FSK_PIPE0);
//Open Tx channel
gen_fsk_tx_pipe_set(GEN_FSK_PIPE0);
//Set crc
gen_fsk_set_crc_config(&crc_config);
//Set the signal strength
gen_fsk_radio_power_set(GEN_FSK_RADIO_POWER_0DBM);
//Set RF frequency
gen_fsk_channel_set(8); //set rf freq as 2408MHz,1M step
//Set to send status
gen_fsk_radio_state_set(GEN_FSK_STATE_TX);
//Set tx fifo
gen_fsk_tx_buffer_set(TX_DMA_NUM,TX_DMA_LEN);
//Even if there is no state machine, it needs to be set, according to the timing tx settle time is 149us
gen_fsk_tx_settle_set(149);
//Wait for tx settle time 130us
WaitUs(130);
//Set interrupt priority
plic_set_priority(IRQ_ZB_RT, 3);
//Enable RF interrupt
plic_interrupt_enable(IRQ_ZB_RT);
//Set interrupt flags
rf_set_irq_mask(FLD_RF_IRQ_TX);
//Clear rf interrupt
rf_clr_irq_status(FLD_RF_IRQ_ALL);
//Enable interrupt
core_interrupt_enable();
//2. Interrupt handling reception
if(rf_get_irq_status(FLD_RF_IRQ_TX)){
tx_done_flag = 1;
irq_cnt_tx++;
rf_clr_irq_status(FLD_RF_IRQ_TX );
}
else{
rf_clr_irq_status(FLD_RF_IRQ_ALL);
}
//3. main_loop handles reception
tx_done_flag = 0;
//Wait for sending buffer
gen_fsk_write_payload(tx_buffer,tx_payload,TX_PAYLOAD_LEN);
//Start sending data
gen_fsk_tx_start(tx_buffer); //start the Radio transmission
//Wait for sending complete
while (tx_done_flag == 0);
#if UI_LED_ENABLE
gpio_toggle(GPIO_LED_WHITE);
#endif
delay_ms(1000); //delay 100 ms
tx_payload[4]++;
Test phenomena:
Burn the gen_fsk_tx and gen_fsk_rx programs into two development boards separately. You will observe the TX side flashing a white light, while the RX side flashes green and blue lights. Using the BDT tool to check the RX, you will see that the rx_buf has received data packets, and the RX interrupt count is increasing.

gen_fsk_srx/stx
The usage steps for B87 chips:
- gen_fsk_srx:
//1. Initialization configuration
//Set the data transfer rate, note that this interface should be placed before other rf configuration interfaces
gen_fsk_datarate_set(GEN_FSK_DATARATE_2MBPS);
//Set the length of the leading code
gen_fsk_preamble_len_set(4);
//Set the length of the synchronization word
gen_fsk_sync_word_len_set(SYNC_WORD_LEN_4BYTE);
//Set the transceiver channel and synchronization word
gen_fsk_sync_word_set(GEN_FSK_PIPE0, sync_word);
//Open send/receive channel
gen_fsk_pipe_open(GEN_FSK_PIPE0);
//Set the packet length type and packet length
gen_fsk_packet_format_set(GEN_FSK_PACKET_FORMAT_FIXED_PAYLOAD, 8);
//Set the signal strength
gen_fsk_radio_power_set(GEN_FSK_RADIO_POWER_0DBM);
//Set the rx buffer and size
gen_fsk_rx_buffer_set(rx_buf + rx_ptr*RX_BUF_LEN, RX_BUF_LEN);
//Set the rf frequency point, set to 7 then the frequency point is 2403.5
gen_fsk_channel_set(7);
//Set the state machine to automatic reception
gen_fsk_radio_state_set(GEN_FSK_STATE_AUTO);
//If you turn on quick setup rx settle mode
#if(RF_FAST_RX_SETTING_ENABLE)
//Initialize fast rx settle in 44us
gen_fsk_fast_rxsettleInit(GEN_RX_SETTLE_TIME_44US);
//Set fast rx settle time to 44us
gen_fsk_rx_settle_set(44);
//Enable quick setup rx settle
gen_fsk_fast_rxsettleEnable();
#else
//Set rx settle time to 89us
gen_fsk_rx_settle_set(89);
#endif
//irq configuration
//Disable all interruptions
rf_irq_disable(FLD_RF_IRQ_ALL);
//Enable rf-related interrupts
rf_irq_enable(FLD_RF_IRQ_RX | FLD_RF_IRQ_FIRST_TIMEOUT);
//Enable rf interrupt
irq_enable_type(FLD_IRQ_ZB_RT_EN);
//Enable interrupt
irq_enable();
//start the SRX
//Start receiving packets, since the second parameter is written to 0 to indicate that the rx first time out interrupt is turned off, it will continue to receive until it receives a packet
gen_fsk_srx_start(clock_time()+50*16, 0);
//2. Interrupt handling reception
if (irq_src & FLD_IRQ_ZB_RT_EN) {
if (rf_irq_src & FLD_RF_IRQ_RX) {
rf_irq_clr_src(FLD_RF_IRQ_RX);
rx_test_cnt++;
rx_packet = rx_buf + rx_ptr*RX_BUF_LEN;
//set to next rx_buf
//Circular packet address
rx_ptr = (rx_ptr + 1) % RX_BUF_NUM;
//Setting the next packet receiving address
gen_fsk_rx_buffer_set((unsigned char *)(rx_buf + rx_ptr*RX_BUF_LEN), RX_BUF_LEN);
//If the crc checksum of the receiving packet is correct
if (gen_fsk_is_rx_crc_ok((unsigned char *)rx_packet)) {
}
rx_flag = 1;
}
if (rf_irq_src & FLD_RF_IRQ_FIRST_TIMEOUT) {
rf_irq_clr_src(FLD_RF_IRQ_FIRST_TIMEOUT);
rx_first_timeout = 1;
}
}
irq_clr_src2(FLD_IRQ_ALL);
//3. main_loop handles reception
//When the correct packet is received
if (rx_flag) {
rx_flag = 0;
//Get the payload address
rx_payload = gen_fsk_rx_payload_get((unsigned char *)rx_packet, (unsigned char *)&rx_payload_len);
//RSSI (Received Signal Strength Indicator) to get the received signal strength
rssi = (gen_fsk_rx_packet_rssi_get((unsigned char *)rx_packet) + 110);
//Get the instantaneous clock when the synchronization word is successfully correlated
rx_timestamp = gen_fsk_rx_timestamp_get((unsigned char *)rx_packet);
gpio_toggle(GREEN_LED_PIN);
//Next reception is turned on after 50us, time out is 350us
gen_fsk_srx_start(clock_time()+50*16, 350);
gpio_write(TIME_DEBUG_PIN,1);
gpio_write(TIME_DEBUG_PIN,0);
}
//When the first time out interrupt occurs
if (rx_first_timeout) {
rx_first_timeout = 0;
//Continue to open new receipts
gen_fsk_srx_start(clock_time()+50*16, 350);
}
- gen_fsk_stx:
//1. Initialization configuration
//Set the data transfer rate, note that this interface should be placed before other rf configuration interfaces
gen_fsk_datarate_set(GEN_FSK_DATARATE_2MBPS);
//Set the length of the leading code
gen_fsk_preamble_len_set(4);
//Set the length of the synchronization word
gen_fsk_sync_word_len_set(SYNC_WORD_LEN_4BYTE);
//Set the transceiver channel and synchronization word
gen_fsk_sync_word_set(GEN_FSK_PIPE0, sync_word);
//Open send/receive channel
gen_fsk_pipe_open(GEN_FSK_PIPE0);
//Set the tx channel
gen_fsk_tx_pipe_set(GEN_FSK_PIPE0);
//Set the fixed-length packet type and packet length
gen_fsk_packet_format_set(GEN_FSK_PACKET_FORMAT_FIXED_PAYLOAD, 8);
//Set the signal strength
gen_fsk_radio_power_set(GEN_FSK_RADIO_POWER_0DBM);
//Set the rf frequency point, set to 7 then the frequency point is 2403.5
gen_fsk_channel_set(7);
//Set the state machine to the active send state
gen_fsk_radio_state_set(GEN_FSK_STATE_AUTO);
//If you turn on quick setup tx settle mode
#if(RF_FAST_TX_SETTING_ENABLE)
//Initialize fast tx settle in 53us
gen_fsk_fast_txsettleInit(GEN_TX_SETTLE_TIME_53US);
//Set the fast tx settle time to 53us
gen_fsk_tx_settle_set(53);
//Enable quick setup of tx settle
gen_fsk_fast_txsettleEnable();
#else
//Set tx settle time to 149us
gen_fsk_tx_settle_set(149);
#endif
//irq setting
//Enable rf tx interrupt
rf_irq_enable(FLD_RF_IRQ_TX);
//Enable rf interrupt
irq_enable_type(FLD_IRQ_ZB_RT_EN);
//Enable interrupt
irq_enable();
//Write tx_buffer
tx_buffer[0] = sizeof(tx_payload);
tx_buffer[1] = 0x00;
tx_buffer[2] = 0x00;
tx_buffer[3] = 0x00;
memcpy(&tx_buffer[4], tx_payload, sizeof(tx_payload));
//2. Interrupt handling reception
if (irq_src & FLD_IRQ_ZB_RT_EN) {
if (rf_irq_src & FLD_RF_IRQ_TX) {
tx_done_flag = 1;
}
rf_irq_clr_src(FLD_RF_IRQ_ALL);
}
irq_clr_src2(FLD_IRQ_ALL);
//3. main_loop handles reception
tx_done_flag = 0;
//Send packet after 100us
gen_fsk_stx_start(tx_buffer, clock_time()+100*16);
gpio_write(TIME_DEBUG_PIN,1);
gpio_write(TIME_DEBUG_PIN,0);
//Send complete
while (tx_done_flag == 0);
cnt++;
gpio_write(GREEN_LED_PIN, 1); //LED On
WaitMs(100);
gpio_write(GREEN_LED_PIN, 0); //LED Off
tx_buffer[4]++;
Test Phenomena:
Burn gen_fsk_stx and gen_fsk_srx programs into two development boards respectively, you can see that the stx is lit green and the srx is blinking green, use BDT tool to check the srx, you will find that the rx_buf has received a packet, and the count of the rx interrupts is also increasing.

The usage steps for TL321x chips:
- gen_fsk_srx:
//1. Initialization configuration
//Set the packet format
gen_fsk_packet_format_set(GEN_FSK_MODE_FIXED_FORMAT,8);
//Set the data transfer rate, note that this interface should be placed before other rf configuration interfaces
gen_fsk_datarate_set(GEN_FSK_DATARATE_1MBPS);
//Set the preamble length
gen_fsk_preamble_len_set(4);
//Set the synchronization word length
gen_fsk_sync_word_len_set(SYNC_WORD_LEN_4BYTE);
//Set the transceiver channel and synchronization word
gen_fsk_sync_word_set(GEN_FSK_PIPE0, sync_word);
//Open send/receive channel
gen_fsk_pipe_open(GEN_FSK_PIPE0);
//Set tx pipe
gen_fsk_tx_pipe_set(GEN_FSK_PIPE0);
//Set crc
gen_fsk_set_crc_config(&crc_config);
//Set the signal strength
gen_fsk_radio_power_set(RF_POWER_INDEX_N0p07dBm);
//Set the rx buffer and size
gen_fsk_rx_buffer_set(rx_buf, RX_BUF_NUM, RX_BUF_LEN);
//set rf freq, 1M step
gen_fsk_channel_set(17);
//Set the state machine to automatic reception
gen_fsk_radio_state_set(GEN_FSK_STATE_AUTO);
//Set packet filtering conditions
#if(PRI_FLT_MODE_EN)
gen_fsk_set_pkt_filter(GEN_FSK_PktFlt);
#endif
//Set rx settle time
gen_fsk_rx_settle_set(89);
//Wait for rx settle
delay_us(90);
//Set interrupt priority
plic_set_priority(IRQ_ZB_RT, 3);
//Enable rf interrupt
plic_interrupt_enable(IRQ_ZB_RT);
//Set interrupt flags
rf_set_irq_mask(FLD_RF_IRQ_RX | FLD_RF_IRQ_FIRST_TIMEOUT | FLD_RF_IRQ_PKT_MATCH | FLD_RF_IRQ_PKT_UNMATCH);
//Clear rf interrupt
rf_clr_irq_status(FLD_RF_IRQ_ALL);
//Enable interrupt
core_interrupt_enable();
//start the SRX
//Start receiving packets, since the second parameter is written to 0 to indicate that the rx first time out interrupt is turned off, it will continue to receive until it receives a packet
gen_fsk_srx_start(rf_stimer_get_tick()+50*RF_SYSTEM_TIMER_TICK_1US, 0);
//2. Interrupt handling reception
if (rf_get_irq_status(FLD_RF_IRQ_RX)){
//Get the rx packet address
rx_packet = rf_get_rx_packet_addr(RX_BUF_NUM,RX_BUF_LEN,rx_buf);
irq_cnt_rx++;
//If the CRC is correct, increment the corresponding cnt value by 1
if (gen_fsk_is_rx_crc_ok(rx_packet)) {
irq_cnt_rx_crc_ok++;
#if UI_LED_ENABLE
gpio_toggle(GPIO_LED_BLUE);
#endif
}
rx_flag = 1;
rf_clr_irq_status(FLD_RF_IRQ_RX );
}
else if (rf_get_irq_status(FLD_RF_IRQ_FIRST_TIMEOUT)){
rx_first_timeout_flag = 1;
irq_cnt_rx_first_timeout++;
rf_clr_irq_status(FLD_RF_IRQ_FIRST_TIMEOUT );
}else if(rf_get_irq_status(FLD_RF_IRQ_PKT_UNMATCH )){
irq_cnt_unmatch++;
rf_clr_irq_status(FLD_RF_IRQ_PKT_UNMATCH);
}
else if(rf_get_irq_status(FLD_RF_IRQ_PKT_MATCH )){
irq_cnt_match++;
rf_clr_irq_status(FLD_RF_IRQ_PKT_MATCH);
}
else{
rf_clr_irq_status(FLD_RF_IRQ_ALL);
}
//3. main_loop handles reception
//When the correct packet is received
if (rx_flag) {
rx_flag = 0;
//Get the payload address and length
rx_payload = gen_fsk_rx_payload_get(rx_packet, &rx_payload_len);
//Get the rx packet rssi value
rssi = (gen_fsk_rx_packet_rssi_get(rx_packet) + 110);
//Get the rx timestamp value
rx_timestamp = gen_fsk_rx_timestamp_get(rx_packet);
#if UI_LED_ENABLE
gpio_toggle(GPIO_LED_GREEN);
#endif
//Start receiving packets
gen_fsk_srx_start(rf_stimer_get_tick()+50*RF_SYSTEM_TIMER_TICK_1US, 0);
}
if (rx_first_timeout_flag) {
rx_first_timeout_flag = 0;
//Start receiving packets
gen_fsk_srx_start(rf_stimer_get_tick()+50*RF_SYSTEM_TIMER_TICK_1US, 0);
}
- gen_fsk_stx:
//1. Initialization configuration
//Set the packet format
gen_fsk_packet_format_set(GEN_FSK_MODE_FIXED_FORMAT,TX_PAYLOAD_LEN);
//Set the data transfer rate, note that this interface should be placed before other rf configuration interfaces
gen_fsk_datarate_set(GEN_FSK_DATARATE_1MBPS);
//Set the preamble length
gen_fsk_preamble_len_set(4);
//Set the synchronization word length
gen_fsk_sync_word_len_set(SYNC_WORD_LEN_4BYTE);
//Set the transceiver channel and synchronization word
gen_fsk_sync_word_set(GEN_FSK_PIPE0, sync_word);
//Open send/receive channel
gen_fsk_pipe_open(GEN_FSK_PIPE0);
//Set tx pipe
gen_fsk_tx_pipe_set(GEN_FSK_PIPE0);
//Set crc
gen_fsk_set_crc_config(&crc_config);
//Set the signal strength
gen_fsk_radio_power_set(RF_POWER_INDEX_N0p07dBms);
//Set the tx buffer and size
gen_fsk_tx_buffer_set(TX_BUF_NUM,TX_BUF_LEN);
//set rf freq, 1M step
gen_fsk_channel_set(17);
//Set the state machine to automatic reception
gen_fsk_radio_state_set(GEN_FSK_STATE_AUTO);
//Set tx settle time
gen_fsk_tx_settle_set(149);
//Set interrupt priority
plic_set_priority(IRQ_ZB_RT, 3);
//Enable rf interrupt
plic_interrupt_enable(IRQ_ZB_RT);
//Set interrupt flags
rf_set_irq_mask(FLD_RF_IRQ_TX);
//Clear rf interrupt
rf_clr_irq_status(FLD_RF_IRQ_ALL);
//Enable interrupt
core_interrupt_enable();
//2. Interrupt handling reception
if (rf_get_irq_status(FLD_RF_IRQ_TX)){
tx_done_flag = 1;
irq_cnt_tx++;
rf_clr_irq_status(FLD_RF_IRQ_TX );
}
else{
rf_clr_irq_status(FLD_RF_IRQ_ALL);
}
//3. main_loop handles reception
tx_done_flag = 0;
//Wait for sending paylaod
gen_fsk_write_payload(tx_buffer,tx_payload,TX_PAYLOAD_LEN);
//Start tx after 100us
gen_fsk_stx_start(tx_buffer, rf_stimer_get_tick()+100*RF_SYSTEM_TIMER_TICK_1US);
//Wait for tx complete
while (tx_done_flag == 0);
tx_payload[4]++;
#if (TLKAPI_DEBUG_ENABLE)
gpio_toggle(GPIO_LED_WHITE);
#endif
delay_ms(200);
Test Phenomena:
Burn the gen_fsk_stx and gen_fsk_srx programs into two development boards separately. You will observe the TX side flashing a white light, while the RX side flashes green and blue lights. Using the BDT tool to check the SRX, you will see that the rx_buf has received data packets, and the RX interrupt count is increasing.

gen_fsk_srx2tx/stx2rx
The usage steps for B87 chips:
- gen_fsk_srx2tx:
//1. Initialization configuration
//Set the data transfer rate, note that this interface should be placed before other rf configuration interfaces
gen_fsk_datarate_set(GEN_FSK_DATARATE_2MBPS);
//Set the length of the leading code
gen_fsk_preamble_len_set(4);
//Set the length of the synchronization word
gen_fsk_sync_word_len_set(SYNC_WORD_LEN_4BYTE);
//Set the transceiver channel and synchronization word
gen_fsk_sync_word_set(GEN_FSK_PIPE0, sync_word);
//Open send/receive channel
gen_fsk_pipe_open(GEN_FSK_PIPE0);
//Set the tx channel
gen_fsk_tx_pipe_set(GEN_FSK_PIPE0);
//Set the packet length type and packet length
gen_fsk_packet_format_set(GEN_FSK_PACKET_FORMAT_FIXED_PAYLOAD, 8);
//Set the signal strength
gen_fsk_radio_power_set(GEN_FSK_RADIO_POWER_0DBM);
//Set the rx buffer and size
gen_fsk_rx_buffer_set(rx_buf + rx_ptr*RX_BUF_LEN, RX_BUF_LEN);
//Set the rf frequency point, set to 7 then the frequency point is 2403.5
gen_fsk_channel_set(7);
//Set the state machine to auto send/receive
gen_fsk_radio_state_set(GEN_FSK_STATE_AUTO);
//Set rx settle time to 89us
gen_fsk_rx_settle_set(89);
//Write tx_buffer
tx_buffer[0] = sizeof(tx_payload);
tx_buffer[1] = 0x00;
tx_buffer[2] = 0x00;
tx_buffer[3] = 0x00;
memcpy(&tx_buffer[4], tx_payload, sizeof(tx_payload));
//irq configuration
//Disable all interruptions
rf_irq_disable(FLD_RF_IRQ_ALL);
//Enable rf-related interrupts
rf_irq_enable(FLD_RF_IRQ_RX | FLD_RF_IRQ_TX);
//Enable rf interrupt
irq_enable_type(FLD_IRQ_ZB_RT_EN);
//Enable interrupt
irq_enable();
//start the SRX
//Start receiving packets at 50us, and send packets automatically after receiving them. Since the third parameter is written 0 to indicate that the rx first time out interrupt is turned off, it will keep receiving until it receives a packet.
gen_fsk_srx2tx_start(tx_buffer, clock_time()+50*16, 0);
//2. Interrupt handling reception
if (irq_src & FLD_IRQ_ZB_RT_EN) {
if (rf_irq_src & FLD_RF_IRQ_RX) {
rf_irq_clr_src(FLD_RF_IRQ_RX);
rx_test_cnt++;
//Get the address of the receiving packet
rx_packet = rx_buf + rx_ptr*RX_BUF_LEN;
//set to next rx_buf
//Circular packet address
rx_ptr = (rx_ptr + 1) % RX_BUF_NUM;
//Set the next packet receiving address
gen_fsk_rx_buffer_set((unsigned char *)(rx_buf + rx_ptr*RX_BUF_LEN), RX_BUF_LEN);
//If the crc checksum of the received packet is correct, i.e., a normal packet is received, then rx_flag is set to 1.
if (gen_fsk_is_rx_crc_ok((unsigned char *)rx_packet)) {
rx_flag = 1;
gpio_toggle(TIME_DEBUG_PIN);
}
}
if (rf_irq_src & FLD_RF_IRQ_TX) {
rf_irq_clr_src(FLD_RF_IRQ_TX);
tx_done_flag = 1;
gpio_toggle(TIME_DEBUG_PIN);
}
}
irq_clr_src2(FLD_IRQ_ALL);
//3. main_loop handles reception
//When the correct packet is received
if (rx_flag) {
rx_flag = 0;
//Get the payload address
rx_payload = gen_fsk_rx_payload_get((unsigned char *)rx_packet, (unsigned char *)&rx_payload_len);
//RSSI (Received Signal Strength Indicator) to get the received signal strength
rssi = (gen_fsk_rx_packet_rssi_get((unsigned char *)rx_packet) + 110);
//Get the instantaneous clock when the synchronization word is successfully correlated
rx_timestamp = gen_fsk_rx_timestamp_get((unsigned char *)rx_packet);
gpio_toggle(GREEN_LED_PIN);
}
//When sending is complete
if (tx_done_flag) {
tx_done_flag = 0;
//start the SRX2TX
//Start receiving packets at 50us, and send packets automatically after receiving them. Since the third parameter is written 0 to indicate that the rx first time out interrupt is turned off, it will keep receiving until it receives a packet.
gen_fsk_srx2tx_start(tx_buffer, clock_time()+50*16, 0);
}
- gen_fsk_stx2rx:
//1. Initialization configuration
//Set the data transfer rate, note that this interface should be placed before other rf configuration interfaces
gen_fsk_datarate_set(GEN_FSK_DATARATE_2MBPS);
//Set the length of the leading code
gen_fsk_preamble_len_set(4);
//Set the length of the synchronization word
gen_fsk_sync_word_len_set(SYNC_WORD_LEN_4BYTE);
//Set the transceiver channel and synchronization word
gen_fsk_sync_word_set(GEN_FSK_PIPE0, sync_word);
//Open send/receive channel
gen_fsk_pipe_open(GEN_FSK_PIPE0);
//Set the tx channel
gen_fsk_tx_pipe_set(GEN_FSK_PIPE0);
//Set the packet length type and packet length
gen_fsk_packet_format_set(GEN_FSK_PACKET_FORMAT_FIXED_PAYLOAD, 8);
//Set the signal strength
gen_fsk_radio_power_set(GEN_FSK_RADIO_POWER_0DBM);
//Set the rx buffer and size
gen_fsk_rx_buffer_set(rx_buf + rx_ptr*RX_BUF_LEN, RX_BUF_LEN);
//Set the rf frequency point, set to 7 then the frequency point is 2403.5
gen_fsk_channel_set(7);
//Set the state machine to auto send/receive
gen_fsk_radio_state_set(GEN_FSK_STATE_AUTO);
//Set tx settle time to 149us
gen_fsk_tx_settle_set(149);
//irq configuration
//Disable all interruptions
rf_irq_disable(FLD_RF_IRQ_ALL);
//Enable rf-related interrupts
rf_irq_enable(FLD_RF_IRQ_RX | FLD_RF_IRQ_TX | FLD_RF_IRQ_RX_TIMEOUT);
//Enable rf interrupt
irq_enable_type(FLD_IRQ_ZB_RT_EN);
//Enable interrupt
irq_enable();
//Write tx_buffer
tx_buffer[0] = sizeof(tx_payload);
tx_buffer[1] = 0x00;
tx_buffer[2] = 0x00;
tx_buffer[3] = 0x00;
memcpy(&tx_buffer[4], tx_payload, sizeof(tx_payload));
//start the STX2SRX
//Start sending packets after 50us, receive packets automatically after sending packets, time out time is 250us
gen_fsk_stx2rx_start(tx_buffer, clock_time()+50*16, 250);
//2. Interrupt handling reception
if (irq_src & FLD_IRQ_ZB_RT_EN) {
if (rf_irq_src & FLD_RF_IRQ_RX) {
rf_irq_clr_src(FLD_RF_IRQ_RX);
rx_test_cnt++;
//Get the address of the receiving packet
rx_packet = rx_buf + rx_ptr*RX_BUF_LEN;
//set to next rx_buf
//Circular packet address
rx_ptr = (rx_ptr + 1) % RX_BUF_NUM;
//Set the next packet receiving address
gen_fsk_rx_buffer_set((unsigned char *)(rx_buf + rx_ptr*RX_BUF_LEN), RX_BUF_LEN);
//If the crc checksum of the receiving packet is correct
if (gen_fsk_is_rx_crc_ok((unsigned char *)rx_packet)) {
gpio_toggle(TIME_DEBUG_PIN);
}
rx_flag = 1;
}
if (rf_irq_src & FLD_RF_IRQ_RX_TIMEOUT) {
rf_irq_clr_src(FLD_RF_IRQ_RX_TIMEOUT);
rx_timeout_flag = 1;
gpio_toggle(TIME_DEBUG_PIN);
}
if (rf_irq_src & FLD_RF_IRQ_TX) {
rf_irq_clr_src(FLD_RF_IRQ_TX);
tx_done_flag = 1;
gpio_toggle(TIME_DEBUG_PIN);
}
}
irq_clr_src2(FLD_IRQ_ALL);
//3. main_loop handles reception
//When the rx time out interrupt occurs
if (rx_timeout_flag) {
rx_timeout_flag = 0;
WaitMs(100);
//Start sending packets at 50us, receive packets automatically after sending packets, time out time is 250us
gen_fsk_stx2rx_start(tx_buffer, clock_time()+50*16, 250);
}
//When the correct packet is received
if (rx_flag) {
rx_flag = 0;
//Get the payload address
rx_payload = gen_fsk_rx_payload_get((unsigned char *)rx_packet, (unsigned char *)&rx_payload_len);
//RSSI (Received Signal Strength Indicator) to get the received signal strength
rssi = (gen_fsk_rx_packet_rssi_get((unsigned char *)rx_packet) + 110);
//Get the instantaneous clock when the synchronization word is successfully correlated
rx_timestamp = gen_fsk_rx_timestamp_get((unsigned char *)rx_packet);
gpio_toggle(GREEN_LED_PIN);
WaitMs(100);
//Start sending packets at 50us, receive packets automatically after sending packets, time out time is 250us
gen_fsk_stx2rx_start(tx_buffer, clock_time()+50*16, 250);
}
Test Phenomena:
Burn gen_fsk_stx2rx and gen_fsk_srx2tx programs into the two development boards respectively, you will see the green light blinking on both boards, and use the BDT tool to check the stx2rx and srx2tx, and you will find that the rx_buf has received packets and the rx interrupt count is increasing.


The usage steps for TL321x chips:
- gen_fsk_srx2tx:
//1. Initialization configuration
//Set the packet format
gen_fsk_packet_format_set(GEN_FSK_MODE_LEGACY_VARIABLE_FORMAT,TX_PAYLOAD_LEN);
//Set the data transfer rate, note that this interface should be placed before other rf configuration interfaces
gen_fsk_datarate_set(GEN_FSK_DATARATE_2MBPS);
//Set the preamble length
gen_fsk_preamble_len_set(4);
//Set the synchronization word length
gen_fsk_sync_word_len_set(SYNC_WORD_LEN_4BYTE);
//Set the transceiver channel and synchronization word
gen_fsk_sync_word_set(GEN_FSK_PIPE0, sync_word);
//Open send/receive channel
gen_fsk_pipe_open(GEN_FSK_PIPE0);
//Set the tx channel
gen_fsk_tx_pipe_set(GEN_FSK_PIPE0);
//Set crc
gen_fsk_set_crc_config(&crc_config);
//Set the signal strength
gen_fsk_radio_power_set(RF_POWER_INDEX_N0p07dBm);
//Set the rx/tx buffer and size
gen_fsk_rx_buffer_set(rx_buf, RX_BUF_NUM, RX_BUF_LEN);
gen_fsk_tx_buffer_set(TX_BUF_NUM,TX_BUF_LEN);
//Set the rf frequency point, set to 7 then the frequency point is 2417
gen_fsk_channel_set(17);
//Set the state machine to auto send/receive
gen_fsk_radio_state_set(GEN_FSK_STATE_AUTO);
//Set packet filtering conditions
#if(PRI_FLT_MODE_EN)
gen_fsk_set_pkt_filter(GEN_FSK_PktFlt);
#endif
//Set rx settle time to 89us
gen_fsk_rx_settle_set(89);
//Wait for rx settle
delay_us(90);
//Set interrupt priority
plic_set_priority(IRQ_ZB_RT, 3);
//Enable rf interrupt
plic_interrupt_enable(IRQ_ZB_RT);
//Set interrupt flags
rf_set_irq_mask(FLD_RF_IRQ_RX | FLD_RF_IRQ_TX | FLD_RF_IRQ_FIRST_TIMEOUT | FLD_RF_IRQ_PKT_MATCH | FLD_RF_IRQ_PKT_UNMATCH);
//Clear rf interrupt
rf_clr_irq_status(FLD_RF_IRQ_ALL);
//Enable interrupt
core_interrupt_enable();
//Wait for sending packets
gen_fsk_write_payload(tx_buffer,tx_payload,TX_PAYLOAD_LEN);
//Start receiving packets at 50us, and send packets automatically after receiving them. Since the third parameter is written 0 to indicate that the rx first time out interrupt is turned off, it will keep receiving until it receives a packet.
gen_fsk_srx2tx_start(tx_buffer, rf_stimer_get_tick()+50*RF_SYSTEM_TIMER_TICK_1US, 0);
//2. Interrupt handling reception
if (rf_get_irq_status(FLD_RF_IRQ_RX)){
//Get the address of the receiving packet
rx_packet = rf_get_rx_packet_addr(RX_BUF_NUM,RX_BUF_LEN,rx_buf);
irq_cnt_rx++;
//Determine whether the received packet's CRC is correct
if (gen_fsk_is_rx_crc_ok(rx_packet)) {
irq_cnt_rx_crc_ok++;
#if UI_LED_ENABLE
gpio_toggle(GPIO_LED_BLUE);
#endif
}
rx_flag = 1;
rf_clr_irq_status(FLD_RF_IRQ_RX );
}
else if (rf_get_irq_status(FLD_RF_IRQ_TX)){
tx_done_flag = 1;
irq_cnt_tx++;
#if UI_LED_ENABLE
gpio_toggle(GPIO_LED_WHITE);
#endif
rf_clr_irq_status(FLD_RF_IRQ_TX );
}
else if (rf_get_irq_status(FLD_RF_IRQ_FIRST_TIMEOUT)){
rx_first_timeout_flag = 1;
irq_cnt_first_timeout++;
rf_clr_irq_status(FLD_RF_IRQ_FIRST_TIMEOUT );
}
else if(rf_get_irq_status(FLD_RF_IRQ_PKT_UNMATCH )){
irq_cnt_unmatch++;
rf_clr_irq_status(FLD_RF_IRQ_PKT_UNMATCH);
}
else if(rf_get_irq_status(FLD_RF_IRQ_PKT_MATCH )){
irq_cnt_match++;
rf_clr_irq_status(FLD_RF_IRQ_PKT_MATCH);
}
else{
rf_clr_irq_status(FLD_RF_IRQ_ALL);
}
//3. main_loop handles reception
//When the correct packet is received
if (rx_flag) {
rx_flag = 0;
//Get the payload address and length
rx_payload = gen_fsk_rx_payload_get((unsigned char *)rx_packet, (unsigned char *)&rx_payload_len);
//Get the rx packet rssi value
rssi = (gen_fsk_rx_packet_rssi_get((unsigned char *)rx_packet) + 110);
//Get the rx packet timestamp
rx_timestamp = gen_fsk_rx_timestamp_get((unsigned char *)rx_packet);
#if UI_LED_ENABLE
gpio_toggle(GPIO_LED_GREEN);
#endif
}
//When sending is complete
if (tx_done_flag) {
tx_done_flag = 0;
//start the SRX2TX
tx_payload[4]++;
//Wait for sending packets
gen_fsk_write_payload(tx_buffer,tx_payload,TX_PAYLOAD_LEN);
//Start receiving packets at 50us
gen_fsk_srx2tx_start(tx_buffer, rf_stimer_get_tick()+50*RF_SYSTEM_TIMER_TICK_1US, 0);
}
//When timeout
if(rx_first_timeout_flag){
rx_first_timeout_flag = 0;
//Start receiving packets at 50us
gen_fsk_srx2tx_start(tx_buffer, rf_stimer_get_tick()+50*RF_SYSTEM_TIMER_TICK_1US, 0);
}
- gen_fsk_stx2rx:
//1. Initialization configuration
//Set the packet format
gen_fsk_packet_format_set(GEN_FSK_MODE_LEGACY_VARIABLE_FORMAT,TX_PAYLOAD_LEN);
//Set the data transfer rate, note that this interface should be placed before other rf configuration interfaces
gen_fsk_datarate_set(GEN_FSK_DATARATE_1MBPS);
//Set the preamble length
gen_fsk_preamble_len_set(4);
//Set the synchronization word length
gen_fsk_sync_word_len_set(SYNC_WORD_LEN_4BYTE);
//Set the transceiver channel and synchronization word
gen_fsk_sync_word_set(GEN_FSK_PIPE0, sync_word);
//Open send/receive channel
gen_fsk_pipe_open(GEN_FSK_PIPE0);
//Set the tx channel
gen_fsk_tx_pipe_set(GEN_FSK_PIPE0);
//Set crc
gen_fsk_set_crc_config(&crc_config);
//Set the signal strength
gen_fsk_radio_power_set(RF_POWER_INDEX_N0p07dBm);
//Set the rx/tx buffer and size
gen_fsk_rx_buffer_set(rx_buf, RX_BUF_NUM, RX_BUF_LEN);
gen_fsk_tx_buffer_set(TX_BUF_NUM,TX_BUF_LEN);
//Set the rf frequency point, set to 7 then the frequency point is 2417
gen_fsk_channel_set(17);
//Set the state machine to auto send/receive
gen_fsk_radio_state_set(GEN_FSK_STATE_AUTO);
//Set packet filtering conditions
#if(PRI_FLT_MODE_EN)
gen_fsk_set_pkt_filter(GEN_FSK_PktFlt);
#endif
//Set rx settle time
gen_fsk_rx_settle_set(89);
//Wait for rx settle
delay_us(90);
//Set interrupt priority
plic_set_priority(IRQ_ZB_RT, 3);
//Enable rf interrupt
plic_interrupt_enable(IRQ_ZB_RT);
//Set interrupt flags
rf_set_irq_mask(FLD_RF_IRQ_RX | FLD_RF_IRQ_TX | FLD_RF_IRQ_RX_TIMEOUT | FLD_RF_IRQ_PKT_UNMATCH | FLD_RF_IRQ_PKT_MATCH);
//Clear rf interrupt
rf_clr_irq_status(FLD_RF_IRQ_ALL);
//Enable interrupt
core_interrupt_enable();
//Wait for tx buffer
gen_fsk_write_payload(tx_buffer,tx_payload,TX_PAYLOAD_LEN);
//Start tx at 50us, and after tx ends, the time from RX settle to RX timeout is 500us.
gen_fsk_stx2rx_start(tx_buffer, rf_stimer_get_tick()+50*RF_SYSTEM_TIMER_TICK_1US, 500);
//2. Interrupt handling reception
if (rf_get_irq_status(FLD_RF_IRQ_TX)){
tx_done_flag = 1;
irq_cnt_tx++;
#if UI_LED_ENABLE
gpio_toggle(GPIO_LED_WHITE);
#endif
rf_clr_irq_status(FLD_RF_IRQ_TX );
}
else if (rf_get_irq_status(FLD_RF_IRQ_RX)){
//Get the address of the receiving packet
rx_packet = rf_get_rx_packet_addr(RX_BUF_NUM,RX_BUF_LEN,rx_buf);
irq_cnt_rx++;
//Determine whether the received packet's CRC is correct
if (gen_fsk_is_rx_crc_ok(rx_packet)) {
irq_cnt_rx_crc_ok++;
#if UI_LED_ENABLE
gpio_toggle(GPIO_LED_BLUE);
#endif
}
rx_flag = 1;
rf_clr_irq_status(FLD_RF_IRQ_RX );
}
else if (rf_get_irq_status(FLD_RF_IRQ_RX_TIMEOUT)){
rx_timeout_flag = 1;
irq_cnt_rx_timeout_cnt++;
rf_clr_irq_status(FLD_RF_IRQ_RX_TIMEOUT );
}
else if(rf_get_irq_status(FLD_RF_IRQ_PKT_UNMATCH )){
irq_cnt_unmatch++;
rf_clr_irq_status(FLD_RF_IRQ_PKT_UNMATCH);
}
else if(rf_get_irq_status(FLD_RF_IRQ_PKT_MATCH )){
irq_cnt_match++;
rf_clr_irq_status(FLD_RF_IRQ_PKT_MATCH);
}
else{
rf_clr_irq_status(FLD_RF_IRQ_ALL);
}
//3. main_loop handles reception
//When the correct packet is received
if (rx_flag) {
rx_flag = 0;
//Get the payload address and length
rx_payload = gen_fsk_rx_payload_get((unsigned char *)rx_packet, (unsigned char *)&rx_payload_len);
//Get the rx packet rssi value
rssi = (gen_fsk_rx_packet_rssi_get((unsigned char *)rx_packet) + 110);
//Get the rx packet timestamp
rx_timestamp = gen_fsk_rx_timestamp_get((unsigned char *)rx_packet);
#if UI_LED_ENABLE
gpio_toggle(GPIO_LED_GREEN);
#endif
delay_ms(200);
tx_payload[4]++;
//Wait for tx buffer
gen_fsk_write_payload(tx_buffer,tx_payload,TX_PAYLOAD_LEN);
//Start tx at 50us
gen_fsk_stx2rx_start(tx_buffer, rf_stimer_get_tick()+50*RF_SYSTEM_TIMER_TICK_1US, 500);
}
//When timeout
if (rx_timeout_flag) {
rx_timeout_flag = 0;
delay_ms(200);
//Start sending last packets at 50us
gen_fsk_stx2rx_start(tx_buffer, rf_stimer_get_tick()+50*RF_SYSTEM_TIMER_TICK_1US, 500);
}
Test Phenomena:
Burn gen_fsk_stx2rx and gen_fsk_srx2tx programs into the two development boards respectively, you will see the green and white light blinking on both boards, and use the BDT tool to check the stx2rx and srx2tx, and you will find that the rx_buf has received packets and the rx interrupt count is increasing.


gen_fsk_stx_dpl_packet
The usage steps for B87 chips:
- gen_fsk_stx_dpl_packet:
//1. Initialization configuration
//Set the data transfer rate, note that this interface should be placed before other rf configuration interfaces
gen_fsk_datarate_set(GEN_FSK_DATARATE_2MBPS);
//Set the length of the leading code
gen_fsk_preamble_len_set(4);
//Set the length of the synchronization word
gen_fsk_sync_word_len_set(SYNC_WORD_LEN_4BYTE);
//Set the transceiver channel and synchronization word
gen_fsk_sync_word_set(GEN_FSK_PIPE0, sync_word);
//Open send/receive channel
gen_fsk_pipe_open(GEN_FSK_PIPE0);
//Set the tx channel
gen_fsk_tx_pipe_set(GEN_FSK_PIPE0);
//Set the variable length packet type
gen_fsk_packet_format_set(GEN_FSK_PACKET_FORMAT_VARIABLE_PAYLOAD, 0);
//Set the signal strength
gen_fsk_radio_power_set(GEN_FSK_RADIO_POWER_0DBM);
//Set the rf frequency point, set to 7 then the frequency point is 2403.5
gen_fsk_channel_set(7);
//Setting the state machine to the active send state
gen_fsk_radio_state_set(GEN_FSK_STATE_AUTO);
//Set tx settle time to 149us
gen_fsk_tx_settle_set(149);
//Automatic disable pid processing
gen_fsk_auto_pid_disable();
//irq setting
//Enable rf tx interrupt
rf_irq_enable(FLD_RF_IRQ_TX);
//Enable rf interrupt
rf_irq_enable(FLD_RF_IRQ_TX);
//Enable interrupt
irq_enable();
tx_payload_length = 5;
//Write tx_buffer
tx_buffer[0] = sizeof(tx_payload);
tx_buffer[1] = 0x00;
tx_buffer[2] = 0x00;
tx_buffer[3] = 0x00;
tx_buffer[4] = tx_payload_length;
memcpy(&tx_buffer[4], tx_payload, sizeof(tx_payload));
//2. main_loop handles reception
//Set the pid manually
gen_fsk_set_pid(&tx_buffer, pid);
//Packets begin to be sent out after 100us
gen_fsk_stx_start(tx_buffer, clock_time()+100*16);
//When the send completion interrupt occurs
while(rf_tx_finish() == 0);
//Clear the relevant interrupt flag bit
rf_tx_finish_clear_flag();
//pid loop
pid = (pid + 1)&0x03;
gpio_toggle(GREEN_LED_PIN);
WaitMs(1000);
Test phenomena:
Since tpll_prx is also a variable-length packet format, the frequency point, synchronization word and synchronization word length of tpll_prx will be the same as that of gen_fsk_stx_dpl_packet and burned into the development board respectively, then we can carry out the communication test, and through the BDT tool to check the tpll_prx, we can see that the tpll_prx can receive a normal packet sent by the stx_dpl_packet.

tpsll (Telink proprietary stack link layer)
The routines associated with the 2.4G proprietary protocol tpsll are listed in the table below:
| Routine | Function introduction |
|---|---|
| tpsll_srx | Send packets through single rx state machine |
| tpsll_stx | Receive packets via single tx state machine |
| tpsll_srx2tx | Receive packets and select whether to send a responsive packet via the single rx to tx state machine |
| tpsll_stx2rx | Receive packets and select whether to send a responsive packet via the single tx to rx state machine |
tpsll_srx/stx
The usage steps for B87 chips:
- tpsll_srx:
//Initialization settings
//Set the data transfer rate, note that this interface should be placed before other rf configuration interfaces
tpsll_init(TPSLL_DATERATE_2M);
//Set the communication frequency
tpsll_channel_set(chn);
//Set the leading code length
tpsll_preamble_len_set(2);
//Set the synchronization word length
tpsll_sync_word_len_set(SYNC_WORD_LEN_4BYTE);
//Set the synchronization word and communication channel
tpsll_sync_word_set(TPSLL_PIPE0,sync_word);
//Open the communication channel
tpsll_pipe_open(TPSLL_PIPE0);
//Set the rx buffer and its size
tpsll_rx_buffer_set((unsigned char *)tpsll_rxbuf,RX_BUF_SIZE);
//Set the communication strength
tpsll_radio_power_set(TPSLL_RADIO_POWER_6DBM);
//Set rx settle time to 90us
tpsll_rx_settle_set(90);
//irq configuration
//Clear all interrupts
rf_irq_disable(FLD_RF_IRQ_ALL);
//Enable related interrupts
rf_irq_enable(FLD_RF_IRQ_RX | FLD_RF_IRQ_FIRST_TIMEOUT);
//Enable rf interrupt
irq_enable_type(FLD_IRQ_ZB_RT_EN);
//Enable interrupt
irq_enable();
//start the SRX
//Start receiving after 50us, since the second parameter is 0 to turn off the rx timeout, it will keep receiving until it receives the packet
tpsll_srx_start(clock_time()+50*16, 0);
//interrupt handling
if (irq_src & FLD_IRQ_ZB_RT_EN) {
if (rf_irq_src & FLD_RF_IRQ_RX) {
rf_irq_clr_src(FLD_RF_IRQ_RX);
rx_test_cnt++;
//If the crc checksum of the received packet is correct, the flag of the corresponding rx is set to 1
if (tpsll_is_rx_crc_ok(tpsll_rxbuf)) {
rx_flag = 1;
}
}
if (rf_irq_src & FLD_RF_IRQ_FIRST_TIMEOUT) {
rf_irq_clr_src(FLD_RF_IRQ_FIRST_TIMEOUT);
rx_first_timeout = 1;
rx_first_timeout_cnt++;
}
}
irq_clr_src2(FLD_IRQ_ALL);
//mian_loop processing
//When the crc correct packet is received
if (rx_flag) {
rx_flag = 0;
//get rx_payload
//Get the payload address
rx_payload = tpsll_rx_payload_get((unsigned char *)tpsll_rxbuf, (unsigned char *)&rx_payload_len);
//Get Received Signal Strength
rssi = (tpsll_rx_packet_rssi_get((unsigned char *)tpsll_rxbuf) + 110);
//Get instantaneous received signal strength
rssi_ins = (tpsll_rx_instantaneous_rssi_get() + 110);
//Get the instantaneous clock time when the synchronization word is relevant
rx_timestamp = tpsll_rx_timestamp_get((unsigned char *)tpsll_rxbuf);
//start the SRX
gpio_toggle(GREEN_LED_PIN);
//Start receiving after 50us, rx time out time is 500us
tpsll_srx_start(clock_time()+50*16, 500);
gpio_write(DEBUG_PIN,1);
gpio_write(DEBUG_PIN,0);
//Get the rx buffer and the length of the payload
tpsll_rx_payload_get(tpsll_rxbuf,&payload_len);
}
if (rx_first_timeout) {//When the rx first timeout occurs
rx_first_timeout = 0;
//start the SRX
//Start receiving after 50us, rx time out time is 500us
tpsll_srx_start(clock_time()+50*16, 500);
}
- tpsll_stx:
//Initialization settings
//Set the data transfer rate, note that this interface should be placed before other rf configuration interfaces
tpsll_init(TPSLL_DATERATE_2M);
//Set the communication frequency
tpsll_channel_set(chn);
//Set the leading code length
tpsll_preamble_len_set(2);
//Set the synchronization word length
tpsll_sync_word_len_set(SYNC_WORD_LEN_4BYTE);
//Set the synchronization word and communication channel
tpsll_sync_word_set(TPSLL_PIPE0,sync_word);
//Open the communication channel
tpsll_pipe_open(TPSLL_PIPE0);
//Set the tx channel
tpsll_tx_pipe_set(TPSLL_PIPE0);
//Set the communication strength
tpsll_radio_power_set(TPSLL_RADIO_POWER_6DBM);
//Set tx settle time to 113us.
tpsll_tx_settle_set(113);
//irq configuration
//Clear all interrupts
rf_irq_disable(FLD_RF_IRQ_ALL);
//Enable related interrupts
rf_irq_enable(FLD_RF_IRQ_TX);
//Enable rf interrupt
irq_enable_type(FLD_IRQ_ZB_RT_EN);
//Enable interrupt
irq_enable();
//start the STX
//Write payload for sending packet data
tpsll_tx_write_payload(payload,payload_len);
//Send packet after 50us
tpsll_stx_start(clock_time()+50*16);
//interrupt handling
if (irq_src & FLD_IRQ_ZB_RT_EN) {
if (rf_irq_src & FLD_RF_IRQ_TX) {
rf_irq_clr_src(FLD_RF_IRQ_TX);
tx_done_flag = 1;
}
}
irq_clr_src2(FLD_IRQ_ALL);
//mian_loop processing
tx_done_flag = 0;
payload[4]++;
gpio_write(DEBUG_PIN,1);
//Write payload for sending packet data
tpsll_tx_write_payload(payload,payload_len);
//Send packet after 50us
tpsll_stx_start(clock_time()+100*16);
gpio_write(DEBUG_PIN,0);
//When sending is complete
while (tx_done_flag == 0);
cnt++;
gpio_write(GREEN_LED_PIN, 1); //LED On
WaitMs(1000);
gpio_write(GREEN_LED_PIN, 0); //LED Off
Test phenomena:
Using two development boards to burn tpsll_stx\tpsll_srx routines for communication test respectively, with BDT tool we can see that tpsll_srx receives packets from tpsll_stx and the receive test count is increasing.
tpsll_srx2tx/stx2rx
The usage steps for B87 chips:
- tpsll_srx2tx:
//Initialization settings
//Set the data transfer rate, note that this interface should be placed before other rf configuration interfaces
tpsll_init(TPSLL_DATERATE_2M);
//Set the communication frequency
tpsll_channel_set(chn);
//Set the leading code length
tpsll_preamble_len_set(2);
//Set the synchronization word length
tpsll_sync_word_len_set(SYNC_WORD_LEN_4BYTE);
//Set the synchronization word and communication channel
tpsll_sync_word_set(TPSLL_PIPE0,sync_word);
//Open the communication channel
tpsll_pipe_open(TPSLL_PIPE0);
//Set the rx buffer and its size
tpsll_rx_buffer_set((unsigned char *)tpsll_rxbuf,RX_BUF_SIZE);
//Set the communication strength
tpsll_radio_power_set(TPSLL_RADIO_POWER_6DBM);
//Set rx settle time to 90us
tpsll_rx_settle_set(90);
//irq configuration
//Clear all interrupts
rf_irq_disable(FLD_RF_IRQ_ALL);
//Enable related interrupts
rf_irq_enable(FLD_RF_IRQ_TX | FLD_RF_IRQ_RX | FLD_RF_IRQ_FIRST_TIMEOUT);
//Enable rf interrupt
irq_enable_type(FLD_IRQ_ZB_RT_EN);
//Enable interrupt
irq_enable();
//start the SRX
//Write the payload of the answer packet
tpsll_tx_write_payload((unsigned char *)payload,payload_len);
//Start to receive after 50us, start to send packets automatically at the end of receiving, since the second parameter is 0 means turn off the rx timeout, it will keep receiving until it receives the packet
tpsll_srx2tx_start(clock_time()+50*16, 0);
//interrupt handling
if (irq_src & FLD_IRQ_ZB_RT_EN) {
if (rf_irq_src & FLD_RF_IRQ_RX) {
rf_irq_clr_src(FLD_RF_IRQ_RX);
rx_test_cnt++;
//If the crc checksum of the received packet is correct, the flag of the corresponding rx is set to 1
if (tpsll_is_rx_crc_ok((unsigned char *)tpsll_rxbuf) ){
rx_flag = 1;
gpio_write(RX_PIN,1);
gpio_write(RX_PIN,0);
}
}
if (rf_irq_src & FLD_RF_IRQ_FIRST_TIMEOUT) {
rf_irq_clr_src(FLD_RF_IRQ_FIRST_TIMEOUT);
rx_first_timeout = 1;
rx_first_timeout_cnt++;
}
if (rf_irq_src & FLD_RF_IRQ_TX) {
rf_irq_clr_src(FLD_RF_IRQ_TX);
tx_done_flag = 1;
gpio_write(TX_PIN,1);
gpio_write(TX_PIN,0);
}
}
irq_clr_src2(FLD_IRQ_ALL);
//mian_loop processing
//When the crc correct packet is received
if (rx_flag) {
rx_flag = 0;
gpio_toggle(GREEN_LED_PIN);
//Get the payload address
rx_payload = tpsll_rx_payload_get((unsigned char *)tpsll_rxbuf, (unsigned char *)&rx_payload_len);
//Getting Received Signal Strength
rssi = (tpsll_rx_packet_rssi_get((unsigned char *)tpsll_rxbuf) + 110);
//Get the instantaneous clock time when the synchronization word is relevant
rx_timestamp = tpsll_rx_timestamp_get((unsigned char *)tpsll_rxbuf);
//When the packet has been sent
if (tx_done_flag) {
tx_done_flag = 0;
//start the SRX2TX
payload[4]++;
//Write the payload of the answer packet
tpsll_tx_write_payload((unsigned char *)payload,payload_len);
//Start to receive after 50us, start to send packets automatically at the end of receiving, since the second parameter is 0 means turn off the rx timeout, it will keep receiving until it receives the packet
tpsll_srx2tx_start(clock_time()+50*16, 0);
}
}
- tpsll_stx2rx:
//1. Initialization settings
tpsll_init(TPSLL_DATERATE_2M);//Set the data transfer rate, note that this interface should be placed before other rf configuration interfaces
tpsll_channel_set(chn);//Set the communication frequency
tpsll_preamble_len_set(2);//Setting the leading code length
tpsll_sync_word_len_set(SYNC_WORD_LEN_4BYTE);//Set the synchronization word length
tpsll_sync_word_set(TPSLL_PIPE0,sync_word);//Set the synchronization word and communication channel
tpsll_pipe_open(TPSLL_PIPE0);//Open the communication channel
tpsll_tx_pipe_set(TPSLL_PIPE0);//Set the tx channel
tpsll_rx_buffer_set((unsigned char *)tpsll_rxbuf,RX_BUF_SIZE);
tpsll_radio_power_set(TPSLL_RADIO_POWER_6DBM);//Set the communication strength
tpsll_tx_settle_set(113);//Set tx settle time to 113us
//irq configuration
//Clear all interrupts
rf_irq_disable(FLD_RF_IRQ_ALL);
//Enable related interrupts
rf_irq_enable(FLD_RF_IRQ_TX|FLD_RF_IRQ_RX | FLD_RF_IRQ_RX_TIMEOUT);
//Enable rf interrupt
irq_enable_type(FLD_IRQ_ZB_RT_EN);
//Enable interrupt
irq_enable();
//start the STX2RX
//Write payload for sending packet data
tpsll_tx_write_payload(payload,payload_len);
//Send the packet after 50us, start to receive the answer packet after sending the packet, the rx timeout time is 250us.
tpsll_stx2rx_start(clock_time()+50*16,250);
//2. Interrupt handling
if (irq_src & FLD_IRQ_ZB_RT_EN) {
if (rf_irq_src & FLD_RF_IRQ_RX) {
rf_irq_clr_src(FLD_RF_IRQ_RX);
rx_test_cnt++;
//When a packet with the correct crc is received, the corresponding flag is set to 1
if (tpsll_is_rx_crc_ok((unsigned char *)tpsll_rxbuf)) {
rx_flag = 1;
gpio_write(RX_PIN,1);
gpio_write(RX_PIN,0);
}
}
if (rf_irq_src & FLD_RF_IRQ_RX_TIMEOUT) {
rf_irq_clr_src(FLD_RF_IRQ_RX_TIMEOUT);
rx_timeout_flag = 1;
rx_first_timeout_cnt++;
}
if (rf_irq_src & FLD_RF_IRQ_TX) {
rf_irq_clr_src(FLD_RF_IRQ_TX);
tx_done_flag = 1;
gpio_write(TX_PIN,1);
gpio_write(TX_PIN,0);
}
}
irq_clr_src2(FLD_IRQ_ALL);
//3. mian_loop processing
//When the rx timeout interrupt occurs
if (rx_timeout_flag) {
rx_timeout_flag = 0;
WaitMs(100);
//Write payload for sending packet data
tpsll_tx_write_payload((unsigned char *)payload,payload_len);
//Send the packet after 50us, start to receive the answer packet after sending the packet, the rx timeout time is 250us.
tpsll_stx2rx_start(clock_time()+50*16,250);
}
//When the rx interrupt occurs
if (rx_flag) {
rx_flag = 0;
gpio_toggle(GREEN_LED_PIN);
WaitMs(100);
payload[4]++;
//Write payload for sending packet data
tpsll_tx_write_payload((unsigned char *)payload,payload_len);
//Send the packet after 50us, start to receive the answer packet after sending the packet, the rx timeout time is 250us.
tpsll_stx2rx_start(clock_time()+50*16,250);
}
Test phenomena:
Use two development boards to burn tpsll_stx2rx\tpsll_srx2tx routines for communication test respectively, with BDT tool you can see that tpsll_srx2tx\tpsll_stx2rx receives packets sent from each other and the receive test count is increasing.
Basic Modules
In addition to the primary proprietary protocol related routines, the 2.4GHz proprietary SDK provides various basic routines such as UART, timer, I2C, SPI, PWM, ADC, sleep, etc. Users can refer to the basic routine code for application development, which improves the development efficiency and reduces the development burden.
The following is an introduction to the use of basic routines using the B87 SDK as an example:
The following three functions are initialization functions that must be used by all routines.
cpu_wakeup_init(LDO_MODE, EXTERNAL_XTAL_24M);//MCU initialization, LDO_MODE: LDO current mode, EXTERNAL_XTAL_24M: external 24M clock
user_read_flash_value_calib();//Used to read optimised values from flash
clock_init(SYS_CLK_24M_Crystal);//Clock initialization, select 24M system clock source
ADC
Users can use the adc_sample routine for adc sampling, there are the following six modes.
(1) ADC_BASE_MODE
This mode is used for GPIO analog signal inputs.
(2) ADC_VBAT_MODE
In this mode, the GPIO output is set to high for GPIO sampling for vbat sampling, at this time the voltage of the GPIO equals that of the Vbat. This method does not require hardware connectivity, users can set up a pin that does not encapsulate, to save gpio resources.
(3) ADC_VBAT_CHANNEL_MODE
In this mode, the battery voltage is obtained via the battery channel.
(4) ADC_TEMP_MODE_EE
This mode is used to obtain the temperature value of the temperature sensor.
(5) ADC_RNG_MODE
This mode is used to generate random numbers. In addition to generating random numbers in this routine, the rand_num_gen routine also generates random numbers by the same principle.
(6) MANNUAL_MODE_GET_ADC_SAMPLE_RESULT
This mode is used for manual sampling. Note: When sampling manually (ADC_NDMA_MODE), only one adc_code can be acquired at a time, and the ADC sampling function will be turned off during the period, and it must be ensured that the time interval between successive acquisitions of the adc_code is greater than 2 sampling cycles.
Chip difference:
The B87 chip supports all modes, the B85 chip supports the first three and manual modes, and the B80 chip supports the first four and manual modes.
Usage steps:
//1. Select mode
#define ADC_BASE_MODE 1 //GPIO voltage
#define ADC_VBAT_MODE 2 //Battery Voltage
#define ADC_VBAT_CHANNEL_MODE 3 //Vbat channel Battery Voltage
#define ADC_TEMP_MODE_EE 4 //Temp test
#define ADC_RNG_MODE 5 //Random number Gernerator
#define MANNUAL_MODE_GET_ADC_SAMPLE_RESULT 0 //Vbat channel Battery Voltage mannual mode
//2. Initialization
#if(ADC_MODE==ADC_RNG_MODE)//Select the random number generation mode
random_generator_init();//Generate random number initialization
#else
adc_init();//ADC initialization
#if(ADC_MODE==ADC_BASE_MODE)//Select ADC_BASE_MODE mode
adc_base_init(ADC_INPUT_PIN);//ADC_BASE_MODE mode initialization
adc_set_ain_pre_scaler(ADC_PRESCALER_1F8);//The prescaler can be set after ADC_BASE_MODE mode initialization
#elif (ADC_MODE==ADC_VBAT_MODE)//Select ADC_VBAT_MODE mode
adc_vbat_init(ADC_INPUT_PIN);//ADC_VBAT_MODE mode initialization
#elif (ADC_MODE==ADC_VBAT_CHANNEL_MODE)//Select ADC_VBAT_CHANNEL_MODE mode
adc_vbat_channel_init();//ADC_VBAT_CHANNEL_MODE mode initialization
#elif (ADC_MODE==ADC_TEMP_MODE_EE)//Select ADC_TEMP_MODE_EE mode
adc_temp_init();//ADC_TEMP_MODE_EE mode initialization
#endif
adc_power_on_sar_adc(1);//After setting the ADC parameters, enalbe the ADC power
#endif
//3. mainloop sampling processing
#if(ADC_MODE==ADC_RNG_MODE)
rns_val = rand();//Get random numbers
#else
#if(ADC_MODE==ADC_BASE_MODE || ADC_MODE==ADC_VBAT_MODE||ADC_MODE==ADC_VBAT_CHANNEL_MODE)
sample_result[i] = adc_sample_and_get_result();//Get ADC sampling
i = (i + 1) % 16;
WaitMs(50);
gpio_toggle(GREEN_LED_PIN);
#elif (ADC_MODE==ADC_TEMP_MODE_EE)
temp_new_val = adc_temp_result();//Get temperature sampling
#endif
#if(MANNUAL_MODE_GET_ADC_SAMPLE_RESULT==1)
adc_manual_val = adc_sample_and_get_result_manual_mode();//Get the sampling value in manual mode
#endif
#endif
Test phenomena:
Burn the programme into the development board, take ADC_VBAT_MODE mode as an example, with the BDT tool you can see that 0xcc7 decimal representation is 3271 MV and the green light flashes.

aes_128
For the hardware AES128 encryption and decryption, users can refer to the routine aes_128.
Usage steps:
//1. Initialization
unsigned char Encrypt_data[13];
unsigned char Decrypt_data[13];
unsigned char aes_init[13] = {...};
unsigned char AES_Key[16] = {...};
//2. mianloop encryption and decryption process
aes_init[0]++;
aes_encrypt(AES_Key,aes_init,Encrypt_data);//encryption
aes_decrypt(AES_Key,Encrypt_data,Decrypt_data);//decryption
gpio_toggle(GREEN_LED_PIN);
WaitMs(500); //delay 500 ms
Test phenomena:
After burning the program into the development board, with the BDT tool, you can see the green light flashes, and the decrypted Decrypt_data array is the same as the aes_init array before encryption, indicating that the encryption and decryption is successful.

Low Power Consumption
There are three modes of low power consumption, namely suspend, deep sleep, and deep retention. The suspend mode has fewer modules to switch off when sleeping compared to deep retention mode, so it saves less power and is suitable for short sleep. The deep retention mode needs to keep some values unchanged after power down compared to deep mode, so it saves less power. The deep sleep mode saves the most power and is suitable for long sleep. Please refer to the driver handbook for more about low power mode. Because GPIO and timer wake-up are used more, they are the main explanations here. The following table shows the relationship between the relevant routines and the wake-up source:
| Wake-up source | Related routine |
|---|---|
| GPIO | suspend_gpio_wakeup, deep_gpio_wakeup |
| TIMER | suspend_32k_timer_wakeup, deep_32k_timer_wakeup |
| MDEC | suspend_mdec_wakeup, deep_mdec_wakeup |
| USB | suspend_core_wakeup |
Usage steps:
(1) Select different sleep modes with different sleep routines and macros.
(2) The following interface is called before all initialization to select that the wake-up source clock is an internal clock wake-up.
blc_pm_select_internal_32k_crystal();
(3) Call the following interface before calling cpu_sleep_wakeup to set the gpio to a high impedance state to prevent current leakage.
gpio_high_z_config();//set all GPIO as high_Z state, avoiding current leakage
(4) When the wake-up source is USB, you need to call pm_set_suspend_power_cfg(PM_POWER_USB, 1) interface, so that the USB will not be powered down during the sleep period to be used as the wake-up source.
(5) Call the cpu_sleep_wakeup(SleepMode_TypeDef sleep_mode, SleepWakeupSrc_TypeDef wakeup_src, unsigned int wakeup_tick) interface to go to sleep.
Parameter: sleep_mode is the sleep mode.
Parameters: wakeup_src is the wakeup source.
Parameter: wakeup_tick is the time to sleep, 16 means 1us,16*1000 means 1ms.
(6) Users can get to know the state of the pm via the pmParam global variable.
/**
* @brief deepsleep wakeup status
*/
typedef struct{
unsigned char is_deepretn_back;//mcu wake up from deep sleep retention or not.
unsigned char is_pad_wakeup;//Whether or not this time it was awakened by the pad
unsigned char wakeup_src;//Which wake-up source this time is being awakened by, including the wake-up states of PAD, TIMER, MDEC, LPC, and CORE.
unsigned char rsvd;
}pm_para_t;
extern _attribute_aligned_(4) pm_para_t pmParam;
Test phenomena
Burn the program into the development board:
deep sleep mode:
If it is a gpio wakeup, the program will start running again after entering sleep by triggering the corresponding gpio interrupt, and you can see the green light is on for three seconds and then off again to enter sleep; if it is a timer wakeup, the green light will be on every other sleep time.
deep retention mode:
The phenomenon is similar to deep sleep, the only difference is that the data in the .retention_data segment will not change after the power is turned off, so every time the sleep is stored in the segment of a counting variable will be accumulated, when the number of times of sleep exceeds the number of times specified in the program, it will enter the cycle of blinking the green light all the time.
suspend mode:
If it is a GPIO wake-up, because the wake-up function is placed in the while loop, after entering sleep, the corresponding GPIO interrupt is triggered, and the program will continue to run from the sleep function after entering sleep, so the phenomenon we see in combination with the routine is that after entering sleep and triggering the GPIO interrupt, the green light will be lit up for 3 seconds and then enter sleep again. If it is timer wake-up, after entering sleep, trigger the corresponding timer interrupt, the program will continue to run from the sleep function after entering sleep, combined with the routine logic analyzer connected to the DEBUG_IO_PIN can see the phenomenon of DEBUG_IO_PIN is 1.1ms between consecutive intervals after pulling high 8 times, the board began to blink the green light all the time.
Flash
Users who want to operate on flash can refer to the flash_operation routine.
Usage steps
(1) In order to ensure the safe operation of flash, you can turn on the low power detection through the macro BATT_CHECK_ENABLE before the operation of flash, when the low power detection detects that the voltage is lower than the safe voltage at this time, then it will not continue the following flash operation, and enter into the cycle of blinking red light.
(2) Reads the current flash mid via the flash_read_mid() interface.
(3) Through flash_erase_sector(), flash_read_page(), flash_write_page() these three interfaces to erase, read, write operations on the flash address, operation success, the corresponding test value is set to 1, the operation fails, the corresponding status error value is set to 1 and fall into the while dead loop.
(4) Through the reading of the mid selection into the different models of flash lock, unlock operation test, operation is successful, the corresponding test value is set to 1, the operation fails, the corresponding state error value is set to 1 and fall into the while dead loop, its related interface for flash_lock_midxxxxxx(), flash_unlock_midxxxxxx().
(5) In the mid for the 146085 model flash lock, unlock operation test and otp erase, write, and through the control macro FLASH_OTP_LOCK to choose whether to lock otp operation, the success of the operation, the corresponding test value is set to 1, the operation fails, the corresponding state of the error value is set to 1 and fall into the while dead loop.
Test phenomena
Burn the program into the development board, the development board flashes green to indicate that all operations on the flash are successful; the development board flashes red to indicate that the current voltage is lower than the safe voltage of the flash in low power detection error; the development board is stuck in the while loop to indicate that there is a flash operation failure, the corresponding status error value is set to 1.
Timer
The routines concerning the clock are listed in the table below:
| Routine | Usage |
|---|---|
| timer_gpio_pulse_width | Starting from the GPIO rising edge \\ falling edge to the next falling edge \\ the end of the rising edge triggers the timer interrupt and so on and so forth, which can be used to calculate the pulse length. |
| timer_gpio_trigger | The timer counts plus 1 for every rising edge \falling edge that occurs in the GPIO, and a timer interrupt is generated when the timer count reaches the specified set number of counts. |
| timer_sys_clock | Used for timing, timer interrupt will be generated when the specified time is reached. |
| timer_tick | Timing that does not generate an interrupt, whenever the rising edge of the system clock is detected, the counter of the timer is increased by 1. The initial value of the timer can be set manually, and it will go back to 0 when it reaches the maximum value. |
| timer_wathcdog | Watchdog function, arriving at the specified time on the microcontroller reset or feed the dog does not enter the microcontroller reset. |
Usage steps
timer_gpio_pulse_width:
timer2_gpio_init(SAMPLE_INTPUT_PIN, POL_FALLING);//The first parameter is the input gpio, and the second parameter indicates whether the timer is triggered by a rising or falling edge.
irq_enable();//Turn on interruptions
timer2_set_mode(TIMER_MODE_GPIO_WIDTH,0,0);//The first parameter selects the pulse width mode for gpio calculation, the next two parameters default to 0
timer_start(TIMER2);//Turn on the TIMER2 timer
timer_gpio_trigger:
timer2_gpio_init(TRIGGER_INPUT_PIN, POL_FALLING);//The first parameter is the input gpio, and the second parameter indicates whether the timer is triggered by a rising or falling edge.
irq_enable();//Turn on interruptions
timer2_set_mode(TIMER_MODE_GPIO_TRIGGER,0,3);//The first parameter selects the gpio trigger mode, the second parameter is the initial value of the count, and the third parameter indicates at which rising/falling edge the timer interrupt is triggered.
timer_start(TIMER2);//Turn on the TIMER2 timer
timer_sys_clock:
timer2_set_mode(TIMER_MODE_SYSCLK,0,1000 * CLOCK_SYS_CLOCK_1MS);//The first parameter selects the system clock mode, the second parameter is the initial count value, and the third parameter is the timed tick value
timer_start(TIMER2);//Turn on the TIMER2 timer
irq_enable();//Turn on interruptions
timer_tick:
timer2_set_mode(TIMER_MODE_TICK,0,0);//The first parameter selects the clock counting mode, the second parameter is the initial counting value, and the third parameter is the timed tick value, which is set to 0 to indicate that it is not timed.
timer_start(TIMER2);//Turn on the TIMER2 timer
timer_wathcdog:
wd_set_interval_ms(1000,CLOCK_SYS_CLOCK_1MS);//The first parameter is the unit of time to feed the dog when ms, the second parameter is the tick value of 1 ms under the system clock
wd_start();//Turn on the Watchdog.
//wd_clear();//Turn off the watchdog.
Test phenomena
Burn the program into the development board:
timer_gpio_pulse_width:Connect the sample pin to the output pin and burn the program you can see the green light blinking, pull out the green light pin with a logic analyzer, you can see the timer interrupt generated every 100ms meets the output pin every 50ms toggle the same jump along the cycle time.
timer_gpio_trigger:When TRIGGER_INPUT_PIN triggers a timer interrupt every three falling edges of the input, the green light is flipped on and off once.
timer_sys_clock:Flashing red light every second.
timer_tick:The green light is blinking, and with the BDT tool you can see that the reg_tmr2_tick value is increasing to the maximum value and going back to 0 to continue increasing.
timer_wathcdog:You can see the green light flashing periodically.

GPIO
For GPIO routines you can refer to the gpio_irq routine, which is used as follows:
Usage steps
(1) First select the different gpio functions via the control macros.
(2) Different functions have different setup interfaces and usage.
- Setting the gpio high resistance state can be done by calling the gpio_shutdown(GPIO_ALL) interface directly.
- gpio interrupt, first call gpio_setup_up_down_resistor setup pin and pin pull-up resistor/pin pull-down resistor, then call gpio_set_interrupt interface setup pin and pin falling edge/rising edge triggered interrupt, and finally enable interrupt.
- To use gpio as a key interrupt, first call gpio_setup_up_down_resistor to set up the pin and pin pull-up resistor, then call gpio_set_interrupt interface to set up the pin and pin falling edge triggered interrupt, and finally enable the interrupt.
- gpio rising edge triggered interrupt call gpio_setup_up_down_resistor interface to set the pin and pin pull-down, and then call gpio_set_interrupt_risc0 to set the rising edge triggered interrupt, and finally enable the interrupt.
- gpio falling edge triggered interrupt call gpio_setup_up_down_resistor interface to set the pin and pull-up resistor, and then call gpio_set_interrupt_risc1 to set the falling edge triggered interrupt, and finally enable the interrupt.
Test phenomena
Burn the program into the development board:
GPIO interrupt: every time GPIO is given a low level, the green light will turn on and off.
The GPIO is used as a key interrupt: each time the corresponding key is pressed it flips the green light on and off.
GPIO rising edge triggered interrupt: the GPIO output will be flipped once every 10ms to access the interrupt pin, the phenomenon is that the green light is flipped every 20ms, and the green light is always on to the naked eye.
GPIO falling edge triggered interrupt: the GPIO output will be flipped once every 10ms to access the interrupt pin, the phenomenon is that the green light is flipped every 20ms, and the green light is always on to the naked eye.
PWM
To control the pulse output, it has 5 related routines as shown in the table below.
| Routine | Function introduction |
|---|---|
| pwm_normal | Continuously sending pulses with the same duty cycle can be stopped immediately, and the duty cycle can be modified during transmission. |
| pwm_count | Continuously sending a preset number of pulses of the same duty cycle can be stopped immediately and modification of the duty cycle during sending is not valid. |
| pwm_ir | Continuously send a preset number of groups of pulses with the same duty cycle, the duty cycle can be changed during the sending period, and it can be stopped directly or switched to pwm_count mode to send a group and then stop, without a start signal. |
| pwm_ir_fifo | When the signal settings to be sent are placed in the FIFO, groups of pulses with different duty cycles can be sent sequentially according to the settings. |
| pwm_ir_dma_fifo | Similar to IR FIFO mode, except that the configuration is not written directly in the FIFO by the MCU, but is written to the FIFO via DMA. |
Usage steps
pwm_normal:
pwm_set_clk(CLOCK_SYS_CLOCK_HZ, CLOCK_SYS_CLOCK_HZ);//Setting the pwm frequency
gpio_set_func(GPIO_PA2, AS_PWM0);//Setting the pwm pin
pwm_set_mode(PWM0_ID, PWM_NORMAL_MODE);//Setting the pwm channel and mode
pwm_set_cycle_and_duty(PWM0_ID, (unsigned short) (1000 * CLOCK_SYS_CLOCK_1US), (unsigned short) (500 * CLOCK_SYS_CLOCK_1US));//Setting the period and duty cycle
pwm_start(PWM0_ID);//Start pwm
pwm_count:
pwm_set_clk(CLOCK_SYS_CLOCK_HZ, CLOCK_SYS_CLOCK_HZ);//Setting the pwm frequency
gpio_set_func(PWM_PIN, AS_PWMx);//Setting the pwm pin
pwm_set_mode(PWM_ID, PWM_COUNT_MODE);//Setting the pwm channel and mode
pwm_set_pulse_num(PWM_ID,PWM_PULSE_NUM);//Setting the number of pulses in a pulse group
pwm_set_cycle_and_duty(PWM_ID, 1000 * CLOCK_SYS_CLOCK_1US, 500* CLOCK_SYS_CLOCK_1US);//Setting the period and duty cycle
pwm_set_interrupt_enable(PWM_IRQ_PWM0_PNUM);//Enable pwm send completion interrupt
irq_set_mask(FLD_IRQ_SW_PWM_EN);//Setting the Interrupt Flag Bit
irq_enable();//Turn on interruptions
pwm_start(PWM_ID);//Start pwm
pwm_ir:
pwm_set_clk(CLOCK_SYS_CLOCK_HZ, CLOCK_SYS_CLOCK_HZ);//Setting the pwm frequency
gpio_set_func(PWM_PIN, AS_PWMx);//Setting Pins
pwm_set_mode(PWM_ID, PWM_IR_MODE);//Setting the pwm mode
pwm_set_pulse_num(PWM_ID,PWM_PULSE_NUM);//Setting the number of pulses in a pulse group
pwm_set_cycle_and_duty(PWM_ID, 1000 * CLOCK_SYS_CLOCK_1US, 500 * CLOCK_SYS_CLOCK_1US);//Setting the period and duty cycle
pwm_set_interrupt_enable(PWM_IRQ_PWM0_PNUM);//Enable pwm send completion interrupt
irq_set_mask(FLD_IRQ_SW_PWM_EN);//Setting the Interrupt Flag Bit
irq_enable();//Turn on interruptions
pwm_start();//Start pwm
pwm_ir_fifo:
pwm_set_clk(CLOCK_SYS_CLOCK_HZ, CLOCK_SYS_CLOCK_HZ);//Setting the pwm frequency
gpio_set_func(PWM_PIN, AS_PWMx);//Setting the pwm pin
pwm_set_mode(PWM_ID, PWM_IR_FIFO_MODE);//Setting the pwm mode
pwm_set_cycle_and_duty(PWM_ID,1000 * CLOCK_SYS_CLOCK_1US, 333 * CLOCK_SYS_CLOCK_1US);//Setting the period and duty cycle
pwm_set_pwm0_shadow_cycle_and_duty(1000 * CLOCK_SYS_CLOCK_1US, 500 * CLOCK_SYS_CLOCK_1US);//Setting the pwm0 period to follow the inverse duty cycle
pwm_ir_fifo_set_data_entry(PWM_PULSE_NUM1,0,1);//Sets the number of pulses in a pulse train, whether or not to use inverse duty cycle and whether or not to carrier
pwm_ir_fifo_set_data_entry(...);
.....
pwm_ir_fifo_set_data_entry(...);
pwm_ir_fifo_set_irq_trig_level(1);//Setting the num in the FIFO when triggering transmission
pwm_set_interrupt_enable(PWM_IRQ_PWM0_IR_FIFO);//Enable pwm interrupt
irq_set_mask(FLD_IRQ_SW_PWM_EN);//Enable pwm flag bit
irq_enable();//Turn on interruptions
pwm_start(PWM_ID);//Start pwm
pwm_ir_dma_fifo:
pwm_set_clk(CLOCK_SYS_CLOCK_HZ, CLOCK_SYS_CLOCK_HZ);//Setting the pwm frequency
gpio_set_func(PWM_PIN, AS_PWMx);//Setting the pwm pin
pwm_set_mode(PWM_ID, PWM_IR_DMA_FIFO_MODE);//Setting the pwm mode
pwm_set_cycle_and_duty(PWM_ID, IR_DMA_MAX_TICK, IR_DMA_CMP_TICK);//Setting the period and duty cycle
pwm_set_pwm0_shadow_cycle_and_duty(IR_DMA_SHADOW_MAX_TICK,IR_DMA_SHADOW_CMP_TICK);//Setting the pwm0 period to follow the inverse duty cycle
unsigned char index=2;
IR_DMA_Buff[index++]= pwm_config_dma_fifo_waveform(1, PWM0_PULSE_NORMAL, 9000 * CLOCK_SYS_CLOCK_1US/IR_DMA_MAX_TICK);//Sets the number of pulses in a pulse train, whether or not to use inverse duty cycle and whether or not to carrier
IR_DMA_Buff[index++]= pwm_config_dma_fifo_waveform(.....);
......
IR_DMA_Buff[index++]= pwm_config_dma_fifo_waveform(.....);
unsigned int length = index*2 - 4;//The first four bytes are data length bytes, not included in the actual length to be sent
unsigned char* buff = &IR_DMA_Buff[0];//Setting the dma length
buff[0]= length&0xff;
buff[1]= (length>>8)&0xff;
buff[2]= (length>>16)&0xff;
buff[3]= (length>>24)&0xff;
pwm_set_dma_address(&IR_DMA_Buff);//Setting the dma handling address
pwm_set_interrupt_enable(PWM_IRQ_PWM0_IR_DMA_FIFO_DONE);//Enable pwm interrupt
irq_set_mask(FLD_IRQ_SW_PWM_EN);//Enable pwm flag bit
irq_enable();//Enable interruptions
pwm_start_dma_ir_sending();//Start dma sending
Test phenomena
Burn the program into the development board and use a logic analyzer to observe the PWM waves generated by the following modes:
pwm_normal: Three sets of continuous PWM waves are generated and the duty cycle and period match those set.
pwm_count:Generate a set of PWM waves with the duty cycle and period and the number of pulses in phase with the settings.
pwm_ir: Four groups of PWM waves with the same period, duty cycle and number of pulses form a PWM wave.
pwm_ir_fifo: Two sets of cyclically discontinuous pwm waves with different numbers and opposite duty cycles whose numbers, periods, and duty cycles match the settings.

pwm_ir_dma_fifo: Combining the code one can first see two large sets of imperfectly continuous pwm waves of the same regularity with the same number but different periods, followed by a set of discontinuous equally spaced pwm waves.
UART
UART is an asynchronous full-duplex serial communication protocol that has four related routines as shown in the table below.
| Routine | Function introduction |
|---|---|
| uart_dma | Serial port sends and receives data via dma |
| uart_ndma | Serial port does not send and receive data via dma, and can control the amount of data that triggers a send/receive interrupt. |
| uart_soft_rx | Software gpio analog serial port to receive data |
| uart_io_printf | Software gpio emulates serial port to send data |
Usage steps
uart_dma:
uart_recbuff_init((unsigned char *)&rec_buff, sizeof(rec_buff));//Initialize receive buffer
uart_gpio_set(UART_TX_PA2, UART_RX_PD6);//Setting the transceiver pins
uart_reset(); //Reset the uart digital registers, so settings about the uart must come after this
uart_init_baudrate(115200,CLOCK_SYS_CLOCK_HZ,PARITY_NONE, STOP_BIT_ONE);//Setting the baud rate, clock, parity and stop bits
uart_dma_enable(1, 1);//The data from the uart will be moved to the dma to be sent, so you need to enable the uart dma.
irq_set_mask(FLD_IRQ_DMA_EN);//Turn on the dma interrupt flag bit
dma_chn_irq_enable(FLD_DMA_CHN_UART_RX | FLD_DMA_CHN_UART_TX, 1);//Enable interrupts for uart rx/tx dma
uart_mask_tx_done_irq_enable();//Turn on the interrupt flag bit for tx_done
uart_mask_error_irq_enable();//Turn on the error flag bit to enter an interrupt when a parity and stop bit error occurs.
irq_enable_type(FLD_IRQ_UART_EN);//Enable uart interrupt
uart_ndma:
uart_gpio_set(UART_TX_PA2, UART_RX_PD6);//Setting the transceiver pins
uart_reset(); //Reset the uart digital registers, so settings about the uart must come after this
uart_init_baudrate(115200,CLOCK_SYS_CLOCK_HZ,PARITY_NONE, STOP_BIT_ONE);//Set the baud rate, clock, parity and stop bits
uart_dma_enable(0, 0);//The data from the uart will be moved to the dma to be sent, so you need to enable the uart dma.
irq_disable_type(FLD_IRQ_DMA_EN);//Disable DMA Interrupt
dma_chn_irq_enable(FLD_DMA_CHN_UART_RX | FLD_DMA_CHN_UART_TX, 0);//Enable interrupts for uart rx/tx dma
uart_irq_enable(1,0); //Enable uart rx interrupt
uart_ndma_irq_triglevel(1,0);//Set the trigger threshold, 1 means that sending and receiving one byte will trigger the receive interrupt.
uart_mask_error_irq_enable();//Turn on the error flag bit to enter an interrupt when a parity and stop bit error occurs.
uart_soft_rx:
soft_uart_rx_init(GPIO_PA4,9600); // Set the receive pins and bit rate
soft_uart_rx_enable();//Enable software reception
soft_uart_rx_data_process(soft_uart_rx_callback)//Continuous data reception
uart_io_printf:
array_printf( (unsigned short*)&trans_buff,trans_buff_Len);//Sets the transmit data address and length
Test phenomena
Burn the program into the development board:
uart_dma:After connecting the uart's tx to the rx on a development board and using the BDT tool, you can see that the buffer data is sent and received normally, note that since the DMA is a 4byte transport, there may be useless data in the received data.

uart_ndma:After cross-connecting the tx and rx of two development boards and using the BDT tool, Iyou can see that the buffer data is sent and received normally.

uart_soft_rx:Use the serial port to usb tool to send data to the microcontroller, will be in the serial port assistant tool to see the data received by the microcontroller. The first line of the first data for the reception of data array index, a total of two arrays, the length of the array for 200, when a reception of more than 200 times will be in this line at the end of the last plus N that and the next time to receive data on the next array; the second data for the reception of the data length; the third with the fourth data were expressed in the receipt of the first and the last data of the assic code value.

uart_io_printf:Receiving data sent by microcontroller with serial to usb tool, we can see the continuous reception of the sent data.

IIC
I2C, a serial bus consisting of the data line SDA and the clock SCL, can send and receive data and is a half-duplex communication method. It has 4 related routines as shown in the table below.
| Routine | Function introduction |
|---|---|
| i2c_slave_dma | I2C sends and receives data via DMA |
| i2c_master_dma | I2C sends and receives data via DMA |
| i2c_slave_mapping | I2C sends and receives data via MCU |
| i2c_master_mapping | I2C sends and receives data via MCU |
Usage steps
i2c_slave_dma:
i2c_gpio_set(I2C_GPIO_SDA_A3,I2C_GPIO_SCL_A4);// Set the i2c data and clock pins
//slave device id 0x5C(write) 0x5D(read)
//i2c slave dma mode, master need send 3 byte sram address(0x40000~0x4ffff) on i2c bus in i2c reading/writing
i2c_slave_init(0x5C, I2C_SLAVE_DMA, NULL);//Set the write address and mode of the i2c slave
//write&read sram address in dma mode
reg_irq_mask |= FLD_IRQ_MIX_CMD_EN; //enable i2c irq
irq_enable(); //enable system irq
pBuf_slave_dma_for_write = (unsigned char*)(REG_BASE_ADDR+SLAVE_DMA_MODE_ADDR_WRITE);//Set the i2c write register
i2c_master_dma:
i2c_gpio_set(I2C_GPIO_SDA_A3,I2C_GPIO_SCL_A4);// Set the i2c data and clock pins
//slave device id 0x5C(write) 0x5D(read)
//i2c clock 200K, only master need set i2c clock
i2c_master_init(0x5c, (unsigned char)(CLOCK_SYS_CLOCK_HZ/(4*200000)) );//Set the write address, clock division factor and mode of the i2c master
i2c_write_series(SLAVE_DMA_MODE_ADDR_WRITE, 3, (unsigned char *)i2c_master_tx_buff, DBG_DATA_LEN);//i2c master write data
i2c_read_series(SLAVE_DMA_MODE_ADDR_READ, 3, (unsigned char *)i2c_master_rx_buff, DBG_DATA_LEN);//i2c master read data
i2c_slave_mapping:
i2c_gpio_set(I2C_GPIO_SDA_A3,I2C_GPIO_SCL_A4);// Set the i2c data and clock pins
i2c_slave_init(0x5C, I2C_SLAVE_MAP, (unsigned char *)slave_mapping_buff);//Initialize the slave's write address, mode, and write buffer
//slave_mapping_buff + 64 is master reading data buffer in mapping mode, put some data here for master read
memset(&slave_mapping_buff[64],0x55,DBG_DATA_LEN);//Initialize the slave's write buffer
reg_i2c_map_host_status = (FLD_HOST_CMD_IRQ | FLD_HOST_READ_IRQ); //Clear iic interrupt status bit
reg_irq_mask |= FLD_IRQ_HOST_CMD_EN; //Enable iic interrupt
irq_enable(); //Enable System Interrupt
i2c_master_mapping:
i2c_gpio_set(I2C_GPIO_SDA_A3,I2C_GPIO_SCL_A4);// Setting the i2c data and clock pins
//slave device id 0x5C(write) 0x5D(read)
//i2c clock 200K, only master need set i2c clock
i2c_master_init(0x5c, (unsigned char)(CLOCK_SYS_CLOCK_HZ/(4*200000)) );//Set the write address, clock division factor and mode of the i2c master
i2c_write_series(0, 0, (unsigned char*)i2c_master_tx_buff, DBG_DATA_LEN);//i2c master write data
i2c_read_series(0, 0, (unsigned char*)i2c_master_rx_buff, DBG_DATA_LEN);//i2c master read data
Test phenomena
Burn the program into the development board:
i2c_master_dma/i2c_slave_dma:Using the BDT tool, you can see that the data in the master's read/write buffer as well as the debug's buffer are as written in the program, and the number of times the slave is read/written as well as the data in the debug's buffer are also normal.


i2c_master_mapping/i2c_slave_mapping: The phenomenon seen with the BDT tool can be referred to the dma mode of i2c.
SPI
The Serial Peripheral Interface (Serial Peripheral Interface) abbreviated as SPI interface, is a synchronous serial peripheral interface that allows embedded processors and various peripheral devices to communicate, exchange data, etc. in a serial manner. It has 2 related routines in the following table.
| Routine | Function introduction |
|---|---|
| spi_master | SPI sends and receives data via MCU |
| spi_slave | SPI sends and receives data via MCU |
Usage steps
spi_master:
//spi clock 500K, only master need set spi clock
//div_clock. spi_clk = sys_clk/((div_clk+1)*2),mode select
spi_master_init((unsigned char)(CLOCK_SYS_CLOCK_HZ/(2*500000)-1),SPI_MODE0);//Set the spi clock rate and mode 0
spi_master_gpio_set(GPIO_PA4,GPIO_PD6,GPIO_PA2,GPIO_PA3);//Set spi related pins
spi_write(slaveRegAddr_WriteCMD, 4,(unsigned char*)spi_master_tx_buff, DBG_DATA_LEN,SPI_CS_PIN);//Write a certain length of data with the address and write commands
spi_read( slaveRegAddr_ReadCMD , 4,(unsigned char*)spi_master_rx_buff,DBG_DATA_LEN,SPI_CS_PIN);//Write a certain length of data with the address and write commands
spi_slave:
//spi clock 500K, only master need set spi clock
//div_clock. spi_clk = sys_clk/((div_clk+1)*2),mode select
spi_master_init((unsigned char)(CLOCK_SYS_CLOCK_HZ/(2*500000)-1),SPI_MODE0);//Set the spi clock rate and mode 0
spi_master_gpio_set(GPIO_PA4,GPIO_PD6,GPIO_PA2,GPIO_PA3);//Set spi related pins
reg_spi_slave_irq_status = (FLD_HOST_CMD_IRQ | FLD_HOST_READ_IRQ);//Clear spi interrupt status bits
reg_irq_mask |= FLD_IRQ_HOST_CMD_EN;//Enable spi interrupt flag bit
Test Phenomena
Burn the program into the development board:
Using the BDT tool you can see that the number of times the spi slave is being read is increasing and the master is reading the same data as it is writing.


USB
The USB, Universal Serial Bus, is an external bus standard used to standardize the connection and communication between computers and external devices, and is an interface technology used in the PC field. The related routine is usb_demo.
Usage steps
(1) Select the application mode in app.config.h.
(2) Select different application.c files for different applications based on the different application modes selected.
mouse_app.c:
//initialization
irq_enable();//Enable General Interrupt
usb_init_interrupt();//Enable USB manual interrupt (in auto interrupt mode, the USB device will be a USB printer device)
usb_set_pin_en();//Enable and pull-up 1.5kohm usb dp pin
//main_loop
usb_handle_irq();//usb interrupt handling
usbmouse_hid_report(USB_HID_MOUSE,(unsigned char*)mouse,4);//usb reporting information
keyboard_app.c:
//initialization
irq_enable();//Enable General Interrupt
usb_init_interrupt();//Enable USB manual interrupt (in auto interrupt mode, the USB device will be a USB printer device)
usb_set_pin_en();//Enable and pull-up 1.5kohm usb dp pin
//main_loop
usb_handle_irq();//usb interrupt handling
usbkb_hid_report_normal(0x00,kb_data);//Report key value information
mic_app.c:
//Select mode
#define AUDIO_AMIC_TO_USB 1//Analog microphones
#define AUDIO_DMIC_TO_USB 2//Digital microphones
#define AUDIO_CODEC_TO_USB 3//coded data
#define AUDIO_MIC_MODE AUDIO_CODEC_TO_USB
//initialization
irq_enable();//Enable general interrupt
usb_init_interrupt();//Enable USB manual interrupt (in auto interrupt mode, the USB device will be a USB printer device)
usb_set_pin_en();//Enable and pull-up 1.5kohm usb dp pin
reg_usb_ep6_buf_addr =0x80;
reg_usb_ep7_buf_addr =0xc0;
reg_usb_ep_max_size =(192 >> 3);
audio_config_mic_buf((unsigned short*)MicBuf,MIC_BUFFER_SIZE);//Set the buffer
#if (AUDIO_MIC_MODE==AUDIO_AMIC_TO_USB)
/*when test audio performance we need disable BIAS pin*/
gpio_set_func(AMIC_BIAS_PIN, AS_GPIO);
gpio_set_output_en(AMIC_BIAS_PIN, 1); //enable output
gpio_set_input_en(AMIC_BIAS_PIN ,0); //disable input
gpio_write(AMIC_BIAS_PIN, 1); //BIAS OUTPUT 1
audio_amic_init(AUDIO_8K);
#elif(AUDIO_MIC_MODE==AUDIO_DMIC_TO_USB)
gpio_set_func(GPIO_PA0, AS_DMIC);
gpio_set_func(GPIO_PA1, AS_DMIC);
gpio_set_input_en(GPIO_PA0, 1);
audio_dmic_init(AUDIO_32K);
#elif (AUDIO_MIC_MODE == AUDIO_CODEC_TO_USB)
audio_i2s_init();
#endif
audio_set_usb_output();
//main_loop
usb_handle_irq();//usb interrupt handling
if(usb_audio_mic_cnt)//Send data
{
#if(AUDIO_MIC_MODE==AUDIO_AMIC_TO_USB)
audio_tx_data_to_usb(AMIC, AUDIO_16K);
#elif(AUDIO_MIC_MODE==AUDIO_DMIC_TO_USB)
audio_tx_data_to_usb(DMIC, AUDIO_16K);
#elif(AUDIO_MIC_MODE==AUDIO_CODEC_TO_USB)
audio_tx_data_to_usb(I2S_IN, AUDIO_16K);
#endif
usb_audio_mic_cnt=0;
}
spk_app.c:
//initialization
irq_enable();//Enable general interrupt
usb_init_interrupt();//Enable USB manual interrupt (in auto interrupt mode, the USB device will be a USB printer device)
usb_set_pin_en();//Enable and pull-up 1.5kohm usb dp pin
reg_usb_ep6_buf_addr =0x80;//ep7 192bytes
reg_usb_ep7_buf_addr =0xc0;//ep7 64bytes
reg_usb_ep_max_size =192>>3;
audio_config_mic_buf((unsigned short*)MicBuf,MIC_BUFFER_SIZE);//set buffer
audio_usb_init(AUDIO_RATE_VAL);//set speed
audio_set_sdm_output(GPIO_PB6_PB7,USB_IN,AUDIO_RATE_VAL,1);//Set the sdm output
audio_set_usb_output();//Set the sdm output
//main_loop
usb_handle_irq();//interrupt processing
if(usb_audio_speaker_cnt)
{
audio_rx_data_from_usb();//Processing data from usb
usb_audio_speaker_cnt=0;
}
cdc_app.c:
//initialization
irq_enable();//Enable general interrupt
usb_init_interrupt();//Enable USB manual interrupt (in auto interrupt mode, the USB device will be a USB printer device)
usb_set_pin_en();//Enable and pull-up 1.5kohm usb dp pin
//main_loop
if(usb_cdc_data_len!=0)
{
usb_cdc_tx_data_to_host(usb_cdc_data,usb_cdc_data_len);//Send data to host
usb_cdc_data_len = 0;
}
Test phenomena
Burn the program into the development board:
mouse_app.c:
- Press to test.
- Ground and then unplug PD1 on the development board, it will execute the function usbmouse_hid_report(USB_HID_MOUSE,mouse,4). It can be observed that the right mouse button on the desktop is pressed and the mouse cursor moves down to the left.
- Release test.
- Perform the same operation on PD2 as PD1, the mouse array is cleared and the key is released.
keyboard_app.c:
- Press to test.
- Ground and then unplug PD1 on the development board, it will execute the function usbkb_hid_report_normal(0x10,kb_data). The effect of the lower key being pressed all the time will be observed.
- Release test.
- Perform the same operation on PD2 as PD1, special keys with kb_data array are cleared and keys are released.
mic_app.c:With the help of Audacity software, select Telink Audio16 for the microphone and PC speakers for the speakers; Device Mic will be tuned in and played on the PC speakers, if the recorded vocals can be played from the speakers without distortion, it means that the Mic is working properly.

spk_app.c:In Audacity software, microphone selection pc microphone, speaker selection Telink Audio16, through the device SDM output audio (3.5mm headphone jack), such as recorded vocals can be played out from the headphones without distortion, indicating that the speaker is working properly.

cdc_app.c:The first time host recognizes as a CDC device it needs to manually install the telink_usb_driver_cdc.inf file. It will be observed to return the content sent by the serial assistant.

Uart_fw_update
The serial port update firmware technique allows wired firmware updates using the Uart_fw_update routine, the relevant routines are listed in the table below:
| Routine | Function introduction |
|---|---|
| uart_fw_update_master | For moving the latest firmware |
| uart_fw_update_slave | Firmware program for receiving updates |
| uart_fw_update_slave2 | Up-to-date and re-receivable firmware program |
Usage steps
uart_fw_update_master:
//main_loop
if (FW_UPDATE_MasterTrig)//Trigger serial port to upgrade firmware and flash blue light
{
FW_UPDATE_MasterTrig = 0;
gpio_toggle(BLUE_LED_PIN);
WaitMs(100);
gpio_toggle(BLUE_LED_PIN);
WaitMs(100);
gpio_toggle(BLUE_LED_PIN);
WaitMs(100);
gpio_toggle(BLUE_LED_PIN);
WaitMs(100);
#if(BATT_CHECK_ENABLE)//Flash low voltage operation detection
if(!battery_power_check())
{
while(1)
{
gpio_toggle(RED_LED_PIN);
WaitMs(50);
}
}
#endif
FW_UPDATE_PHY_Init(FW_UPDATE_RxIrq);//Serial protocol initialization
FW_UPDATE_MasterInit(FW_UPDATE_MASTER_BIN_ADDR, FW_UPDATE_FW_VERSION);//Serial upgrade firmware initialization
while (1)
{
FW_UPDATE_MasterStart();//Start serial firmware upgrade
ev_process_timer();//Turn on timed processing
}
}
gpio_toggle(GREEN_LED_PIN);//The LED in IDLE state is green.
WaitMs(1000);
uart_fw_update_slave:
//main_loop
if (FW_UPDATE_SlaveTrig) {//Trigger serial port to upgrade firmware and flash blue light
FW_UPDATE_SlaveTrig = 0;
gpio_toggle(BLUE_LED_PIN);
WaitMs(100);
gpio_toggle(BLUE_LED_PIN);
WaitMs(100);
gpio_toggle(BLUE_LED_PIN);
WaitMs(100);
gpio_toggle(BLUE_LED_PIN);
WaitMs(100);
#if(BATT_CHECK_ENABLE)
if(!battery_power_check())//Flash low voltage operation detection
{
while(1)
{
gpio_toggle(RED_LED_PIN);
WaitMs(50);
}
}
#endif
FW_UPDATE_PHY_Init(FW_UPDATE_RxIrq);//Serial protocol initialization
flash_read_page(Flash_Addr,Flash_Buff_Len,(unsigned char *)Flash_Read_Buff);
if(Flash_Read_Buff[0]==0x4b)//Detect startup flag bit
{
FW_UPDATE_SlaveInit(FW_UPDATE_SLAVE_BIN_ADDR, FW_UPDATE_FW_VERSION);//Serial protocol initialization
}
else
{
FW_UPDATE_SlaveInit(0, FW_UPDATE_FW_VERSION);//Serial protocol initialization
}
while (1) {
FW_UPDATE_SlaveStart();//Start serial firmware upgrade
ev_process_timer();//Turn on timed processing
}
}
gpio_toggle(GREEN_LED_PIN);//The LED in IDLE state is green.
WaitMs(1000);
uart_fw_update_slave2:
Same as uart_fw_update_slave, except that the IDLE state is white while uart_fw_update_slave is green.
Test phenomena
(1) First of all, compile and burn the master, slave and firmware to be upgraded, to be upgraded firmware compiled also need to be configured to call bin_append.exe script to compile the native firmware to be upgraded, the role of which is to be upgraded firmware to be upgraded after the addition of a good calculation of TargetFwCRC results, and then output a new firmware as a real firmware to be upgraded, as shown in the figure:


(2) Burn UART_FW_UPDATE_MASTER.bin from SDK as master board and UART_FW_UPDATE _SLAVE.bin with two boards respectively. As the slave board. Burn the upgraded firmware UART_FW_UPDATE_SLAVE2 _NEW.bin at the address 0x20000 on the master side, you can select the burning address by clicking on the setting on Telink BDT.exe.
(3) Use DuPont cable to connect Master, Slave, to prepare for serial communication; UART_TX(GPIO_PA2) in demo, UART_RX(GPIO_PD6), connect PB1 of the slave board with the computer using the serial module, open the serial assistant, the bit rate is set to 10000, which is convenient for the subsequent view of the print information.
(4) Power up the Master and Slave boards, you can see the green LEDs on both boards are flashing slowly. First press the SW1 button on the Slave board to trigger the Slave to enter the upgrade ready state, wait for a few moments, wait for the blue LEDs on the Slave board to flash, indicating that the Slave has entered the upgrade state. Then press the SW1 button on the Master board to trigger the Master to enter the upgrade ready state, you will see that the blue LEDs on the Master board will also flash once, indicating that it has entered the upgrade state. Then the upgrade process begins, this time the Slave side of the upgrade process will print out the PktCRC of each packet of the new firmware through the serial port, and the result of this packet PktCRC will be used as the initial value of the next PktCRC calculation. The loop iterates until the last packet is received, and a final PktCRC check value will be obtained, which can be finished after a few moments. After the transmission of firmware is finished, the Slave side will re-cycle and iterate the CRC calculation for the new firmware, and compare the CRC result FwCRC with the final CRC result PktCRC in the transmission process and the TargetFwCRC appended in the bin file, and then the serial port will print out the three and the current startup address and the startup address of the upcoming restart. If the values are the same, it is considered that the upgrade transmission has been successful, and the firmware is complete, and the green LED on the Slave board blinks fast and then the white LED on the Master board blinks fast and then the green LED blinks slowly, then the update is successful. At this time, the Slave is running UART_FW_UPDATE_NEW_SLAVE.bin, so the white LED is blinking when it is ready. If after completion, the green LED on the Slave board is still flashing slowly and the red LED on the Master board is blinking, then it means that the OTA has failed and the OTA Slave board is still running UART_FW_UPDATE_NEW_SLAVE.bin.
(5) The serial port prints as shown after successful firmware transfer:

(6) After the firmware transfer is successful, repeat the firmware upgrade, the slave's upgrade firmware boot address will cycle between 0x00 and 0x40000/0x20000, whether it cycles between 0 and 0x20000 or 0x40000 depends on the macro control in the code to place the firmware to be upgraded at that boot address, the serial port prints as shown in the figure.

epd_driver_demo
The epd_driver_demo is the display driver routine for ink screen, users can refer to this routine to develop applications.
Usage steps
//1.main_loop processing
GUI_Clear(data_buf, 1);//screen cleaning
GUI_DispStr(data_buf, 6, 2, " ESL DEMO", 1);//Output the string "ESL DEMO" horizontally at coordinates (6,2)
GUI_DispPic(data_buf, 220, 0, telink_log, 48, 128);//Show telink icon at coordinates (220,0)
GUI_DispStr(data_buf, 6, 4, "IEEE ADDR", 1);//Output the "IEEE ADDR" string horizontally at coordinates (6,4)
GUI_DispStr(data_buf, 6, 6, "0X", 1);//Output the string "0X" horizontally at coordinates (6,6)
unsigned char prompt_str[20];
unsigned char ieee_addr[]={0,0,0,0};
GUI_BytesToHexStr(ieee_addr, sizeof(ieee_addr), prompt_str);//Convert a hexadecimal number to a string and store it in prompt_str
GUI_DispStr(data_buf, 6+strlen("0X")*GUI_FONT_WIDTH, 6, prompt_str, 1);//Display the string prompt_str after the string "0X"
GUI_DispPic(data_buf, 0, 8, bar_code, 200, 64);//Display QR code at coordinates (0,8)
EPD_Init();//Ink screen initialization
EPD_Display(data_buf, 4736);//Deposit a data_buf message of size 4736byte to be displayed on the screen
EPD_Close();//Turn off the ink screen
Testing and phenomena
Burn this code into the board of the ink screen to see the ink screen display the corresponding information.

ble_beacon_tx
The ble_beacon_tx routine is used to send ble broadcast beacon packets in a 2.4GHz project, and users can develop the application on demand.
Usage steps
//1.Initialization settings
rf_irq_disable(FLD_RF_IRQ_ALL);//Clear all interrupt flag bits
rf_irq_enable(FLD_RF_IRQ_TX);//Enable tx interrupt flag bit
irq_enable_type(FLD_IRQ_ZB_RT_EN); //Enable rf interrupt flag bit
irq_enable(); //Disable interrupt
ble_adv_init(37, RF_POWER);//Initialize communication frequency and communication strength of ble
flash_read_page(CAP_VALUE,1,&cap);//Read the cap value
if(cap!=0xff)
{
rf_update_internal_cap(cap);//Update cap value
}
//2.Interrupt handling
if (rf_irq_src & FLD_RF_IRQ_TX)//When sending is complete
{
rf_irq_clr_src(FLD_RF_IRQ_TX);//Clear the corresponding interrupt flag bit
tx_done_flag = 1;//The corresponding flag is set to 1.
tx_cnt++;//corresponding count
}
irq_clr_src2(FLD_IRQ_ALL);//Clear all interrupt flag bits
//3.main_loop processing
ble_adv_send(test_pdu, sizeof(test_pdu));//broadcast packet
while(0 == tx_done_flag);//When sending is complete
tx_done_flag = 0;
Tests and phenomena
Burn the code into the development board and open the connection software to search for the appropriate broadcast package.
![]()
OTA
The over-the-air firmware update technique allows firmware to be updated over-the-air using ota routines, the relevant routines are listed in the table below:
| Routine | Function |
|---|---|
| ota_master | For moving the latest firmware |
| ota_slave | Firmware program for receiving updates |
| ota_slave2 | Up-to-date and re-receivable firmware program |
Usage steps
ota_master:
//main_loop
if (OTA_MasterTrig)//Trigger ota
{
OTA_MasterTrig = 0;
#if(BATT_CHECK_ENABLE)//Flash low voltage operation detection
if(!battery_power_check())
{
while(1)
{
gpio_toggle(RED_LED_PIN);
WaitMs(50);
}
}
#endif
MAC_Init(OTA_MASTER_CHANNEL,
OTA_RxIrq,
OTA_RxTimeoutIrq,
OTA_RxTimeoutIrq);//Protocol Initialization
OTA_MasterInit(OTA_MASTER_BIN_ADDR, OTA_FW_VERSION);//ota initialization
gpio_write(BLUE_LED_PIN,1);//Flashing blue light before starting ota
WaitMs(80);
gpio_write(BLUE_LED_PIN,1);
WaitMs(80);
gpio_write(BLUE_LED_PIN,1);
WaitMs(80);
gpio_write(BLUE_LED_PIN,0);
while (1) {
OTA_MasterStart();//Start ota
}
}
gpio_toggle(GREEN_LED_PIN);//The IDLE state is green.
WaitMs(1000);
ota_slave:
//main_loop
if (OTA_SlaveTrig) {//Trigger ota
OTA_SlaveTrig = 0;
#if(BATT_CHECK_ENABLE)//Flash low voltage operation detection
if(!battery_power_check())
{
while(1)
{
gpio_toggle(RED_LED_PIN);
WaitMs(50);
}
}
#endif
MAC_Init(OTA_SLAVE_CHANNEL,
OTA_RxIrq,
OTA_RxTimeoutIrq,
OTA_RxTimeoutIrq);//Protocol Initialization
flash_read_page(Flash_Addr,Flash_Buff_Len,(unsigned char *)Flash_Read_Buff);
if(Flash_Read_Buff[0]==0x4b)//Detect startup flag bit
{
OTA_SlaveInit(OTA_SLAVE_BIN_ADDR, OTA_FW_VERSION);//ota initialization
}
else
{
OTA_SlaveInit(0, OTA_FW_VERSION);//ota initialization
}
gpio_write(BLUE_LED_PIN,1);//Flashing blue light before starting ota
WaitMs(80);
gpio_write(BLUE_LED_PIN,0);
WaitMs(80);
gpio_write(BLUE_LED_PIN,1);
WaitMs(80);
gpio_write(BLUE_LED_PIN,0);
while (1) {
OTA_SlaveStart();//Start OTA
}
}
gpio_toggle(GREEN_LED_PIN);//The IDLE state is green.
WaitMs(1000);
ota_slave2:
Same as ota_slave, except that the IDLE state is lit white instead of green.
Test and phenomena
(1) First of all, compile and burn the master, slave and firmware to be upgraded, after the firmware to be upgraded is compiled it needs to be configured to call bin_append.exe script to compile the native firmware to be upgraded, the role of which is to add a calculated TargetFwCRC results after the firmware to be upgraded, and then output a new firmware as a real firmware to be upgraded, as shown in the figure:


(2) Burn OTA_MASTER.bin and OTA_SLAVE.bin to two different development boards through the Burning EVK, where the one that burns OTA_MASTER.bin is OTA Master and the one that burns OTA_SLAVE.bin is OTA Slave.
(3) Burn the OTA to be upgraded firmware OTA_SLAVE2_NEW.bin to the space starting from 0x20000 in the OTA Master flash, select the burning address by clicking on the setting on Telink BDT.exe.
(4) Use DuPont cable to connect Master and Slave to prepare for serial communication; UART_TX(GPIO_PA2), UART_RX(GPIO_PD6) in the demo, connect PB1 of the OTA slave board with the computer using the serial module, open the serial assistant, and the bit rate is set to 10000 to facilitate the subsequent view of the print information.
(5) Power up the OTA Master board and OTA Slave board, you can see the green LED on the two boards flashing slowly. First press the SW1 button on the OTA Slave board to trigger the OTA Slave to enter the OTA mode, wait for a moment, wait for the blue LED on the OTA Slave board to blink once, indicating that the OTA Slave has entered the OTA mode. Then press the SW1 button on the OTA Master board to trigger the OTA Master to enter OTA mode, you will see that the blue LED on the OTA Master board will also flash once, indicating that it has entered the OTA mode. And then the OTA process will start, and at this time, the Slave side will be upgraded to the new firmware during the upgrade process to the new firmware in each sub-packet of the PktCRC will be printed out through the serial port. The PktCRC result will be used as the initial value for the next PktCRC calculation, and the cycle will be iterated until the last packet is received, and a final PktCRC check value will be obtained, which can be finished after a few moments. After the transfer of firmware is finished, the Slave side will re-cycle and iterate the CRC calculation for the new firmware, and compare the CRC result FwCRC with the final CRC result PktCRC in the transfer process and the TargetFwCRC appended in the bin file, and then the serial port will print out the three and the current startup address and the startup address of the upcoming restart. If the values are the same, it is considered that the upgrade transfer has been successful, and the white LED on the OTA Slave board flashes slowly, and the white LED on the OTA Master board blinks, which means that the OTA update is successful. At this time, the OTA Slave is running OTA_SLAVE2_NEW.bin, so it is ready to white LED slow flash. If after completion, the green LED on the OTA Slave board is still flashing slowly and the red LED on the OTA Master board is blinking, it means that the OTA has failed and the OTA Slave board is still running OTA_Slave.bin.
(6) The serial port prints as shown after successful firmware transfer:

(7) After the firmware transfer is successful and then repeat the firmware upgrade, the slave's upgrade firmware boot address will cycle between 0x00 and 0x40000/0x20000, whether it cycles between 0 and 0x20000 or 0x40000 depends on the macro control in the code to place the firmware to be upgraded at that boot address, the serial port prints as shown in the following figure.

FSK Link Layer
Packet format
Flexible variable-length packet format (TL721x/TL321x).

Traditional 9-bit header variable-length packet format (TLSR8xxx/B91/TL721x/TL321x).

Traditional fixed-length packet format (TLSR8xxx/B91/TL721x/TL321x).

Traditional BLE variable-length packet format (TLSR8xxx/B91).

Preamble
The preamble format is 0x55 or 0xAA. The Sync Word, which immediately follows the preamble, determines which of the 2 preamble options is selected by hardware: the hardware layer selects the preamble format based on the first transmitted bit of Sync Word, such that the last bit of preamble is the opposite polarity from the first bit of Sync Word. The number of octets of preamble is programmable with a range from 0 to 31 octets.
Note
- 31 octet is the maximum allowable length, the extra length is for debug purpose, typical application only requires 1-8 octet.
Sync Word
The Sync Word is the second packet element and in a network of associated devices, all devices generally share a common Sync Word. The number of octets of Sync Word is programmable through a dedicated register, with a usually range from 3 to 5 octets. The content of Sync Word is also configured through dedicated registers rather than the TX Packet Buffer. Other chips support searching for more than one Sync Word at the same time, while B85 only supports searching for one Sync Word. In addition, there is a register to set the Sync Word matching threshold. That means during the synchronization process of PHY's Sync Word in RX mode, once the number of matched bits of the demodulated data reaches to the pre-set threshold, the baseband will determine an expected Sync Word has been synchronized with. In other words, the match threshold determines the number of bit errors that can be tolerated by the PHY during Sync Word correlation. Note that the received Sync Word is neither stored in the RX Packet Buffer. There is no dedicated status bit or interrupt signal used as the indication of the Sync Word matching for the TLSR8258 while the RX DONE irq may be taken as an alternative for that. The RX DONE irq occurs once the payload of a pre-set length has been received after a successful Sync Word matching regardless of the CRC checking result.
Note
- When the sync word match threshold is less than the length of the sync word (that is, allowing for a bit error in the sync word), CRC computing uses payload and “correct” sync word rather than actually received sync word that although passes threshold but may still has error bit. For example, for sync word of 0x11223344, received sync word could be 0x00223344 that passes threshold. CRC computing will use 0x11223344, instead 0x00223344. That means even the sync word match threshold is less than the length of the sync word the CRC still can recognize the possible bit error in the payload field.
Header
The header in the traditional 9-bit header variable-length packet format is shown below. The header is 9-bit in length and contains a 6-bit length subfield indicating the length of the payload field.

Payload Length: indicating the length of payload in octet, the maximum length is 2^6-1 = 63 byte.
PID: reserved for Telink Primary TX/RX Link layer, meaningless for Telink Generic FSK Link Layer, the content of this field can be controlled by the software.
NO_ACK: reserved for Telink Primary TX/RX Link layer, meaningless for Telink Generic FSK Link Layer, the content of this field can be controlled by the software.
The header in the traditional BLE variable-length packet format is shown below. It consists of a two-byte header, with header0 being user-defined and header1 representing the payload length.

The header in the flexible variable-length packet format is shown below.

H0, H1: Controlled by the MASK | MATCH | SZ registers. The meaning of the values is determined by customer software. In the TPLL protocol, the PID and NO_ACK bits are placed in these fields.
-
MASK: Specifies that the corresponding bits in the MATCH register must match for the packet to be processed; otherwise, further processing is skipped.
-
MATCH: Specifies the content in the PACKET format that needs to be matched.
-
SZ: Specifies the length of the H0/H1 fields in octets, with a range of 0 to 16 bits (maximum of 65535).
Length: Controlled by the SZ | ORD | ADJ registers and represents the length of the payload.
-
SZ: Specifies the length of the LENGTH field in octets, with a range of 0 to 16 bits (maximum of 65535).
-
ORD: Specifies the bit order of the LENGTH field.
-
ADJ: Specifies how the LENGTH is calculated: PAYLOAD_LEN + ADJ (offset) [−31 <= LENGTH_ADJ <= 31].
Note
- The combined length of H0, H1, and LENGTH must be aligned to 8 bits, so this packet format is not compatible with the 9-bit header packet format.
Payload
The Telink link layer supports three packet formats: the flexible variable-length packet format (currently only available on TL721x/TL321x), the traditional 9-bit header variable-length packet format, and the traditional fixed-length packet format.
In the traditional fixed-length packet structure, the payload follows directly after the Sync Word, while in the variable-length payload packets, it comes after the Header field. During transmission, the payload is sent following the LSByte and MSBit-first rule. Similarly, during reception, the payload is received according to the LSByte and MSBit-first rule. For both TX and RX, the payload is exchanged between the RF transceiver and the link layer via the DMA module. For TX, a dedicated TX Packet Buffer (an array of unsigned char elements) must be defined in SRAM with the following formats:

Before starting transmission at the PHY layer, the DMA size, header, and payload must be written into the TX Packet Buffer. In the TLSR9xxx chips, the DMA size in the TX buffer needs to be calculated and filled using a specific interface.
For RX, a dedicated RX Packet Buffer (an array of unsigned char elements) must also be defined in SRAM with the following formats:




Note that for fixed-length payload packets, before enabling PHY reception, the expected payload length must be preset through a specific register. Once the Sync Word is successfully matched, the link layer controller will load the preset payload length along with the RX information into the RX Packet Buffer, and an RX packet IRQ will be generated immediately. For variable-length payload packets, the link layer controller determines the payload length based on the payload length subfield in the Header.
CRC
CRC is the error-checking mechanism within the packet, following the payload. Different chips have different CRC configurations, as shown in the table below:
| Chip | Calculation Range | Length Range | Initial Value | Polynomial | Bit Order |
|---|---|---|---|---|---|
| TLSR8xxx /B91 | sync word ~ payload (BLE format only includes header + payload) | 0~3 bytes | Fixed 0xff / 0xffff (BLE format can be customized) | Fixed (1 byte: X^8 + X^2 + X + 1), (2 bytes: X^16 + X^12 + X^5 + 1) | Not adjustable |
| TL721x/TL321x | sync word ~ payload (starting position selectable, BLE format only includes header + payload) | 0~4 bytes | Customizable | Customizable | Adjustable |
RSSI
There are two types of RSSI at the Telink link layer. One is the RSSI of an RX packet, which is stored in the RX Packet Buffer once the packet has been received, and has a value equal to the instantaneous value of the RSSI when the Sync Word has been recognized successfully, plus 110. The other is the real-time RSSI of a given channel. If the transceiver is in the RX state on a given channel, the real-time power measurement for that channel can be read from a specific register, and that value minus 110 is the real-time RSSI for that channel. The real-time RSSI is usually used to determine whether a given channel is busy or not.
Data Rate
The Telink Generic FSK Link Layer controller provides 4 bitrate options, 2Mbps, 1Mbps, 500Kbps and 250Kbps, which is programmable through a specific register.
Timebase
A 32-bit multi-purpose System Timer running at the rate of 16MHz (24MHz for TLSR9xxx) can be employed by the Telink Generic FSK Link Layer controller to schedule all its TX and RX activities, except during intervals of low power mode(suspend/deep sleep). The clock source is a high-precision 24MHz external crystal oscillator. The 32-bit System Timer running at 16 MHz rolls over (wraps around) approximately every 268.4 seconds. The System Timer tick count value can be read and changed with corresponding API at any time.
Two 32-bit timer-compare registers are provided, T1_CMP and T2_CMP in the chips of B85 series. When System Timer reaches the T1_CMP value, the link layer controller can automatically launch a transceiver operation. Similarly, once System Timer reaches the T2_CMP value, the link layer controller can automatically stop a transceiver operation. Four automatic transceiver operation mode, implemented by respective hardware state machines, are provided for the user’s convenience: Single TX, Single RX, Single TX to RX and Single RX to TX.
Telink Generic FSK Link Layer (genfsk_ll)
Single TX
In this mode, only the T1_CMP is involved in, that is, when System Timer reaches the T1_CMP value, the prepared packets are transmitted. A TX DONE irq occurs once the transmission is completed.

Single RX
In this mode, both the T1_CMP and T2_CMP are involved in. When System Timer reaches the T1_CMP value, the transceiver enters RX state and starts to search an expected Sync Word. If the sync word is not successfully correlated until the System Timer reaches the T2_CMP value, then a RX FIRST TIMEOUT irq occurs and the transceiver returns to IDLE state. If a packet is received before the System Timer reaches the T2_CMP value, a RX DONE irq occurs and the transceiver returns to IDLE state. It is also possible to disable the T2_CMP, which means the transceiver will always remain in the RX state until a packet is received.

Single TX to RX
In this mode, the two compare timers are involved in. When System Timer reaches the T1_CMP value, it starts sending prepared packets. After sending, a TX DONE irq occurs and the transceiver waits for a short while(this waiting duration is programmable) then turns into RX state. If the sync word is not successfully correlated until the System Timer reaches the T2_CMP value, then a RX TIMEOUT irq occurs and the transceiver returns to IDLE state. If a packet is received before the T2_CMP match, a RX DONE irq occurs and the transceiver returns to IDLE state. This automatic mode applies for the scenario where an ACK or a response packet is desired after transmitting a packet.

Single RX to TX
In this mode, the transceiver firstly enters RX state on the T1_CMP match and waits for a packet arriving. If no packet is received before System Timer reaches the T2_CMP value, a RX FIRST TIMEOUT irq occurs and the transceiver resumes IDLE state. If a packet arrives before the T2_CMP match, a RX DONE irq occurs and the transceiver waits for a short while (this waiting duration is programmable) then turns into TX state and starts the transmission of a prepared packet. This automatic mode applies for the case where an ACK or a response packet needs to be transmitted after receiving a packet.

Interrupt
There are totally six relevant interrupt signals for the Telink Generic FSK Link Layer Controller: TX DONE, RX DONE, RX FIRST TIMEOUT, RX TIMEOUT, PKT_MATCH, PKT_UNMATCH.
TX DONE
Every time the transmission of a packet is finished a TX DONE irq occurs.
RX DONE
A RX DONE irq will occur once a packet is received regardless of the CRC checking result, meaning that as long as the Sync Word matches then a RX DONE irq will occur after the whole packet is received.
RX FIRST TIMEOUT
This irq occurs in the two automatic modes Single RX and Single RX to TX as described above when the System Timer reaches the T2_CMP value.
RX TIMEOUT
A RX TIMEOUT irq only occurs in the Single TX to RX mode when the System Timer reaches the T2_CMP value.
PKT_MATCH
As long as any matching field is found between the start and end of the match range, a PKT_MATCH interrupt will be triggered.
PKT_UNMATCH
If no matching field is found between the start and end of the match range, or if there are no fields to match (i.e., the MASK register is set to 0), a PKT_UNMATCH interrupt will be triggered.
Telink Proprietary Link Layer (tpll)
PRX
In this mode, use the chips of TLSR8xxx as examples, it is assumed that all RF-related configurations have been completed before entering the PRX RX mode, and RF implementation reception is triggered by writing "0x84" to register "0xf00".
First, the state machine enters the RX Settle phase and waits for the PLL of the RF RX Settle phase to stabilize (at least 85us). The duration (in us) of the "RX Settle" can be configured via registers 0xf04\<7:0> and 0xf05\<3:0>. After the RX Settle phase is over, the state machine enters the actual RX phase.
The actual RX phase is the actual phase of packet reception and its duration is configurable.
-
When register 0xf03<2> is set to 1'b1, the duration of the actual RX phase is equal to the RX Time minus the duration of the RX Settle phase, which can be configured through registers 0xf0a and 0xf0b (in us).
-
When register 0xf03<2> is cleared, the state machine stays in the actual RX phase until a packet is received for the next phase.
When a packet is received in the RX phase, the state machine determines whether the packet is a duplicate packet.
-
If it is a duplicate packet, it is not stored in the associated register.
-
If it is not a duplicate packet, the RF hardware parses the "NO_ACK" bit in the Packet Control field of the packet's Header and performs an XOR operation on this bit and the local 0xf15\<5> bit.
If the result of the XOR operation is 1, it is assumed that the other party does not need an ACK and the state machine will return to the RX Settle phase to wait to receive the next packet.
If the result of the XOR operation is 0, the other party is considered to require an ACK, and the state machine sends an ACK packet to the other party.
When the state machine needs to respond an ACK, it first waits for the duration of the "TX wait" (in us), which is configurable via registers 0xf0e\<7:0> and 0xf0f\<3:0>. The state machine then enters the TX Settle phase in order to wait for the PLL of the TX phase to stabilize (a minimum of 112.5 us is required).The duration of the TX Settle phase is configurable via registers 0xf04\<7:0> and 0xf05\<3:0>.
After the TX Settle phase ends, the state machine enters the Actual TX phase to send the ACK. The duration of the Actual TX phase is determined by the length of the ACK packet.
After sending the ACK packet, the state machine waits for the duration of the "RX wait" (in us), which can be configured via registers 0xf06\<7:0> and 0xf07\<3:0>, and then returns to the RX Settle phase to receive the next packet.
The following figure shows the PRX timing sequence for the state machine.

PTX
In this mode, it is assumed that all configurations have been completed and the TX buffer has been filled before entering PTX TX mode, and then the RF implementation transmission is triggered by writing "0x83" to register "0xf00".
First, the state machine waits for the TX Settle phase configured through registers 0xf04\<7:0> and 0xf05\<3:0> in order for the PLL of the RF TX Settle phase to stabilize (which takes at least 112.5us) (in us).
The state machine then enters the actual TX phase where the RF will send packets over the air. The duration of the packet transmission is determined by the packet length.
After the end of the packet transmission, first the RF state machine calculates the XOR result of registers 0xf15<5> and 0x404<6>, and then determines whether to wait for the other party's ACK accordingly.
-
When the XOR result is 1, the state machine will assume that there is no need to wait for an ACK from the other party, thus ending the current packet transmission and waiting for the next transmission to be triggered.
-
When the XOR result is 0, the state machine will assume that it needs to wait for an ACK from the other side, it will wait for the "RX Wait" time (in us) configured by registers 0xf06\<7:0> and 0xf07\<3:0>, and then it will switch to the RX mode and wait for the PRX to switch from RX to TX and respond with an ACK. ACK.
The PTX will remain in RX mode (in us) for the "RX Time" configured via registers 0xf0a<7:0> and 0xf0b<3:0>, waiting for the other side to respond to the ACK.
Note
- During the "RX Settle" period of PRX mode (configured via 0xf0c\<7:0> and 0xf0d\<11:8> in us), the state machine stays in the RX Settle state and waits for the RF PLL to become stabilized (a minimum of 85us is required). During the RX Settle phase, it is not possible to receive any upcoming packets because the RF RX function is not actually enabled, i.e., the effective receive period during the RX time is the actual RX phase.
If an ACK packet is received during the actual RX phase, the transmission of the current packet is completed.
If no ACK is received from the other party when the RX time expires, PTX will try to retransmit. First it determines whether the maximum number of retries has been reached: if not, it executes "retry cnt++". Then it waits for the "ARD" duration (automatic retransmission delay in us) configured via 0xf10\<7:0> and 0xf11\<3:0>.
The state machine then waits for the duration of "TX Settle", enters TX mode, sends the packet, waits for the duration of "RX Wait", enters RX, and then waits for an ACK. The state machine repeats this process until the number of retries reaches the maximum value.
The following figure shows the PTX timing sequence for the state machine.

Interrupt
For PTX side, TX interrupt is triggered every time a packet is sent. Users can check the flag bit to determine whether the packet is sent successfully or not.
If the PTX is ACK enabled, the following two conditions apply when an ACK is received from the PRX:
-
If the PRX responds with a null ACK, the PTX will only trigger the TX_DS interrupt.
-
If the PRX responds with an ACK with payload, the PTX will generate RX_DR and TX_DS interrupts.
If the PTX does not receive any ACKs for the specified duration, it will attempt to retransmit the same packet until either an ACK is received or the number of retries reaches the preset threshold. If the PTX does not receive any ACK after the number of retries reaches the threshold, it generates a RETRY_HIT interrupt.
For the PRX side, the RX interrupt is generated every time a packet is received, but the RX_DR interrupt is generated only when the received packet has the correct CRC result and Payload_Len is not equal to zero. In case of ACK mode with Payload, TX_DS interrupt is generated from the reception of the second packet.
If the PRX is ACK enabled, TX and RX interrupts are generated in a single transmission, but not RX_DR interrupts. The reasons may be as follows:
-
A packet with an incorrect CRC result is received.
-
A null packet with Payload_Len equal to 0 is received.
-
A packet with the same PID as the previous packet is received.
Note
- Both PTX and PRX generate the INVALID_PID interrupt when the received packet PID is neither local_pid nor local_pid + 1. PTX\PRX generates the CRC_2 interrupt when it receives two consecutive packets with incorrect CRC results.
- The PRX can be configured to receive packets continuously or only for a specified duration. If the PRX is configured to receive packets for a specific duration and no packets are received for that duration, an RX_TIMEOUT interrupt is generated.
Transmission Flow
Normal communication process for ACK with payload

Normal communication process for empty ACK

Single communication with a lost packet

Single communication with a lost ACK packet

Multiple communication process with max retransmission count
In this scenario, the preset ARC is 2. If the PTX does not receive any ACKs after the threshold number of retries (ARC = 2), a RETRY_HIT interrupt is generated.

Classic communication process
During communication, the transceiver will appear to generate TX and RX interrupts but not RX_DR interrupts. The reason for this phenomenon is non-unique and is mainly caused by the following problems:
-
A packet with an incorrect CRC result was received.
-
Received packet with no payload (payload_len = 0).
-
The received packet PID is the same as the local PID.

Note
- Typically, the Telink PTX only sends consecutive packets with the same PID if it loses an ACK packet.
Example of tpll packet format
PTX TX buffer packet format(payload len=8)
For example:
00000000: 09 00 00 00 08 01 02 03 04 05 06 07 08 00 00 00
00000010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00000020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00000030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Description of the packet data:
-
tx_packet[0]: tx packet length (payload length +1)
-
tx_packet[1]: 0x00
-
tx_packet[2]: 0x00
-
tx_packet[3]: 0x00
-
tx_packet[4]: payload length
-
tx_packet[5]: data[0]
-
…
-
tx_packet[5 + length -1 ]: data[length -1]
PRX RX buffer packet format(payload len=8)
For example:
00000000: 13 00 00 00 08 01 02 03 04 05 06 07 08 81 66 12
00000010: cb 7d 14 18 11 58 10 10 cc cf f3 0c 03 c0 f3 30
00000020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00000030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Description of the packet data:
-
rx_packet[0]: 0x13 (Total number of bytes received and transmitted to RX DMA)
-
rx_packet[1]: 0x00
-
rx_packet[2]: 0x00
-
rx_packet[3]: 0x00
-
rx_packet[4]: bit[5:0] payload length, bit[7:6] pid
-
rx_packet[5]: data[0]
-
rx_packet[6]: data[1]
-
rx_packet[7]: data[2]
-
…
-
rx_packet[5 + length -1 ]: data[length -1]
-
rx_packet[5 + length]: CRC(0)
-
rx_packet[5 + length + 1]: CRC(1)
-
rx_packet[5 + length + 2]: TimeStamp_Byte[0]
-
rx_packet[5 + length + 3]: TimeStamp_Byte[1]
-
rx_packet[5 + length + 4]: TimeStamp_Byte[2]
-
rx_packet[5 + length + 5]: TimeStamp_Byte[4]
-
rx_packet[5 + length + 8]: PACKET RSSI