泰凌2.4GHz私有协议SDK开发手册
SDK概述
该2.4G SDK适用于泰凌TLSR8208, TLSR8373, TLSR825x,TLSR8359, TLSR827x,TLSR8355, TLSR921x, TLSR951x系列芯片,用于2.4G私有协议相关工程应用程序开发,客户可在2.4G SDK的基础上参考技术手册开发自己的应用程序。
硬件平台
2.4GHz proprietary SDK目前针对不同的硬件平台有四个基础SDK。
| 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 |
不同的芯片其外设功能会有差异,但私有协议原理机制大同小异。
软件开发环境
软件工具:
(1) 集成开发环境
TLSR8 Chips: Telink IDE for TC32

TLSR9 Chips: Telink RDS IDE for RISC-V

(2) 下载调试工具
Telink Burning and Debugging Tools.

软件开发包:
选择需要芯片类型的sdk。
目录结构
目录结构如下图(以B87为例):

相比boot、link、common、drivers、vendor、div_mod.S为driver SDK的基本部分。2.4GHz还有ble_adv_tx、docs、epd、fw_update、genfsk_ll、ota、script、tpll、tpsll。
以下针对driver SDK类似部分会做简单介绍,更详细的介绍请参考驱动开发手册。
boot:
该文件夹下是启动文件。

link:
链接文件。

div_mod.S:
div_mod.S文件是用于处理除法、取余的汇编运算。

common:
该文件夹下是一些跨文件的中间文件,其中sdk_version用来将SDK的版本信息存入flash段中。

vendor:
该文件夹下是所有模块的例程代码。
ble_adv_tx:
该文件夹下是BLE的广播tx接口,对应的ble_beacon_tx例程。

docs:
该文件夹下放置的是相关的handbook文档。
epd:
该文件夹为对应epd_driver_demo例程接口,该功能为操作ESL电子标签的墨水屏幕例程。

fw_update:
该文件夹为对应uart_fw_update例程接口,用于串口通信升级固件。


genfsk_ll:
该文件夹为Genfsk_ll接口,用于Genfsk_ll通信例程。


ota:
该文件夹为对应ota例程接口,用于无线通信升级固件。


script:
该文件用于放置文本文件,如给bin文件末添加crc校准信息用于固件升级时检查固件升级是否成功的文本文件。

tpll:
该文件夹为TPLL接口,用作TPLL通信例程。


tpsll:
该文件夹为TPSLL (Telink proprietary stack link layer)接口,用作TPSLL通信例程。


2.4GHz私有协议代码介绍
2.4GHz私有协议代码是2.4GHz私有SDK的主要构成部分,协议详解可参考相关章节,由于后续芯片更新出现了灵活包格式,导致相关例程有部分改动,以B87、TL321x为例,本节将参考例程,主要介绍其功能用法、测试现象以便用户轻松上手使用。根据包格式不同,2.4GHz私有协议代码分为如下表三类:
| 类型 | 模式 |
|---|---|
| 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 |
其中tpll、genfsk_ll是2.4GHz的私有包格式,tpsll是Bluetooth LE的包格式。
TPLL (Telink Primary Link Layer)
2.4GHz私有协议tpll的相关例程如下表:
| 例程 | 功能简介 |
|---|---|
| tpll_ptx | Sending packets through the ptx state machine and selecting whether an answer packet is required or not |
| tpll_prx | Receive packets and select whether to send an answer packet via the prx state machine |
| tpll_fast_ptx | Quickly get ready to send packets and select whether an answer packet is needed via the ptx state machine |
| tpll_fast_prx | Quickly get ready to receive packets and choose whether to send an answer packet via the prx state machine |
tpll_ptx/prx
B87用法步骤:
- tpll_ptx:
//1.初始化配置
//设置数据传输速率
TPLL_Init(TPLL_BITRATE_2MBPS);
//设置信号强度
TPLL_(TPLL_RF_POWER_0DBM);
//设置同步字长度
TPLL_SetAddressWidth(ADDRESS_WIDTH_5BYTES);
//关闭所有通道
TPLL_ClosePipe(TPLL_PIPE_ALL);
//设置传输通道及同步字
TPLL_SetAddress(TPLL_PIPE0, tx_address);
//打开通道
TPLL_OpenPipe(TPLL_PIPE0);
//打开tx通道
TPLL_SetTXPipe(TPLL_PIPE0);
//设置ptx模式
TPLL_ModeSet(TPLL_MODE_PTX);
//设置通信频点
TPLL_SetRFChannel(chn);
//设置主动重发次数及延时重发时间,设置为0代表关闭主动重发
TPLL_SetAutoRetry(0,150);
//设置rx超时时间,当传输速率为250kbps时,rx的超时时间需要设置为1000us才能合理通信
TPLL_RxTimeoutSet(500);
//设置rx settle时间
TPLL_RxSettleSet(80);
//设置tx settle时间
TPLL_TxSettleSet(149);
//这个延时是不必要的
WaitUs(150);
//清除所有中断源
irq_clr_src();
//清除所有中断标志位
rf_irq_clr_src(FLD_RF_IRQ_ALL);
//失能rf通信所有中断
rf_irq_disable(FLD_RF_IRQ_ALL);
//使能rf通信中断
irq_enable_type(FLD_IRQ_ZB_RT_EN);
//使能特定rf通信中断
rf_irq_enable(FLD_RF_IRQ_TX|FLD_RF_IRQ_TX_DS|FLD_RF_IRQ_RETRY_HIT|FLD_RF_IRQ_RX_DR);
//使能中断
irq_enable();
//写要发送的数据
tmp = TPLL_WriteTxPayload(PTX_CHANNEL, tx_data, tx_len);
//设置前导码为8byte
TPLL_Preamble_Set(8);
//开始ptx
TPLL_PTXTrig();
//2.中断处理
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;
//更新发送fifo指针,发送新包
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处理发送
//收到非空包或达到最大重发次数
if (ds_flag ||maxretry_flag)
{
if(ds_flag)
gpio_toggle(DEBUG_PIN);
ds_flag = 0;
maxretry_flag = 0;
WaitMs(500);
//写要发送的数据
tmp = TPLL_WriteTxPayload(PTX_CHANNEL, tx_data, tx_len);
if(tmp)
{
//写成功后开始ptx
TPLL_PTXTrig();
}
}
- tpll_prx:
//1.初始化配置
//设置数据传输速率
TPLL_Init(TPLL_BITRATE_2MBPS);
//设置信号强度
TPLL_SetOutputPower(TPLL_RF_POWER_0DBM);
//设置同步字长度
TPLL_SetAddressWidth(ADDRESS_WIDTH_5BYTES);
//关闭所有通道
TPLL_ClosePipe(TPLL_PIPE_ALL);
//设置传输通道及同步字
TPLL_SetAddress(TPLL_PIPE0, tx_address);
//打开通道
TPLL_OpenPipe(TPLL_PIPE0);
//设置prx模式
TPLL_ModeSet(TPLL_MODE_PRX);
//设置通信频点
TPLL_SetRFChannel(chn);
//设置rx settle时间
TPLL_RxSettleSet(80);
//设置tx settle时间
TPLL_TxSettleSet(149);
//清除所有中断源
irq_clr_src();
//清除所有中断标志位
rf_irq_clr_src(FLD_RF_IRQ_ALL);
//失能rf通信所有中断
rf_irq_disable(FLD_RF_IRQ_ALL);
//使能rf通信中断
irq_enable_type(FLD_IRQ_ZB_RT_EN);
//使能特定rf通信中断
rf_irq_enable(FLD_RF_IRQ_TX|FLD_RF_IRQ_TX_DS|FLD_RF_IRQ_RX_DR);
//使能中断
irq_enable();
//开始prx
TPLL_PRXTrig();
Timestamp_value = clock_time();
//2.中断处理
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处理发送
//当收到正确包时
if(1 == rx_flag)
{
rx_flag = 0;
//获取payload长度与pipe值
length_pip_ret = TPLL_ReadRxPayload(&rx_data);
//计算接收时间
Rx_interval_us = (TPLL_GetTimestamp() - Timestamp_value) >> 4;
//获取同步字成功相关时的瞬时时钟
Timestamp_value = TPLL_GetTimestamp();
//RSSI(Received Signal Strength Indicator)获取接收信号强度
Rssi_value = TPLL_GetRxRssiValue();
//如果tx fifo为空
while(!TPLL_TxFifoEmpty(0));
//写应答数据包
TPLL_WriteAckPayload(TPLL_PIPE0, ack_payload, ack_payload_length);
}
测试现象:
在两块板子上分别烧录ptx与prx的例程进行通信。用BDT工具可以看到prx跟ptx的各个中断计数值,以及收到的包值。正常情况下rx_dr\rx_ds\tx的中断数应该很接近。


TL321x用法步骤:
通用结构体配置:
//配置crc结构体参数
TPLL_CrcConfig_t TPLL_CrcConfig = {
.init_value = 0xffffffff,
.poly = 0x00001021,
.xor_out = 0,
.byte_order = 1,
.start_cal_pos = 0,
.len = 2,
};
//如果打开包过滤功能,需要设置包过滤的字段范围,想要匹配的数据字段以及匹配阈值
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
};
//如果选择灵活包格式,需要设置header内容,包括h0,h1,length以及pid ,no ack位置与初值,注意h0,h1,length size单位为bit且之合必须是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.初始化配置
//选择包格式
TPLL_SetFormatMode(TPLL_MODE_GENERIC_FORMAT);
//设置数据传输速率
TPLL_SetBitrate(TPLL_BITRATE_2MBPS);
//设置信号强度
TPLL_SetOutputPower(RF_POWER_INDEX_N0p07dBm);
//设置同步字长度
TPLL_SetAddressWidth(ADDRESS_WIDTH_5BYTES);
//设置包过滤条件
#if(PRI_FLT_MODE_EN)
TPLL_PktFilter(TPLL_PktFlt);
#endif
//关闭所有通道
TPLL_ClosePipe(TPLL_PIPE_ALL);
//设置传输通道及同步字
TPLL_SetAddress(TPLL_PIPE0, tx_address);
//打开通道
TPLL_OpenPipe(TPLL_PIPE0);
//打开tx通道
TPLL_SetTXPipe(TPLL_PIPE0);
//设置dma tx/rxbuffer
TPLL_DmaInit(rx_buf,ptx_buffer);
//设置ptx模式
TPLL_ModeSet(TPLL_MODE_PTX);
//设置通信频点
TPLL_SetRFChannel(chn);
//设置主动重发次数及延时重发时间,设置为0代表关闭主动重发
TPLL_SetAutoRetry(0,150);
//设置rx超时时间,当传输速率为250kbps时,rx的超时时间需要设置为1000us才能合理通信
TPLL_RxTimeoutSet(500);
//设置rx settle时间
TPLL_RxSettleSet(80);
//设置tx settle时间
TPLL_TxSettleSet(149);
//设置preamble长度
TPLL_Preamble_Set(8);
//设置crc
TPLL_CrcSet(TPLL_CrcConfig);
//这个延时是不必要的
WaitUs(150);
//使能中断
core_interrupt_enable()
//使能rf中断
plic_interrupt_enable(IRQ_ZB_RT);
//清除所有中断标志位
rf_clr_irq_mask(FLD_RF_IRQ_ALL);
//清除所有中断标志位
rf_irq_clr_src(FLD_RF_IRQ_ALL);
//使能特定rf通信中断
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);
//写要发送的数据
TPLL_WriteTxPayload(PTX_PIPE, ptx_buffer, (unsigned char *)tx_data, 16);
//开始ptx
TPLL_PTXTrig();
//2.中断处理
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处理发送
//收到非空包或达到最大重发次数
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]++;
//写要发送的数据
tmp = TPLL_WriteTxPayload(PTX_PIPE, ptx_buffer, (unsigned char *)tx_data, TX_PAYLOAD_LEN);
if(!tmp)
{
//写成功后开始ptx
TPLL_PTXTrig();
}
}
- tpll_prx:
//1.初始化配置
//选择包格式
TPLL_SetFormatMode(TPLL_MODE_GENERIC_FORMAT);
//设置数据传输速率
TPLL_SetBitrate(TPLL_BITRATE_2MBPS);
//设置信号强度
TPLL_SetOutputPower(RF_POWER_N0p07dBm);
//设置同步字长度
TPLL_SetAddressWidth(ADDRESS_WIDTH_5BYTES);
//设置包过滤条件
#if(PRI_FLT_MODE_EN)
TPLL_PktFilter(TPLL_PktFlt);
#endif
//关闭所有通道
TPLL_ClosePipe(TPLL_PIPE_ALL);
//设置传输通道及同步字
TPLL_SetAddress(PRX_PIPE,rx_address);
//打开通道
TPLL_OpenPipe(TPLL_PIPE0);
//设置dma tx/rxbuffer
TPLL_DmaInit(rx_buf,ptx_buffer);
//设置prx模式
TPLL_ModeSet(TPLL_MODE_PRX);
//设置通信频点
TPLL_SetRFChannel(4);
//设置tx settle时间
TPLL_TxSettleSet(149);
//设置rx settle时间
TPLL_RxSettleSet(85);
//设置preamble长度
TPLL_Preamble_Set(8);
//设置crc
TPLL_CrcSet(TPLL_CrcConfig);
//使能中断
core_interrupt_enable()
//使能rf中断
plic_interrupt_enable(IRQ_ZB_RT);
//清除所有中断标志位
rf_clr_irq_mask(FLD_RF_IRQ_ALL);
//清除所有中断标志位
rf_irq_clr_src(FLD_RF_IRQ_ALL);
//使能特定rf通信中断
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);
//填写ack payload
TPLL_WriteAckPayload(PRX_PIPE, ptx_buffer, ack_payload, TX_PAYLOAD_LEN);
//开始prx
TPLL_PRXTrig();
rx_timestamp = reg_bb_timer_tick;
//2.中断处理
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处理发送
//当收到正确包时
if(1 == rx_flag)
{
#if UI_LED_ENABLE
gpio_toggle(GPIO_LED_GREEN);
#endif
rx_flag = 0;
//获取当前收包的包地址
rx_packet = TPLL_GetRxPacket(rx_buf);
//获取收包crc
TPLL_GetRxPacketCrc(rx_packet,crc);
//获取收包payload地址以及payload len、timestamp、rssi等在ReadRxPayload结构体里变量值。
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;
//等待tx fifo 为空
while(!TPLL_TxFifoEmpty(0));
ack_payload[4]++;
//准备发送ack 内容
TPLL_WriteAckPayload(PRX_PIPE, ptx_buffer, ack_payload, TX_PAYLOAD_LEN);
}
测试现象:
在两块板子上分别烧录ptx与prx的例程进行通信。用BDT工具可以看到prx跟ptx的各个中断计数值,以及收到的包值。正常情况下rx_dr\rx_ds\tx的中断数应该很接近。


fast_tpll_ptx/prx
B87用法步骤:
- tpll_fast_ptx:
//1.初始化设置
//设置数据传输速率
TPLL_Init(TPLL_BITRATE_2MBPS);
//设置信号强度
TPLL_SetOutputPower(TPLL_RF_POWER_0DBM);
//设置同步字长度
TPLL_SetAddressWidth(ADDRESS_WIDTH_5BYTES);
//关闭所有通道
TPLL_ClosePipe(TPLL_PIPE_ALL);
//设置传输通道及同步字
TPLL_SetAddress(TPLL_PIPE0, tx_address);
//打开通道
TPLL_OpenPipe(TPLL_PIPE0);
//设置tx通道
TPLL_SetTXPipe(TPLL_PIPE0);
//非必要延时
WaitUs(150);
//清除所有中断源
irq_clr_src();
//清除所有rf中断源
rf_irq_clr_src(FLD_RF_IRQ_ALL);
//使能rf中断
irq_enable_type(FLD_IRQ_ZB_RT_EN);
//清除所有rf中断标志位
rf_irq_disable(FLD_RF_IRQ_ALL);
//使能相应中断标志位
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);
//使能中断
irq_enable();
//设置PTX模式
TPLL_ModeSet(TPLL_MODE_PTX);
//设置 fast rx settle时间为44us
TPLL_Fast_RxSettleInit(TPLL_RX_SETTLE_TIME_44US);
//设置 fast tx settle时间为53us
TPLL_Fast_TxSettleInit(TPLL_TX_SETTLE_TIME_53US);
//设置主动重发次数及重发延迟时间
TPLL_SetAutoRetry(0,150);
//设置rx超时时间,当传输速率为250kbps时,rx的超时时间需要设置为1000us才能合理通信
TPLL_RxTimeoutSet(500);
//使能获取校准Hpmc值,这个接口需要放在设置频点接口前
TPLL_EnableHpmc();
//设置通信频点
TPLL_SetRFChannel(4);
//设置 fast rx settle时间为85us
TPLL_Fast_RxSettleSet(85);
//设置 fast tx settle时间118us
TPLL_Fast_TxSettleSet(118);
//设置 fast tx wait 时间为5us
TPLL_Fast_TxWaitSet(5);
//设置前导码长度
TPLL_Preamble_Set(2);
//如果打开校准
#if(FAST_CALIBRATION_ENABLE)
//通过PTX发送一些包获取校准值
TPLL_Fast_SettleCalib();
//设置校准值
TPLL_SetHpmcCalVal(hpmc[4]);
#endif
//设置通信频点
TPLL_SetRFChannel(chn);
//设置 fast rx settle时间为50us
TPLL_Fast_RxSettleSet(50);
//设置 fast tx settle时间为50us
TPLL_Fast_TxSettleSet(50);
//设置 fast rx wait时间为0us
TPLL_Fast_TxWaitSet(0);
//设置 fast rx wait时间为0us
TPLL_Fast_RxWaitSet(0);
//使能fast rx settle
TPLL_Fast_RxSettleEnable();
//使能fast tx settle
TPLL_Fast_TxSettleEnable();
//复位buffer
TPLL_ResetBuf();
//写发送包payload
tmp = TPLL_WriteTxPayload(PTX_CHANNEL, (const unsigned char *)tx_data, tx_len);
//开始发送数据包
TPLL_PTXTrig();
//2.中断处理参考tpll_ptx例程
//3.main_loop处理参考tpll_ptx例程
- tpll_fast_prx:
//1.初始化设置
//设置数据传输速率
TPLL_Init(TPLL_BITRATE_2MBPS);
//设置信号强度
TPLL_SetOutputPower(TPLL_RF_POWER_0DBM);
//设置同步字长度
TPLL_SetAddressWidth(ADDRESS_WIDTH_5BYTES);
//关闭所有通道
TPLL_ClosePipe(TPLL_PIPE_ALL);
//设置传输通道及同步字
TPLL_SetAddress(TPLL_PIPE0, tx_address);
//打开通道
TPLL_OpenPipe(TPLL_PIPE0);
//非必要延时
WaitUs(150);
//清除所有中断源
irq_clr_src();
//清除所有rf中断源
rf_irq_clr_src(FLD_RF_IRQ_ALL);
//使能rf中断
irq_enable_type(FLD_IRQ_ZB_RT_EN);
//清除所有rf中断标志位
rf_irq_disable(FLD_RF_IRQ_ALL);
//使能相应中断标志位
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);
//使能中断
irq_enable();
//设置PTX模式
TPLL_ModeSet(TPLL_MODE_PTX);
//使能获取校准Hpmc值,这个接口需要放在设置频点接口前
TPLL_EnableHpmc();
//设置通信频点
TPLL_SetRFChannel(4);
//设置主动重发次数及重发延迟时间
TPLL_SetAutoRetry(0,150);
//设置rx超时时间,当传输速率为250kbps时,rx的超时时间需要设置为1000us才能合理通信
TPLL_RxTimeoutSet(500);
//设置 fast rx settle时间为44us
TPLL_Fast_RxSettleInit(TPLL_RX_SETTLE_TIME_44US);
//设置 fast tx settle时间为53us
TPLL_Fast_TxSettleInit(TPLL_TX_SETTLE_TIME_53US);
//设置 fast rx settle时间为85us
TPLL_Fast_RxSettleSet(85);
//设置 fast tx settle时间118us
TPLL_Fast_TxSettleSet(118);
//设置 fast tx wait 时间为5us
TPLL_Fast_TxWaitSet(5);
//设置前导码长度
TPLL_Preamble_Set(2);
//如果打开校准
#if(FAST_CALIBRATION_ENABLE)
//通过PTX发送一些包获取校准值
TPLL_Fast_SettleCalib();
//设置校准值
TPLL_SetHpmcCalVal(hpmc[4]);
#endif
//设置PRX模式
TPLL_ModeSet(TPLL_MODE_PRX);
//设置通信频点
TPLL_SetRFChannel(chn);
//设置 fast rx settle时间为50us
TPLL_Fast_RxSettleSet(50);
//设置 fast tx settle时间为50us
TPLL_Fast_TxSettleSet(50);
//设置 fast rx wait时间为0us
TPLL_Fast_TxWaitSet(0);
//设置 fast rx wait时间为0us
TPLL_Fast_RxWaitSet(0);
//使能fast rx settle
TPLL_Fast_RxSettleEnable();
//使能fast tx settle
TPLL_Fast_TxSettleEnable();
//复位buffer
TPLL_ResetBuf();
//开始PRX
TPLL_PRXTrig();
Timestamp_value = clock_time();
//2.中断处理参考tpll_prx例程
//3.main_loop处理参考tpll_prx例程
测试现象:
fast ptx与prx的例程与ptx与prx的例程测试方法现象相同。
genfsk_ll (Telink Generic Frequency Shift Keying Link Layer)
2.4GHz私有协议genfsk_ll的相关例程如下表:
| 例程 | 功能简介 |
|---|---|
| gen_fsk_rx | 通过手动控制发送数据包 |
| gen_fsk_tx | 通过手动控制接收数据包 |
| gen_fsk_srx | 通过single rx状态机接收数据包 |
| gen_fsk_stx | 通过single tx状态机发送数据包 |
| gen_fsk_srx2tx | 通过single rx to tx状态机接收数据包并发送应答包 |
| gen_fsk_stx2rx | 通过single tx to rx状态机发送数据包并接收应答包 |
| gen_fsk_stx_dpl_packet | 通过single tx状态机发送可变长数据包 |
gen_fsk_rx/tx
B87用法步骤:
- gen_fsk_rx:
//1.初始化配置
//设置数据传输速率,需要注意的是这个接口要放在其他rf配置接口前
gen_fsk_datarate_set(GEN_FSK_DATARATE_2MBPS);
//设置前导码的长度
gen_fsk_preamble_len_set(4);
//设置同步字的长度
gen_fsk_sync_word_len_set(SYNC_WORD_LEN_4BYTE);
//设置收发通道以及同步字
gen_fsk_sync_word_set(GEN_FSK_PIPE0, sync_word);
//打开收发通道
gen_fsk_pipe_open(GEN_FSK_PIPE0);
//设置包长类型及包长
gen_fsk_packet_format_set(GEN_FSK_PACKET_FORMAT_FIXED_PAYLOAD, 8);
//设置信号强度
gen_fsk_radio_power_set(GEN_FSK_RADIO_POWER_0DBM);
//设置rx buffer及大小
gen_fsk_rx_buffer_set(rx_buf + rx_ptr*RX_BUF_LEN, RX_BUF_LEN);
//设置rf频点,设置为7则频点为2403.5
gen_fsk_channel_set(7);
//设置为接收状态
gen_fsk_radio_state_set(GEN_FSK_STATE_RX);
//就算没有状态机也需要设置,根据时序 rx settle 时间为89us
gen_fsk_rx_settle_set(89);
//等待rx settle时间90us
WaitUs(90);
//2.中断处理接收
if (irq_src & FLD_IRQ_ZB_RT_EN) {
if (rf_irq_src & FLD_RF_IRQ_RX) {
rx_test_cnt++;
//获取接收包地址
rx_packet = rx_buf + rx_ptr*RX_BUF_LEN;
//set to next rx_buf
//循环包地址
rx_ptr = (rx_ptr + 1) % RX_BUF_NUM;
//设置下一次收包地址
gen_fsk_rx_buffer_set((unsigned char *)(rx_buf + rx_ptr*RX_BUF_LEN), RX_BUF_LEN);
//如果接收包的crc校验正确,也就是说收到正常包则rx_flag置1
if (gen_fsk_is_rx_crc_ok(rx_packet)) {
rx_flag = 1;
}
}
rf_irq_clr_src(FLD_RF_IRQ_ALL);
}
//3.main_loop处理接收
//如果接受到包
if (rx_flag) {
rx_flag = 0;
//获取payload地址
rx_payload = gen_fsk_rx_payload_get(rx_packet, &rx_payload_len);
//RSSI(Received Signal Strength Indicator)获取接收信号强度
rssi = (gen_fsk_rx_packet_rssi_get(rx_packet) + 110);
//获取同步字成功相关时的瞬时时钟
rx_timestamp = gen_fsk_rx_timestamp_get(rx_packet);
gpio_toggle(GREEN_LED_PIN);
}
- gen_fsk_tx:
//1.初始化配置
//设置数据传输速率,需要注意的是这个接口要放在其他rf配置接口前
gen_fsk_datarate_set(GEN_FSK_DATARATE_2MBPS);
//设置前导码的长度
gen_fsk_preamble_len_set(4);
//设置同步字的长度
gen_fsk_sync_word_len_set(SYNC_WORD_LEN_4BYTE);
//设置收发通道以及同步字
gen_fsk_sync_word_set(GEN_FSK_PIPE0, sync_word);
//打开收发通道
gen_fsk_pipe_open(GEN_FSK_PIPE0);
//设置tx 通道
gen_fsk_tx_pipe_set(GEN_FSK_PIPE0);
//设置定长包类型及包长
gen_fsk_packet_format_set(GEN_FSK_PACKET_FORMAT_FIXED_PAYLOAD, 8);
//设置信号强度
gen_fsk_radio_power_set(GEN_FSK_RADIO_POWER_0DBM);
//设置rf频点,设置为7则频点为2403.5
gen_fsk_channel_set(7);
//设置为发送状态
gen_fsk_radio_state_set(GEN_FSK_STATE_TX);
//就算没有状态机也需要设置,根据时序 tx settle 时间为149us
gen_fsk_tx_settle_set(149);
//等待tx settle时间130us
WaitUs(130);
//irq 设置
//使能rf tx中断
rf_irq_enable(FLD_RF_IRQ_TX);
//使能rf中断
irq_enable_type(FLD_IRQ_ZB_RT_EN);
//使能中断
irq_enable();
//写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.中断处理接收
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处理接收
tx_done_flag = 0;
//开始发送数据
gen_fsk_tx_start(tx_buffer);
cnt ++;
//当发送完成
while (tx_done_flag == 0);
gpio_toggle(GREEN_LED_PIN);
WaitMs(1000); //delay 500 ms
tx_buffer[4]++;
实验现象:
将gen_fsk_tx与gen_fsk_rx程序分别烧录进两个开发板里,会观测到两个开发板绿灯闪烁,用BDT工具查看rx,会发现rx_buf收到了数据包,rx中断计数也在增加。

TL321x用法步骤:
通用结构体配置
//配置crc结构体
rf_crc_config_t crc_config = {
.init_value = 0xffffffff,
.poly = 0x00001021,
.xor_out = 0,
.byte_order = 1,
.start_cal_pos = 0,
.len = 2,
};
//如果选择灵活包格式,需要设置header内容,包括h0,h1,length,注意h0,h1,length size单位为bit且之合必须是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,
};
//如果打开包过滤功能,需要设置包过滤的字段范围,想要匹配的数据字段以及匹配阈值
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.初始化配置
//选择包格式,如果是定长包第二个参数生效为包长度
gen_fsk_packet_format_set(GEN_FSK_MODE_FIXED_FORMAT,8);
//设置数据传输速率,需要注意的是这个接口要放在其他rf配置接口前
gen_fsk_datarate_set(GEN_FSK_DATARATE_1MBPS);
//设置前导码的长度
gen_fsk_preamble_len_set(4);
//设置同步字的长度
gen_fsk_sync_word_len_set(SYNC_WORD_LEN_4BYTE);
//设置收发通道以及同步字
gen_fsk_sync_word_set(GEN_FSK_PIPE0, sync_word);
//打开收发通道
gen_fsk_pipe_open(GEN_FSK_PIPE0);
//设置crc
gen_fsk_set_crc_config(&crc_config);
//设置信号强度
gen_fsk_radio_power_set(RF_POWER_INDEX_N0p07dBm);
//设置rf频点
gen_fsk_channel_set(8); //set rf freq as 2408MHz,1M step
//设置为接收状态
gen_fsk_radio_state_set(GEN_FSK_STATE_RX);
//设置rx buffer及大小
gen_fsk_rx_buffer_set(rx_buf, RX_BUF_NUM, RX_BUF_LEN);
//设置包过滤条件
#if(PRI_FLT_MODE_EN)
gen_fsk_set_pkt_filter(GEN_FSK_PktFlt);
#endif
//就算没有状态机也需要设置,根据时序 rx settle 时间为89us
gen_fsk_rx_settle_set(89);
//等待rx settle时间90us
WaitUs(90);
//2.中断处理接收
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处理接收
//如果接受到包
if (rx_flag) {
rx_flag = 0;
//获取payload地址
rx_payload = gen_fsk_rx_payload_get(rx_buf, &rx_payload_len);
//RSSI(Received Signal Strength Indicator)获取接收信号强度
rssi = (gen_fsk_rx_packet_rssi_get(rx_buf) + 110);
//获取同步字成功相关时的瞬时时钟
rx_timestamp = gen_fsk_rx_timestamp_get(rx_buf);
gpio_toggle(GREEN_LED_PIN);
}
- gen_fsk_tx:
//1.初始化配置
//选择包格式
gen_fsk_packet_format_set(GEN_FSK_MODE_FIXED_FORMAT,TX_PAYLOAD_LEN);
//设置数据传输速率,需要注意的是这个接口要放在其他rf配置接口前
gen_fsk_datarate_set(GEN_FSK_DATARATE_1MBPS);
//设置前导码的长度
gen_fsk_preamble_len_set(4);
//设置同步字的长度
gen_fsk_sync_word_len_set(SYNC_WORD_LEN_4BYTE);
//设置收发通道以及同步字
gen_fsk_sync_word_set(GEN_FSK_PIPE0, sync_word);
//打开收发通道
gen_fsk_pipe_open(GEN_FSK_PIPE0);
//设置tx 通道
gen_fsk_tx_pipe_set(GEN_FSK_PIPE0);
//设置crc
gen_fsk_set_crc_config(&crc_config);
//设置信号强度
gen_fsk_radio_power_set(GEN_FSK_RADIO_POWER_0DBM);
//设置rf频点
gen_fsk_channel_set(8); //set rf freq as 2408MHz,1M step
//设置为发送状态
gen_fsk_radio_state_set(GEN_FSK_STATE_TX);
//设置tx fifo
gen_fsk_tx_buffer_set(TX_DMA_NUM,TX_DMA_LEN);
//就算没有状态机也需要设置,根据时序 tx settle 时间为149us
gen_fsk_tx_settle_set(149);
//等待tx settle时间130us
WaitUs(130);
//设置中断优先级
plic_set_priority(IRQ_ZB_RT, 3);
//使能rf中断
plic_interrupt_enable(IRQ_ZB_RT);
//设置中断标志位
rf_set_irq_mask(FLD_RF_IRQ_TX);
//清rf中断
rf_clr_irq_status(FLD_RF_IRQ_ALL);
//使能总中断
core_interrupt_enable();
//2.中断处理接收
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处理接收
tx_done_flag = 0;
//准备发送buffer
gen_fsk_write_payload(tx_buffer,tx_payload,TX_PAYLOAD_LEN);
//开始发送
gen_fsk_tx_start(tx_buffer); //start the Radio transmission
//等待发送完成
while (tx_done_flag == 0);
#if UI_LED_ENABLE
gpio_toggle(GPIO_LED_WHITE);
#endif
delay_ms(1000); //delay 100 ms
tx_payload[4]++;
实验现象:
将gen_fsk_tx与gen_fsk_rx程序分别烧录进两个开发板里,会观测到tx端闪白灯,rx端闪绿灯跟蓝灯,用BDT工具查看rx,会发现rx_buf收到了数据包,rx中断计数也在增加。

gen_fsk_srx/stx
B87用法步骤:
- gen_fsk_srx:
//1.初始化配置
//设置数据传输速率,需要注意的是这个接口要放在其他rf配置接口前
gen_fsk_datarate_set(GEN_FSK_DATARATE_2MBPS);
//设置前导码的长度
gen_fsk_preamble_len_set(4);
//设置同步字的长度
gen_fsk_sync_word_len_set(SYNC_WORD_LEN_4BYTE);
//设置收发通道以及同步字
gen_fsk_sync_word_set(GEN_FSK_PIPE0, sync_word);
//打开收发通道
gen_fsk_pipe_open(GEN_FSK_PIPE0);
//设置包长类型及包长
gen_fsk_packet_format_set(GEN_FSK_PACKET_FORMAT_FIXED_PAYLOAD, 8);
//设置信号强度
gen_fsk_radio_power_set(GEN_FSK_RADIO_POWER_0DBM);
//设置rx buffer及大小
gen_fsk_rx_buffer_set(rx_buf + rx_ptr*RX_BUF_LEN, RX_BUF_LEN);
//设置rf频点,设置为7则频点为2403.5
gen_fsk_channel_set(7);
//设置状态机为自动接收状态
gen_fsk_radio_state_set(GEN_FSK_STATE_AUTO);
//如果打开快速设置rx settle模式
#if(RF_FAST_RX_SETTING_ENABLE)
//初始化快速 rx settle的时间为44us
gen_fsk_fast_rxsettleInit(GEN_RX_SETTLE_TIME_44US);
//设置快速 rx settle的时间为44us
gen_fsk_rx_settle_set(44);
//使能快速设置rx settle
gen_fsk_fast_rxsettleEnable();
#else
//设置 rx settle时间为89us
gen_fsk_rx_settle_set(89);
#endif
//irq configuration
//失能所有中断
rf_irq_disable(FLD_RF_IRQ_ALL);
//使能rf相关中断
rf_irq_enable(FLD_RF_IRQ_RX | FLD_RF_IRQ_FIRST_TIMEOUT);
//使能rf中断
irq_enable_type(FLD_IRQ_ZB_RT_EN);
//使能中断
irq_enable();
//start the SRX
//开始接收包,由于第二个参数写0表示rx first time out中断关闭,所以会一直接收直到收到包
gen_fsk_srx_start(clock_time()+50*16, 0);
//2.中断处理接收
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
//循环包地址
rx_ptr = (rx_ptr + 1) % RX_BUF_NUM;
//设置下一次收包地址
gen_fsk_rx_buffer_set((unsigned char *)(rx_buf + rx_ptr*RX_BUF_LEN), RX_BUF_LEN);
//如果接收包的crc校验正确
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处理接收
//当收到正确包
if (rx_flag) {
rx_flag = 0;
//获取payload地址
rx_payload = gen_fsk_rx_payload_get((unsigned char *)rx_packet, (unsigned char *)&rx_payload_len);
//RSSI(Received Signal Strength Indicator)获取接收信号强度
rssi = (gen_fsk_rx_packet_rssi_get((unsigned char *)rx_packet) + 110);
//获取同步字成功相关时的瞬时时钟
rx_timestamp = gen_fsk_rx_timestamp_get((unsigned char *)rx_packet);
gpio_toggle(GREEN_LED_PIN);
//50us后开启下一次接收,time out时间为350us
gen_fsk_srx_start(clock_time()+50*16, 350);
gpio_write(TIME_DEBUG_PIN,1);
gpio_write(TIME_DEBUG_PIN,0);
}
//当first time out 中断发生
if (rx_first_timeout) {
rx_first_timeout = 0;
//继续开启新的接收
gen_fsk_srx_start(clock_time()+50*16, 350);
}
- gen_fsk_stx:
//1.初始化配置
//设置数据传输速率,需要注意的是这个接口要放在其他rf配置接口前
gen_fsk_datarate_set(GEN_FSK_DATARATE_2MBPS);
//设置前导码的长度
gen_fsk_preamble_len_set(4);
//设置同步字的长度
gen_fsk_sync_word_len_set(SYNC_WORD_LEN_4BYTE);
//设置收发通道以及同步字
gen_fsk_sync_word_set(GEN_FSK_PIPE0, sync_word);
//打开收发通道
gen_fsk_pipe_open(GEN_FSK_PIPE0);
//设置tx 通道
gen_fsk_tx_pipe_set(GEN_FSK_PIPE0);
//设置定长包类型及包长
gen_fsk_packet_format_set(GEN_FSK_PACKET_FORMAT_FIXED_PAYLOAD, 8);
//设置信号强度
gen_fsk_radio_power_set(GEN_FSK_RADIO_POWER_0DBM);
//设置rf频点,设置为7则频点为2403.5
gen_fsk_channel_set(7);
//设置状态机为主动发送状态
gen_fsk_radio_state_set(GEN_FSK_STATE_AUTO);
//如果打开快速设置tx settle模式
#if(RF_FAST_TX_SETTING_ENABLE)
//初始化快速 tx settle的时间为53us
gen_fsk_fast_txsettleInit(GEN_TX_SETTLE_TIME_53US);
//设置快速 tx settle的时间为53us
gen_fsk_tx_settle_set(53);
//使能快速设置tx settle
gen_fsk_fast_txsettleEnable();
#else
//设置 tx settle时间为149us
gen_fsk_tx_settle_set(149);
#endif
//irq 设置
//使能rf tx中断
rf_irq_enable(FLD_RF_IRQ_TX);
//使能rf中断
irq_enable_type(FLD_IRQ_ZB_RT_EN);
//使能中断
irq_enable();
//写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.中断处理接收
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处理接收
tx_done_flag = 0;
//100us后发送数据包
gen_fsk_stx_start(tx_buffer, clock_time()+100*16);
gpio_write(TIME_DEBUG_PIN,1);
gpio_write(TIME_DEBUG_PIN,0);
//发送完成
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]++;
实验现象:
将gen_fsk_stx与gen_fsk_srx程序分别烧录进两个开发板里,会观测到stx亮绿灯,srx绿灯闪烁,用BDT工具查看srx,会发现rx_buf收到了数据包,rx中断计数也在增加。

TL321x用法步骤:
- gen_fsk_srx:
//1.初始化配置
//选择包格式
gen_fsk_packet_format_set(GEN_FSK_MODE_FIXED_FORMAT,8);
//设置数据传输速率,需要注意的是这个接口要放在其他rf配置接口前
gen_fsk_datarate_set(GEN_FSK_DATARATE_1MBPS);
//设置前导码的长度
gen_fsk_preamble_len_set(4);
//设置同步字的长度
gen_fsk_sync_word_len_set(SYNC_WORD_LEN_4BYTE);
//设置收发通道以及同步字
gen_fsk_sync_word_set(GEN_FSK_PIPE0, sync_word);
//打开收发通道
gen_fsk_pipe_open(GEN_FSK_PIPE0);
//设置tx pipe
gen_fsk_tx_pipe_set(GEN_FSK_PIPE0);
//设置crc
gen_fsk_set_crc_config(&crc_config);
//设置信号强度
gen_fsk_radio_power_set(RF_POWER_INDEX_N0p07dBm);
//设置tx buffer及大小
gen_fsk_rx_buffer_set(rx_buf, RX_BUF_NUM, RX_BUF_LEN);
//设置rf频点,step为1M
gen_fsk_channel_set(17);
//设置状态机为自动接收状态
gen_fsk_radio_state_set(GEN_FSK_STATE_AUTO);
//设置包过滤条件
#if(PRI_FLT_MODE_EN)
gen_fsk_set_pkt_filter(GEN_FSK_PktFlt);
#endif
//设置rx settle 时间
gen_fsk_rx_settle_set(89);
//等待rx settle
delay_us(90);
//设置中断优先级
plic_set_priority(IRQ_ZB_RT, 3);
//使能rf中断
plic_interrupt_enable(IRQ_ZB_RT);
//设置中断标志位
rf_set_irq_mask(FLD_RF_IRQ_RX | FLD_RF_IRQ_FIRST_TIMEOUT | FLD_RF_IRQ_PKT_MATCH | FLD_RF_IRQ_PKT_UNMATCH);
//清rf中断
rf_clr_irq_status(FLD_RF_IRQ_ALL);
//使能总中断
core_interrupt_enable();
//start the SRX
//开始接收包,由于第二个参数写0表示rx first time out中断关闭,所以会一直接收直到收到包
gen_fsk_srx_start(rf_stimer_get_tick()+50*RF_SYSTEM_TIMER_TICK_1US, 0);
//2.中断处理接收
if (rf_get_irq_status(FLD_RF_IRQ_RX)){
//获取接收包地址
rx_packet = rf_get_rx_packet_addr(RX_BUF_NUM,RX_BUF_LEN,rx_buf);
irq_cnt_rx++;
//如果crc正确相应cnt值加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处理接收
//当收到正确包
if (rx_flag) {
rx_flag = 0;
//获取payload起始位置及payload长度
rx_payload = gen_fsk_rx_payload_get(rx_packet, &rx_payload_len);
//获取收包rssi值
rssi = (gen_fsk_rx_packet_rssi_get(rx_packet) + 110);
//获取timestamp值
rx_timestamp = gen_fsk_rx_timestamp_get(rx_packet);
#if UI_LED_ENABLE
gpio_toggle(GPIO_LED_GREEN);
#endif
//开始收包
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;
//开始收包
gen_fsk_srx_start(rf_stimer_get_tick()+50*RF_SYSTEM_TIMER_TICK_1US, 0);
}
- gen_fsk_stx:
//1.初始化配置
//选择包格式
gen_fsk_packet_format_set(GEN_FSK_MODE_FIXED_FORMAT,TX_PAYLOAD_LEN);
//设置数据传输速率,需要注意的是这个接口要放在其他rf配置接口前
gen_fsk_datarate_set(GEN_FSK_DATARATE_1MBPS);
//设置前导码的长度
gen_fsk_preamble_len_set(4);
//设置同步字的长度
gen_fsk_sync_word_len_set(SYNC_WORD_LEN_4BYTE);
//设置收发通道以及同步字
gen_fsk_sync_word_set(GEN_FSK_PIPE0, sync_word);
//打开收发通道
gen_fsk_pipe_open(GEN_FSK_PIPE0);
//设置tx pipe
gen_fsk_tx_pipe_set(GEN_FSK_PIPE0);
//设置crc
gen_fsk_set_crc_config(&crc_config);
//设置信号强度
gen_fsk_radio_power_set(RF_POWER_INDEX_N0p07dBms);
//设置tx buffer及大小
gen_fsk_tx_buffer_set(TX_BUF_NUM,TX_BUF_LEN);
//设置rf频点,step为1M
gen_fsk_channel_set(17);
//设置状态机为自动接收状态
gen_fsk_radio_state_set(GEN_FSK_STATE_AUTO);
//设置tx settle时间
gen_fsk_tx_settle_set(149);
//设置中断优先级
plic_set_priority(IRQ_ZB_RT, 3);
//使能rf中断
plic_interrupt_enable(IRQ_ZB_RT);
//设置中断标志位
rf_set_irq_mask(FLD_RF_IRQ_TX);
//清rf中断
rf_clr_irq_status(FLD_RF_IRQ_ALL);
//使能总中断
core_interrupt_enable();
//2.中断处理接收
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处理接收
tx_done_flag = 0;
//准备发送paylaod
gen_fsk_write_payload(tx_buffer,tx_payload,TX_PAYLOAD_LEN);
//100us后开始tx
gen_fsk_stx_start(tx_buffer, rf_stimer_get_tick()+100*RF_SYSTEM_TIMER_TICK_1US);
//等待tx结束
while (tx_done_flag == 0);
tx_payload[4]++;
#if (TLKAPI_DEBUG_ENABLE)
gpio_toggle(GPIO_LED_WHITE);
#endif
delay_ms(200);
实验现象:
将gen_fsk_stx与gen_fsk_srx程序分别烧录进两个开发板里,会观测到tx端闪白灯,rx端闪绿灯跟蓝灯,用BDT工具查看srx,会发现rx_buf收到了数据包,rx中断计数也在增加。

gen_fsk_srx2tx/stx2rx
B87用法步骤:
- gen_fsk_srx2tx:
//1.初始化配置
//设置数据传输速率,需要注意的是这个接口要放在其他rf配置接口前
gen_fsk_datarate_set(GEN_FSK_DATARATE_2MBPS);
//设置前导码的长度
gen_fsk_preamble_len_set(4);
//设置同步字的长度
gen_fsk_sync_word_len_set(SYNC_WORD_LEN_4BYTE);
//设置收发通道以及同步字
gen_fsk_sync_word_set(GEN_FSK_PIPE0, sync_word);
//打开收发通道
gen_fsk_pipe_open(GEN_FSK_PIPE0);
//设置tx通道
gen_fsk_tx_pipe_set(GEN_FSK_PIPE0);
//设置包长类型及包长
gen_fsk_packet_format_set(GEN_FSK_PACKET_FORMAT_FIXED_PAYLOAD, 8);
//设置信号强度
gen_fsk_radio_power_set(GEN_FSK_RADIO_POWER_0DBM);
//设置rx buffer及大小
gen_fsk_rx_buffer_set(rx_buf + rx_ptr*RX_BUF_LEN, RX_BUF_LEN);
//设置rf频点,设置为7则频点为2403.5
gen_fsk_channel_set(7);
//设置状态机为自动收发状态
gen_fsk_radio_state_set(GEN_FSK_STATE_AUTO);
//设置 rx settle时间为89us
gen_fsk_rx_settle_set(89);
//写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
//失能所有中断
rf_irq_disable(FLD_RF_IRQ_ALL);
//使能rf相关中断
rf_irq_enable(FLD_RF_IRQ_RX | FLD_RF_IRQ_TX);
//使能rf中断
irq_enable_type(FLD_IRQ_ZB_RT_EN);
//使能中断
irq_enable();
//start the SRX
//50us开始接收包,收到包后自动发包,由于第三个参数写0表示rx first time out中断关闭,所以会一直接收直到收到包
gen_fsk_srx2tx_start(tx_buffer, clock_time()+50*16, 0);
//2.中断处理接收
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
//循环包地址
rx_ptr = (rx_ptr + 1) % RX_BUF_NUM;
//设置下一次收包地址
gen_fsk_rx_buffer_set((unsigned char *)(rx_buf + rx_ptr*RX_BUF_LEN), RX_BUF_LEN);
//如果接收包的crc校验正确,也就是说收到正常包则rx_flag置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处理接收
//当收到正确包
if (rx_flag) {
rx_flag = 0;
//获取payload地址
rx_payload = gen_fsk_rx_payload_get((unsigned char *)rx_packet, (unsigned char *)&rx_payload_len);
//RSSI(Received Signal Strength Indicator)获取接收信号强度
rssi = (gen_fsk_rx_packet_rssi_get((unsigned char *)rx_packet) + 110);
//获取同步字成功相关时的瞬时时钟
rx_timestamp = gen_fsk_rx_timestamp_get((unsigned char *)rx_packet);
gpio_toggle(GREEN_LED_PIN);
}
//当发送完成
if (tx_done_flag) {
tx_done_flag = 0;
//start the SRX2TX
//50us开始接收包,收到包后自动发包,由于第三个参数写0表示rx first time out中断关闭,所以会一直接收直到收到包
gen_fsk_srx2tx_start(tx_buffer, clock_time()+50*16, 0);
}
- gen_fsk_stx2rx:
//1.初始化配置
//设置数据传输速率,需要注意的是这个接口要放在其他rf配置接口前
gen_fsk_datarate_set(GEN_FSK_DATARATE_2MBPS);
//设置前导码的长度
gen_fsk_preamble_len_set(4);
//设置同步字的长度
gen_fsk_sync_word_len_set(SYNC_WORD_LEN_4BYTE);
//设置收发通道以及同步字
gen_fsk_sync_word_set(GEN_FSK_PIPE0, sync_word);
//打开收发通道
gen_fsk_pipe_open(GEN_FSK_PIPE0);
//设置tx通道
gen_fsk_tx_pipe_set(GEN_FSK_PIPE0);
//设置包长类型及包长
gen_fsk_packet_format_set(GEN_FSK_PACKET_FORMAT_FIXED_PAYLOAD, 8);
//设置信号强度
gen_fsk_radio_power_set(GEN_FSK_RADIO_POWER_0DBM);
//设置rx buffer及大小
gen_fsk_rx_buffer_set(rx_buf + rx_ptr*RX_BUF_LEN, RX_BUF_LEN);
//设置rf频点,设置为7则频点为2403.5
gen_fsk_channel_set(7);
//设置状态机为自动收发状态
gen_fsk_radio_state_set(GEN_FSK_STATE_AUTO);
//设置 tx settle时间为149us
gen_fsk_tx_settle_set(149);
//irq configuration
//失能所有中断
rf_irq_disable(FLD_RF_IRQ_ALL);
//使能rf相关中断
rf_irq_enable(FLD_RF_IRQ_RX | FLD_RF_IRQ_TX | FLD_RF_IRQ_RX_TIMEOUT);
//使能rf中断
irq_enable_type(FLD_IRQ_ZB_RT_EN);
//使能中断
irq_enable();
//写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
//50us后开始发包,发送包后自动接收包,time out 时间为250us
gen_fsk_stx2rx_start(tx_buffer, clock_time()+50*16, 250);
//2.中断处理接收
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
//循环包地址
rx_ptr = (rx_ptr + 1) % RX_BUF_NUM;
//设置下一次收包地址
gen_fsk_rx_buffer_set((unsigned char *)(rx_buf + rx_ptr*RX_BUF_LEN), RX_BUF_LEN);
//如果接收包的crc校验正确
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处理接收
//当rx time out 中断发生
if (rx_timeout_flag) {
rx_timeout_flag = 0;
WaitMs(100);
//50us开始发包,发送包后自动接收包,time out 时间为250us
gen_fsk_stx2rx_start(tx_buffer, clock_time()+50*16, 250);
}
//当收到正确包
if (rx_flag) {
rx_flag = 0;
//获取payload地址
rx_payload = gen_fsk_rx_payload_get((unsigned char *)rx_packet, (unsigned char *)&rx_payload_len);
//RSSI(Received Signal Strength Indicator)获取接收信号强度
rssi = (gen_fsk_rx_packet_rssi_get((unsigned char *)rx_packet) + 110);
//获取同步字成功相关时的瞬时时钟
rx_timestamp = gen_fsk_rx_timestamp_get((unsigned char *)rx_packet);
gpio_toggle(GREEN_LED_PIN);
WaitMs(100);
//50us开始发包,发送包后自动接收包,time out 时间为250us
gen_fsk_stx2rx_start(tx_buffer, clock_time()+50*16, 250);
}
测试现象:
将gen_fsk_stx2rx与gen_fsk_srx2tx程序分别烧录进两个开发板里,会观测到两个板子绿灯闪烁,用BDT工具查看stx2rx与srx2tx,会发现rx_buf都收到了包,且rx中断计数也在增加。


TL321x用法步骤:
- gen_fsk_srx2tx:
//1.初始化配置
//选择包格式
gen_fsk_packet_format_set(GEN_FSK_MODE_LEGACY_VARIABLE_FORMAT,TX_PAYLOAD_LEN);
//设置数据传输速率,需要注意的是这个接口要放在其他rf配置接口前
gen_fsk_datarate_set(GEN_FSK_DATARATE_2MBPS);
//设置前导码的长度
gen_fsk_preamble_len_set(4);
//设置同步字的长度
gen_fsk_sync_word_len_set(SYNC_WORD_LEN_4BYTE);
//设置收发通道以及同步字
gen_fsk_sync_word_set(GEN_FSK_PIPE0, sync_word);
//打开收发通道
gen_fsk_pipe_open(GEN_FSK_PIPE0);
//设置tx通道
gen_fsk_tx_pipe_set(GEN_FSK_PIPE0);
//设置crc
gen_fsk_set_crc_config(&crc_config);
//设置信号强度
gen_fsk_radio_power_set(RF_POWER_INDEX_N0p07dBm);
//设置rx/tx buffer及大小
gen_fsk_rx_buffer_set(rx_buf, RX_BUF_NUM, RX_BUF_LEN);
gen_fsk_tx_buffer_set(TX_BUF_NUM,TX_BUF_LEN);
//设置rf频点,设置为17则频点为2417
gen_fsk_channel_set(17);
//设置状态机为自动收发状态
gen_fsk_radio_state_set(GEN_FSK_STATE_AUTO);
//设置包过滤条件
#if(PRI_FLT_MODE_EN)
gen_fsk_set_pkt_filter(GEN_FSK_PktFlt);
#endif
//设置 rx settle时间为89us
gen_fsk_rx_settle_set(89);
//等待rx settle
delay_us(90);
//设置中断优先级
plic_set_priority(IRQ_ZB_RT, 3);
//使能rf中断
plic_interrupt_enable(IRQ_ZB_RT);
//设置中断标志位
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);
//清rf中断
rf_clr_irq_status(FLD_RF_IRQ_ALL);
//使能总中断
core_interrupt_enable();
//准备发送包
gen_fsk_write_payload(tx_buffer,tx_payload,TX_PAYLOAD_LEN);
//50us开始接收包,收到包后自动发包,由于第三个参数写0表示rx first time out中断关闭,所以会一直接收直到收到包
gen_fsk_srx2tx_start(tx_buffer, rf_stimer_get_tick()+50*RF_SYSTEM_TIMER_TICK_1US, 0);
//2.中断处理接收
if (rf_get_irq_status(FLD_RF_IRQ_RX)){
//获取收包地址
rx_packet = rf_get_rx_packet_addr(RX_BUF_NUM,RX_BUF_LEN,rx_buf);
irq_cnt_rx++;
//判断收包crc是否正确
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处理接收
//当收到正确包
if (rx_flag) {
rx_flag = 0;
//获取paylaod起始地址及其长度
rx_payload = gen_fsk_rx_payload_get((unsigned char *)rx_packet, (unsigned char *)&rx_payload_len);
//获取收包rssi值
rssi = (gen_fsk_rx_packet_rssi_get((unsigned char *)rx_packet) + 110);
//获取收包瞬间时间 timestamp
rx_timestamp = gen_fsk_rx_timestamp_get((unsigned char *)rx_packet);
#if UI_LED_ENABLE
gpio_toggle(GPIO_LED_GREEN);
#endif
}
//发包结束
if (tx_done_flag) {
tx_done_flag = 0;
//start the SRX2TX
tx_payload[4]++;
//准备发包数据
gen_fsk_write_payload(tx_buffer,tx_payload,TX_PAYLOAD_LEN);
//50us后开始收包
gen_fsk_srx2tx_start(tx_buffer, rf_stimer_get_tick()+50*RF_SYSTEM_TIMER_TICK_1US, 0);
}
//收包超时
if(rx_first_timeout_flag){
rx_first_timeout_flag = 0;
//50us后开始收包
gen_fsk_srx2tx_start(tx_buffer, rf_stimer_get_tick()+50*RF_SYSTEM_TIMER_TICK_1US, 0);
}
- gen_fsk_stx2rx:
//1.初始化配置
//选择包格式
gen_fsk_packet_format_set(GEN_FSK_MODE_LEGACY_VARIABLE_FORMAT,TX_PAYLOAD_LEN);
//设置数据传输速率,需要注意的是这个接口要放在其他rf配置接口前
gen_fsk_datarate_set(GEN_FSK_DATARATE_1MBPS);
//设置前导码的长度
gen_fsk_preamble_len_set(4);
//设置同步字的长度
gen_fsk_sync_word_len_set(SYNC_WORD_LEN_4BYTE);
//设置收发通道以及同步字
gen_fsk_sync_word_set(GEN_FSK_PIPE0, sync_word);
//打开收发通道
gen_fsk_pipe_open(GEN_FSK_PIPE0);
//设置tx通道
gen_fsk_tx_pipe_set(GEN_FSK_PIPE0);
//设置crc
gen_fsk_set_crc_config(&crc_config);
//设置信号强度
gen_fsk_radio_power_set(RF_POWER_INDEX_N0p07dBm);
//设置rx/tx buffer及大小
gen_fsk_rx_buffer_set(rx_buf, RX_BUF_NUM, RX_BUF_LEN);
gen_fsk_tx_buffer_set(TX_BUF_NUM,TX_BUF_LEN);
//设置rf频点,设置为17则频点为2417
gen_fsk_channel_set(17);
//设置状态机为自动收发状态
gen_fsk_radio_state_set(GEN_FSK_STATE_AUTO);
//设置过滤包条件
#if(PRI_FLT_MODE_EN)
gen_fsk_set_pkt_filter(GEN_FSK_PktFlt);
#endif
//设置rx settle时间
gen_fsk_rx_settle_set(89);
//等待rx settle
delay_us(90);
//设置中断优先级
plic_set_priority(IRQ_ZB_RT, 3);
//使能rf中断
plic_interrupt_enable(IRQ_ZB_RT);
//设置中断标志位
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);
//清除rf中断标志
rf_clr_irq_status(FLD_RF_IRQ_ALL);
//使能总中断
core_interrupt_enable();
//准备tx buffer
gen_fsk_write_payload(tx_buffer,tx_payload,TX_PAYLOAD_LEN);
//50us后开始tx,tx结束后从rx settle到rx timeout时间为500us
gen_fsk_stx2rx_start(tx_buffer, rf_stimer_get_tick()+50*RF_SYSTEM_TIMER_TICK_1US, 500);
//2.中断处理接收
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)){
//获取收包地址
rx_packet = rf_get_rx_packet_addr(RX_BUF_NUM,RX_BUF_LEN,rx_buf);
irq_cnt_rx++;
//判断收包crc是否正确
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处理接收
//收到包
if (rx_flag) {
rx_flag = 0;
//获取paylaod起始地址及paylaod长度
rx_payload = gen_fsk_rx_payload_get((unsigned char *)rx_packet, (unsigned char *)&rx_payload_len);
//获取收包rssi
rssi = (gen_fsk_rx_packet_rssi_get((unsigned char *)rx_packet) + 110);
//获取收包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]++;
//准备tx buffer数据
gen_fsk_write_payload(tx_buffer,tx_payload,TX_PAYLOAD_LEN);
//50us后开始发包
gen_fsk_stx2rx_start(tx_buffer, rf_stimer_get_tick()+50*RF_SYSTEM_TIMER_TICK_1US, 500);
}
//收包超时
if (rx_timeout_flag) {
rx_timeout_flag = 0;
delay_ms(200);
//50us后开始发旧包
gen_fsk_stx2rx_start(tx_buffer, rf_stimer_get_tick()+50*RF_SYSTEM_TIMER_TICK_1US, 500);
}
测试现象:
将gen_fsk_stx2rx与gen_fsk_srx2tx程序分别烧录进两个开发板里,会观测到两个板子都同时闪蓝灯跟白灯,用BDT工具查看stx2rx与srx2tx,会发现rx_buf都收到了包,且rx中断计数也在增加。


gen_fsk_stx_dpl_packet
B87用法步骤:
- gen_fsk_stx_dpl_packet:
//1.初始化配置
//设置数据传输速率,需要注意的是这个接口要放在其他rf配置接口前
gen_fsk_datarate_set(GEN_FSK_DATARATE_2MBPS);
//设置前导码的长度
gen_fsk_preamble_len_set(4);
//设置同步字的长度
gen_fsk_sync_word_len_set(SYNC_WORD_LEN_4BYTE);
//设置收发通道以及同步字
gen_fsk_sync_word_set(GEN_FSK_PIPE0, sync_word);
//打开收发通道
gen_fsk_pipe_open(GEN_FSK_PIPE0);
//设置tx 通道
gen_fsk_tx_pipe_set(GEN_FSK_PIPE0);
//设置变长包类型
gen_fsk_packet_format_set(GEN_FSK_PACKET_FORMAT_VARIABLE_PAYLOAD, 0);
//设置信号强度
gen_fsk_radio_power_set(GEN_FSK_RADIO_POWER_0DBM);
//设置rf频点,设置为7则频点为2403.5
gen_fsk_channel_set(7);
//设置状态机为主动发送状态
gen_fsk_radio_state_set(GEN_FSK_STATE_AUTO);
//设置 tx settle时间为149us
gen_fsk_tx_settle_set(149);
//失能自动pid处理
gen_fsk_auto_pid_disable();
//irq 设置
//使能rf tx中断
rf_irq_enable(FLD_RF_IRQ_TX);
//使能rf中断
rf_irq_enable(FLD_RF_IRQ_TX);
//使能中断
irq_enable();
tx_payload_length = 5;
//写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处理接收
//手动设置pid
gen_fsk_set_pid(&tx_buffer, pid);
//100us后开始发包
gen_fsk_stx_start(tx_buffer, clock_time()+100*16);
//当发送完成中断发生
while(rf_tx_finish() == 0);
//清除相关中断标志位
rf_tx_finish_clear_flag();
//pid循环
pid = (pid + 1)&0x03;
gpio_toggle(GREEN_LED_PIN);
WaitMs(1000);
测试现象:
由于tpll_prx也是不定长包格式所以将tpll_prx的频点、同步字以及同步字长度与gen_fsk_stx_dpl_packet设置一样并分别烧录进开发板里就可以进行通信测试,通过BDT工具查看tpll_prx,可以看到tpll_prx能收到stx_dpl_packet发出的正常包。

tpsll(Telink proprietary stack link layer)
2.4G私有协议tpsll的相关例程如下表:
| 例程 | 功能简介 |
|---|---|
| tpsll_srx | 通过single rx状态机发送数据包 |
| tpsll_stx | 通过single tx状态机接收数据包 |
| tpsll_srx2tx | 通过single rx to tx状态机接收数据包并选择是否发送应答包 |
| tpsll_stx2rx | 通过single tx to rx状态机接收数据包并选择是否发送应答包 |
tpsll_srx/stx
B87用法步骤:
- tpsll_srx:
//初始化设置
//设置数据传输速率,需要注意的是这个接口要放在其他rf配置接口前
tpsll_init(TPSLL_DATERATE_2M);
//设置通信频点
tpsll_channel_set(chn);
//设置前导码长度
tpsll_preamble_len_set(2);
//设置同步字长度
tpsll_sync_word_len_set(SYNC_WORD_LEN_4BYTE);
//设置同步字以及通信通道
tpsll_sync_word_set(TPSLL_PIPE0,sync_word);
//打开通信通道
tpsll_pipe_open(TPSLL_PIPE0);
//设置rx buffer及其大小
tpsll_rx_buffer_set((unsigned char *)tpsll_rxbuf,RX_BUF_SIZE);
//设置通信强度
tpsll_radio_power_set(TPSLL_RADIO_POWER_6DBM);
//设置rx settle 时间为90us
tpsll_rx_settle_set(90);
//irq configuration
//清除所有中断
rf_irq_disable(FLD_RF_IRQ_ALL);
//使能相关中断
rf_irq_enable(FLD_RF_IRQ_RX | FLD_RF_IRQ_FIRST_TIMEOUT);
//使能rf中断
irq_enable_type(FLD_IRQ_ZB_RT_EN);
//使能中断
irq_enable();
//start the SRX
//50us后开始接收,由于第二个参数为0表示关闭rx timeout,会一直接收直到接收到包
tpsll_srx_start(clock_time()+50*16, 0);
//中断处理
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++;
//如果收到包的crc校验正确,则相应rx的flag置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处理
//当收到crc正确包
if (rx_flag) {
rx_flag = 0;
//get rx_payload
//获取payload地址
rx_payload = tpsll_rx_payload_get((unsigned char *)tpsll_rxbuf, (unsigned char *)&rx_payload_len);
//获取接收信号强度
rssi = (tpsll_rx_packet_rssi_get((unsigned char *)tpsll_rxbuf) + 110);
//获取瞬时接收信号强度
rssi_ins = (tpsll_rx_instantaneous_rssi_get() + 110);
//获取当同步字相关时的瞬时时钟时间
rx_timestamp = tpsll_rx_timestamp_get((unsigned char *)tpsll_rxbuf);
//start the SRX
gpio_toggle(GREEN_LED_PIN);
//在50us后开始接收,rx time out时间为500us
tpsll_srx_start(clock_time()+50*16, 500);
gpio_write(DEBUG_PIN,1);
gpio_write(DEBUG_PIN,0);
//获取rx buffer以及payload的长度
tpsll_rx_payload_get(tpsll_rxbuf,&payload_len);
}
if (rx_first_timeout) {//当rx first timeout 发生
rx_first_timeout = 0;
//start the SRX
//在50us后开始接收,rx time out时间为500us
tpsll_srx_start(clock_time()+50*16, 500);
}
- tpsll_stx:
//初始化设置
//设置数据传输速率,需要注意的是这个接口要放在其他rf配置接口前
tpsll_init(TPSLL_DATERATE_2M);
//设置通信频点
tpsll_channel_set(chn);
//设置前导码长度
tpsll_preamble_len_set(2);
//设置同步字长度
tpsll_sync_word_len_set(SYNC_WORD_LEN_4BYTE);
//设置同步字以及通信通道
tpsll_sync_word_set(TPSLL_PIPE0,sync_word);
//打开通信通道
tpsll_pipe_open(TPSLL_PIPE0);
//设置tx信道
tpsll_tx_pipe_set(TPSLL_PIPE0);
//设置通信强度
tpsll_radio_power_set(TPSLL_RADIO_POWER_6DBM);
//设置tx settle 时间为113us
tpsll_tx_settle_set(113);
//irq configuration
//清除所有中断
rf_irq_disable(FLD_RF_IRQ_ALL);
//使能相关中断
rf_irq_enable(FLD_RF_IRQ_TX);
//使能rf中断
irq_enable_type(FLD_IRQ_ZB_RT_EN);
//使能中断
irq_enable();
//start the STX
//写发送包数据的payload
tpsll_tx_write_payload(payload,payload_len);
//在50us后发送数据包
tpsll_stx_start(clock_time()+50*16);
//中断处理
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处理
tx_done_flag = 0;
payload[4]++;
gpio_write(DEBUG_PIN,1);
//写发送包数据的payload
tpsll_tx_write_payload(payload,payload_len);
//在50us后发送数据包
tpsll_stx_start(clock_time()+100*16);
gpio_write(DEBUG_PIN,0);
//当发送完成
while (tx_done_flag == 0);
cnt++;
gpio_write(GREEN_LED_PIN, 1); //LED On
WaitMs(1000);
gpio_write(GREEN_LED_PIN, 0); //LED Off
测试现象:
用两块开发板分别烧录tpsll_stx\tpsll_srx例程进行通信测试,用BDT工具可以看到tpsll_srx接收到来自tpsll_stx发出的包,且接收测试计数也在增加。
tpsll_srx2tx/stx2rx
B87用法步骤:
- tpsll_srx2tx:
//初始化设置
//设置数据传输速率,需要注意的是这个接口要放在其他rf配置接口前
tpsll_init(TPSLL_DATERATE_2M);
//设置通信频点
tpsll_channel_set(chn);
//设置前导码长度
tpsll_preamble_len_set(2);
//设置同步字长度
tpsll_sync_word_len_set(SYNC_WORD_LEN_4BYTE);
//设置同步字以及通信通道
tpsll_sync_word_set(TPSLL_PIPE0,sync_word);
//打开通信通道
tpsll_pipe_open(TPSLL_PIPE0);
//设置rx buffer及其大小
tpsll_rx_buffer_set((unsigned char *)tpsll_rxbuf,RX_BUF_SIZE);
//设置通信强度
tpsll_radio_power_set(TPSLL_RADIO_POWER_6DBM);
//设置rx settle 时间为90us
tpsll_rx_settle_set(90);
//irq configuration
//清除所有中断
rf_irq_disable(FLD_RF_IRQ_ALL);
//使能相关中断
rf_irq_enable(FLD_RF_IRQ_TX | FLD_RF_IRQ_RX | FLD_RF_IRQ_FIRST_TIMEOUT);
//使能rf中断
irq_enable_type(FLD_IRQ_ZB_RT_EN);
//使能中断
irq_enable();
//start the SRX
//写应答包的payload
tpsll_tx_write_payload((unsigned char *)payload,payload_len);
//50us后开始接收,接收结束开始自动发包,由于第二个参数为0表示关闭rx timeout,会一直接收直到接收到包
tpsll_srx2tx_start(clock_time()+50*16, 0);
//中断处理
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++;
//如果收到包的crc校验正确,则相应rx的flag置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处理
//当收到crc正确包
if (rx_flag) {
rx_flag = 0;
gpio_toggle(GREEN_LED_PIN);
//获取payload地址
rx_payload = tpsll_rx_payload_get((unsigned char *)tpsll_rxbuf, (unsigned char *)&rx_payload_len);
//获取接收信号强度
rssi = (tpsll_rx_packet_rssi_get((unsigned char *)tpsll_rxbuf) + 110);
//获取当同步字相关时的瞬时时钟时间
rx_timestamp = tpsll_rx_timestamp_get((unsigned char *)tpsll_rxbuf);
//当发送完包
if (tx_done_flag) {
tx_done_flag = 0;
//start the SRX2TX
payload[4]++;
//写应答包的payload
tpsll_tx_write_payload((unsigned char *)payload,payload_len);
//50us后开始接收,接收结束开始自动发包,由于第二个参数为0表示关闭rx timeout,会一直接收直到接收到包
tpsll_srx2tx_start(clock_time()+50*16, 0);
}
}
- tpsll_stx2rx:
//1.初始化设置
tpsll_init(TPSLL_DATERATE_2M);//设置数据传输速率,需要注意的是这个接口要放在其他rf配置接口前
tpsll_channel_set(chn);//设置通信频点
tpsll_preamble_len_set(2);//设置前导码长度
tpsll_sync_word_len_set(SYNC_WORD_LEN_4BYTE);//设置同步字长度
tpsll_sync_word_set(TPSLL_PIPE0,sync_word);//设置同步字以及通信通道
tpsll_pipe_open(TPSLL_PIPE0);//打开通信通道
tpsll_tx_pipe_set(TPSLL_PIPE0);//设置tx信道
tpsll_rx_buffer_set((unsigned char *)tpsll_rxbuf,RX_BUF_SIZE);
tpsll_radio_power_set(TPSLL_RADIO_POWER_6DBM);//设置通信强度
tpsll_tx_settle_set(113);//设置tx settle 时间为113us
//irq configuration
//清除所有中断
rf_irq_disable(FLD_RF_IRQ_ALL);
//使能相关中断
rf_irq_enable(FLD_RF_IRQ_TX|FLD_RF_IRQ_RX | FLD_RF_IRQ_RX_TIMEOUT);
//使能rf中断
irq_enable_type(FLD_IRQ_ZB_RT_EN);
//使能中断
irq_enable();
//start the STX2RX
//写发送包数据的payload
tpsll_tx_write_payload(payload,payload_len);
//在50us后发送数据包,发送完数据包后开始接收应答包,rx timeout时间为250us
tpsll_stx2rx_start(clock_time()+50*16,250);
//2.中断处理
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++;
//当收到crc正确的包时,相应flag置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处理
//当rx timeout中断发生
if (rx_timeout_flag) {
rx_timeout_flag = 0;
WaitMs(100);
//写发送包数据的payload
tpsll_tx_write_payload((unsigned char *)payload,payload_len);
//在50us后发送数据包,发送完数据包后开始接收应答包,rx timeout时间为250us
tpsll_stx2rx_start(clock_time()+50*16,250);
}
//当rx中断发生
if (rx_flag) {
rx_flag = 0;
gpio_toggle(GREEN_LED_PIN);
WaitMs(100);
payload[4]++;
//写发送包数据的payload
tpsll_tx_write_payload((unsigned char *)payload,payload_len);
//在50us后发送数据包,发送完数据包后开始接收应答包,rx timeout时间为250us
tpsll_stx2rx_start(clock_time()+50*16,250);
}
测试现象:
用两块开发板分别烧录tpsll_stx2rx\tpsll_srx2tx例程进行通信测试,用BDT工具可以看到tpsll_srx2tx\tpsll_stx2rx互相接收到对方发出的包,且接收测试计数也在增加。
基础代码介绍
2.4GHz proprietary SDK除了主要的私有协议相关例程外,还提供了丰富的基础例程如UART、timer、I2C、SPI、PWM、ADC、sleep等。客户可以参考基础例程代码进行应用开发,提高开发效率,减轻开发负担。
以下基础例程以B87的SDK为例进行用法介绍:
以下三个函数为所有例程都必须用到的初始化函数。
cpu_wakeup_init(LDO_MODE, EXTERNAL_XTAL_24M);//MCU的初始化,LDO_MODE:LDO电流模式,EXTERNAL_XTAL_24M:外部24M时钟
user_read_flash_value_calib();//用于从flash读取优化值
clock_init(SYS_CLK_24M_Crystal);//时钟初始化选择24M系统时钟源
ADC
用户可以使用adc_sample例程进行adc采样,其共有以下六种模式。
(1) ADC_BASE_MODE
该模式用于GPIO模拟信号输入。
(2) ADC_VBAT_MODE
该模式利用将GPIO输出高进行GPIO采样的方式进行vbat采样的,此时GPIO的电压就是Vbat的电压(该方法是不需要硬件连线的,可以设置一个没有封装出的pin,以节省GPIO资源)
(3) ADC_VBAT_CHANNEL_MODE
该模式通过电池通道获取电池电压。
(4) ADC_TEMP_MODE_EE
该模式获取温度传感器的温度值。
(5) ADC_RNG_MODE
该模式用于产生随机数,除了在这个例程中实现产生随机数,rand_num_gen例程也是同样原理产生随机数。
(6) MANNUAL_MODE_GET_ADC_SAMPLE_RESULT
该模式用于手动采样。注:手动(ADC_NDMA_MODE)采样时,一次只能获取一个adc_code,期间会关闭ADC采样功能,且在使用时一定要保证连续获取adc_code的时间间隔大于2个采样周期。
芯片差异:
B87芯片支持所有模式,B85芯片支持前三种与手动模式,B80支持前四种与手动模式。
用法步骤:
//1.选择模式
#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.初始化配置
#if(ADC_MODE==ADC_RNG_MODE)//选择产生随机数模式
random_generator_init();//产生随机数初始化
#else
adc_init();//ADC初始化
#if(ADC_MODE==ADC_BASE_MODE)//选择ADC_BASE_MODE模式
adc_base_init(ADC_INPUT_PIN);//ADC_BASE_MODE模式初始化
adc_set_ain_pre_scaler(ADC_PRESCALER_1F8);//在ADC_BASE_MODE模式初始化后可以设置预分频
#elif (ADC_MODE==ADC_VBAT_MODE)//选择ADC_VBAT_MODE模式
adc_vbat_init(ADC_INPUT_PIN);//ADC_VBAT_MODE模式初始化
#elif (ADC_MODE==ADC_VBAT_CHANNEL_MODE)//选择ADC_VBAT_CHANNEL_MODE模式
adc_vbat_channel_init();//ADC_VBAT_CHANNEL_MODE模式初始化
#elif (ADC_MODE==ADC_TEMP_MODE_EE)//选择ADC_TEMP_MODE_EE模式
adc_temp_init();//ADC_TEMP_MODE_EE模式初始化
#endif
adc_power_on_sar_adc(1);//在设置完ADC参数后,打开ADC power
#endif
//3.mainloop采样处理
#if(ADC_MODE==ADC_RNG_MODE)
rns_val = rand();//获取随机数
#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();//获取ADC采样
i = (i + 1) % 16;
WaitMs(50);
gpio_toggle(GREEN_LED_PIN);
#elif (ADC_MODE==ADC_TEMP_MODE_EE)
temp_new_val = adc_temp_result();//获取温度采样
#endif
#if(MANNUAL_MODE_GET_ADC_SAMPLE_RESULT==1)
adc_manual_val = adc_sample_and_get_result_manual_mode();//获取手动模式下采样值
#endif
#endif
测试现象:
烧录程序进开发板,以ADC_VBAT_MODE模式为例,用BDT工具可以看到0xcc7十进制表示为3271mv,且绿灯闪烁。

aes_128
硬件aes128加解密,用户可参考例程aes_128。
用法步骤:
//1.初始化代码
unsigned char Encrypt_data[13];
unsigned char Decrypt_data[13];
unsigned char aes_init[13] = {...};
unsigned char AES_Key[16] = {...};
//2.mianloop加解密过程
aes_init[0]++;
aes_encrypt(AES_Key,aes_init,Encrypt_data);//加密
aes_decrypt(AES_Key,Encrypt_data,Decrypt_data);//解密
gpio_toggle(GREEN_LED_PIN);
WaitMs(500); //delay 500 ms
测试现象:
烧录程序进开发板后用BDT工具可以看到绿灯闪烁,且解密后的Decrypt_data数组跟加密前的aes_init数组元素相同,表示加解密成功。

低功耗
低功耗有三种模式,分别为suspend低功耗、deep低功耗、deep retention低功耗。suspend模式相较deep retention低功耗模式睡眠时关掉的模块少所以省电也少,适合短时间睡眠。deep retention低功耗相较于deep低功耗模式由于需要保留一些值在掉电后不改变,所以省电较少。deep低功耗模式省电最多适合长睡眠。想要详细了解低功耗模式可以参考驱动手册,由于gpio、timer唤醒使用较多,所以作为主要讲解。下表展示了相关例程与唤醒源的关系:
| 唤醒源 | 相关例程 |
|---|---|
| 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 |
用法步骤:
(1) 通过不同睡眠例程与宏选择不同睡眠模式。
(2) 在所有初始化前调用以下接口,选择唤醒源时钟是内部时钟唤醒。
blc_pm_select_internal_32k_crystal();
(3) 在调用cpu_sleep_wakeup前调用如下接口,将gpio设置为高阻态,防止电流泄露。
gpio_high_z_config();//set all GPIO as high_Z state, avoiding current leakage
(4) 当唤醒源为USB时需要调用pm_set_suspend_power_cfg(PM_POWER_USB, 1)接口,使得睡眠期间USB不掉电用作唤醒源
(5) 调用cpu_sleep_wakeup(SleepMode_TypeDef sleep_mode, SleepWakeupSrc_TypeDef wakeup_src, unsigned int wakeup_tick)接口进入睡眠。
参数:sleep_mode为睡眠模式。
参数:wakeup_src为唤醒源。
参数:wakeup_tick为睡眠的时间,16表示1us,16*1000就表示1ms。
(6) 用户可以通过pmParam全局变量知道pm相关状态
/**
* @brief deepsleep wakeup status
*/
typedef struct{
unsigned char is_deepretn_back;//mcu是否从deep sleep retention睡眠唤醒回来
unsigned char is_pad_wakeup;//本次是否是被pad唤醒
unsigned char wakeup_src;//本次是被哪种唤醒源唤醒,包括PAD、TIMER、MDEC、LPC和CORE的唤醒状态。
unsigned char rsvd;
}pm_para_t;
extern _attribute_aligned_(4) pm_para_t pmParam;
测试现象:
烧录程序进开发板:
deep 低功耗:
如果是gpio唤醒,则进入睡眠后触发相应gpio中断,程序会重头开始运行,能看到绿灯亮三秒后又熄灭进入睡眠;如果是timer唤醒,则绿灯每隔睡眠时间亮一次。
deep retention 低功耗:
现象与deep sleep相似唯一不同的是由于在掉电后存在.retention_data段的数据不会变,所以每次睡眠储存在该段的一个计数变量就会累加,当进入睡眠次数超过编程规定次数后就会进入一直闪绿灯的循环。
suspend 低功耗:
如果是gpio唤醒,由于唤醒函数放在while循环中,进入睡眠后触发相应gpio中断,程序会从进入睡眠的睡眠函数后继续开始运行,所以结合例程看到的现象是进入睡眠后触发gpio中断,则绿灯亮3秒后又进入睡眠。如果是timer唤醒,进入睡眠后触发相应timer中断,程序会从进入睡眠的睡眠函数后继续开始运行,结合例程用逻辑分析仪连接DEBUG_IO_PIN可以看到的现象是DEBUG_IO_PIN被连续间隔1.1ms拉高8次后,板子开始一直闪绿灯。
Flash
用户要对flash进行操作可参考flash_operation例程。
用法步骤:
(1) 为了保证安全地对flash操作,在对flash进行操作前可以通过宏BATT_CHECK_ENABLE开启低电检测,当低电检测检测到此时的电压低于安全电压则不会再继续下面的flash操作,进入闪红灯循环。
(2) 通过flash_read_mid()接口读取当前flash的mid。
(3) 通过flash_erase_sector()、flash_read_page()、flash_write_page()这三个接口对flash地址进行擦、读、写操作,操作成功则相应测试值置1,操作失败则相应状态错误值置1并陷入while死循环。
(4) 通过读取的mid选择进入不同型号flash的锁、解锁操作测试,操作成功则相应测试值置1,操作失败则相应状态错误值置1并陷入while死循环,其相关接口为flash_lock_midxxxxxx()、flash_unlock_midxxxxxx()。
(5) 在mid为146085型号的flash的锁、解锁操作测试中还有otp的擦、写以及可以通过控制宏FLASH_OTP_LOCK选择是否进行锁otp操作,操作成功则相应测试值置1,操作失败则相应状态错误值置1并陷入while死循环。
测试现象
烧录程序进开发板,开发板闪绿灯说明所有对flash的操作都成功;开发板闪红灯则说明低电检测错误当前电压低于flash安全电压;开发板卡在while循环里则说明有flash操作失败,相应的状态错误值被置1。
Timer
有关时钟的例程如下表:
| 例程 | 用途 |
|---|---|
| timer_gpio_pulse_width | 从GPIO上升沿\下降沿开始到下一个下降沿\上升沿结束触发timer中断并如此循环往复,可用作计算脉冲长度。 |
| timer_gpio_trigger | GPIO每发生一个上升沿\下降沿,定时器就会计数加1,当定时器的计数值达到指定的设定个数,就会产生timer中断。 |
| timer_sys_clock | 用于定时,到达指定时间就会产生timer中断。 |
| timer_tick | 不会产生中断的定时,每当检测到system clock的上升沿,定时器的计数器加1,可手动设置定时器初始值,当达到最大值时又会归0。 |
| timer_wathcdog | 看门狗功能,到达指定时间就进行单片机复位或喂狗不进入单片机复位。 |
用法步骤
timer_gpio_pulse_width:
timer2_gpio_init(SAMPLE_INTPUT_PIN, POL_FALLING);//第一个参数为输入gpio,第二个参数表示以上升沿还是下降沿触发定时器计时
irq_enable();//开启中断
timer2_set_mode(TIMER_MODE_GPIO_WIDTH,0,0);//第一个参数选择gpio计算脉宽模式,后面两个参数默认0
timer_start(TIMER2);//打开TIMER2定时器
timer_gpio_trigger:
timer2_gpio_init(TRIGGER_INPUT_PIN, POL_FALLING);//第一个参数为输入gpio,第二个参数表示以上升沿还是下降沿触发定时器计时
irq_enable();//开启中断
timer2_set_mode(TIMER_MODE_GPIO_TRIGGER,0,3);//第一个参数选择gpio触发模式,第二个参数为计数初始值,第三个参数表示在第几次上升沿\下降沿触发计时器中断
timer_start(TIMER2);//打开TIMER2定时器
timer_sys_clock:
timer2_set_mode(TIMER_MODE_SYSCLK,0,1000 * CLOCK_SYS_CLOCK_1MS);//第一个参数选择系统时钟模式,第二个参数为初始计数值,第三个参数为定时的tick值
timer_start(TIMER2);//打开TIMER2定时器
irq_enable();//开启中断
timer_tick:
timer2_set_mode(TIMER_MODE_TICK,0,0);//第一个参数选择时钟计数模式,第二个参数为初始计数值,第三个参数为定时的tick值,设为0表示不定时
timer_start(TIMER2);//打开TIMER2定时器
timer_wathcdog:
wd_set_interval_ms(1000,CLOCK_SYS_CLOCK_1MS);//第一个参数为喂狗时间单位时ms,第二个参数是在系统时钟下1ms的tick值
wd_start();//打开看门狗
//wd_clear();//关闭看门狗
测试现象
烧录程序进开发板:
timer_gpio_pulse_width:将采样引脚与输出引脚相连,并烧录程序可以看到绿灯闪烁,用逻辑分析仪拉出绿灯引脚,可看到每隔100ms产生一次timer中断符合输出引脚每隔50ms toggle一次的相同跳变沿周期时间。
timer_gpio_trigger:当TRIGGER_INPUT_PIN每输入三次下降沿,触发timer中断,绿灯进行一次亮灭翻转。
timer_sys_clock:每隔一秒闪一次红灯。
timer_tick:绿灯闪烁,用BDT工具可以查看reg_tmr2_tick值在不断增加到最大值又归0继续增加。
timer_wathcdog:可以看到绿灯周期性闪烁。

GPIO
有关GPIO的例程可以参考gpio_irq例程,其用法如下:
用法步骤
(1) 首先通过控制宏选择不同的gpio功能。
(2) 不同的功能有不同的设置接口及用法。
- 设置gpio高阻态可以直接调用gpio_shutdown(GPIO_ALL)接口。
- gpio中断,首先调用gpio_setup_up_down_resistor设置引脚与引脚上拉电阻/引脚下拉电阻,再调用gpio_set_interrupt接口设置引脚与引脚下降沿/上升沿触发中断,最后使能中断即可。
- gpio用作按键中断,首先调用gpio_setup_up_down_resistor设置引脚与引脚上拉电阻,再调用gpio_set_interrupt接口设置引脚与引脚下降沿触发中断,最后使能中断即可。
- gpio上升沿触发中断调用gpio_setup_up_down_resistor接口设置引脚与引脚下拉,再调用gpio_set_interrupt_risc0设置上升沿触发中断,最后使能中断即可。
- gpio下降沿触发中断调用gpio_setup_up_down_resistor接口设置引脚与上拉电阻,再调用gpio_set_interrupt_risc1设置下降沿触发中断,最后使能中断即可。
测试现象
烧录程序进开发板:
gpio中断:每次给与gpio低电平则绿灯亮灭翻转。
gpio用作按键中断:每次按下相应按键会让绿灯亮灭翻转。
gpio上升沿触发中断:将每10ms进行一次翻转的gpio输出接入中断引脚,现象是绿灯隔20ms进行亮灭翻转,肉眼可见绿灯常亮。
gpio下降沿触发中断:将每10ms进行一次翻转的gpio输出接入中断引脚,现象是绿灯隔20ms进行亮灭翻转,肉眼可见绿灯常亮。
PWM
控制脉冲输出,它有如下表5个相关例程。
| 例程 | 功能简介 |
|---|---|
| pwm_normal | 持续发送相同占空比的脉冲,可立刻停下,在发送期间可修改占空比。 |
| pwm_count | 持续发送预设数量的相同占空比的脉冲,可立刻停下,在发送期间修改占空比无效。 |
| pwm_ir | 连续发送预设数量的相同占空比的脉冲组,发送期间可改变占空比,可直接停下也可切换成pwm_count模式发送完一个组再停下,不需要开始信号。 |
| pwm_ir_fifo | 将要发送的信号设置放入FIFO里,则可按照设置内容依次发送不同的占空比的脉冲组。 |
| pwm_ir_dma_fifo | 与IR FIFO模式相似,只是配置不是直接由MCU写在FIFO中,而是通过DMA写到FIFO中。 |
用法步骤
pwm_normal:
pwm_set_clk(CLOCK_SYS_CLOCK_HZ, CLOCK_SYS_CLOCK_HZ);//设置pwm频率
gpio_set_func(GPIO_PA2, AS_PWM0);//设置pwm引脚
pwm_set_mode(PWM0_ID, PWM_NORMAL_MODE);//设置pwm通道与模式
pwm_set_cycle_and_duty(PWM0_ID, (unsigned short) (1000 * CLOCK_SYS_CLOCK_1US), (unsigned short) (500 * CLOCK_SYS_CLOCK_1US));//设置周期跟占空比
pwm_start(PWM0_ID);//开始pwm
pwm_count:
pwm_set_clk(CLOCK_SYS_CLOCK_HZ, CLOCK_SYS_CLOCK_HZ);//设置pwm频率
gpio_set_func(PWM_PIN, AS_PWMx);//设置pwm引脚
pwm_set_mode(PWM_ID, PWM_COUNT_MODE);//设置pwm通道与模式
pwm_set_pulse_num(PWM_ID,PWM_PULSE_NUM);//设置一个脉冲组的脉冲数量
pwm_set_cycle_and_duty(PWM_ID, 1000 * CLOCK_SYS_CLOCK_1US, 500* CLOCK_SYS_CLOCK_1US);//设置周期跟占空比
pwm_set_interrupt_enable(PWM_IRQ_PWM0_PNUM);//开启pwm发送完成中断
irq_set_mask(FLD_IRQ_SW_PWM_EN);//设置中断标志位
irq_enable();//开启中断
pwm_start(PWM_ID);//开始pwm
pwm_ir:
pwm_set_clk(CLOCK_SYS_CLOCK_HZ, CLOCK_SYS_CLOCK_HZ);//设置pwm频率
gpio_set_func(PWM_PIN, AS_PWMx);//设置引脚
pwm_set_mode(PWM_ID, PWM_IR_MODE);//设置pwm模式
pwm_set_pulse_num(PWM_ID,PWM_PULSE_NUM);//设置一个脉冲组的脉冲数量
pwm_set_cycle_and_duty(PWM_ID, 1000 * CLOCK_SYS_CLOCK_1US, 500 * CLOCK_SYS_CLOCK_1US);//设置周期跟占空比
pwm_set_interrupt_enable(PWM_IRQ_PWM0_PNUM);//开启pwm发送完成中断
irq_set_mask(FLD_IRQ_SW_PWM_EN);//设置中断标志位
irq_enable();//开启中断
pwm_start();//开始pwm
pwm_ir_fifo:
pwm_set_clk(CLOCK_SYS_CLOCK_HZ, CLOCK_SYS_CLOCK_HZ);//设置pwm频率
gpio_set_func(PWM_PIN, AS_PWMx);//设置pwm引脚
pwm_set_mode(PWM_ID, PWM_IR_FIFO_MODE);//设置pwm模式
pwm_set_cycle_and_duty(PWM_ID,1000 * CLOCK_SYS_CLOCK_1US, 333 * CLOCK_SYS_CLOCK_1US);//设置周期跟占空比
pwm_set_pwm0_shadow_cycle_and_duty(1000 * CLOCK_SYS_CLOCK_1US, 500 * CLOCK_SYS_CLOCK_1US);//设置pwm0周期跟反占空比
pwm_ir_fifo_set_data_entry(PWM_PULSE_NUM1,0,1);//设置一个脉冲组的脉冲数量,是否使用反占空比以及是否载波
pwm_ir_fifo_set_data_entry(...);
.....
pwm_ir_fifo_set_data_entry(...);
pwm_ir_fifo_set_irq_trig_level(1);//设置触发发送时FIFO中的num
pwm_set_interrupt_enable(PWM_IRQ_PWM0_IR_FIFO);//使能pwm中断
irq_set_mask(FLD_IRQ_SW_PWM_EN);//使能pwm标志位
irq_enable();//开启中断
pwm_start(PWM_ID);//开始pwm
pwm_ir_dma_fifo:
pwm_set_clk(CLOCK_SYS_CLOCK_HZ, CLOCK_SYS_CLOCK_HZ);//设置pwm频率
gpio_set_func(PWM_PIN, AS_PWMx);//设置pwm引脚
pwm_set_mode(PWM_ID, PWM_IR_DMA_FIFO_MODE);//设置pwm模式
pwm_set_cycle_and_duty(PWM_ID, IR_DMA_MAX_TICK, IR_DMA_CMP_TICK);//设置周期跟占空比
pwm_set_pwm0_shadow_cycle_and_duty(IR_DMA_SHADOW_MAX_TICK,IR_DMA_SHADOW_CMP_TICK);//设置pwm0周期跟反占空比
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);//设置一个脉冲组的脉冲数量,是否使用反占空比以及是否载波
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];//设置dma长度
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);//设置dma搬运地址
pwm_set_interrupt_enable(PWM_IRQ_PWM0_IR_DMA_FIFO_DONE);//使能pwm中断
irq_set_mask(FLD_IRQ_SW_PWM_EN);//使能pwm标志位
irq_enable();//开启中断
pwm_start_dma_ir_sending();//开始dma搬运
测试现象
烧录程序进开发板,用逻辑分析仪观察以下几种模式所产生的PWM波:
pwm_normal: 产生3组连续的PWM波,且占空比与周期与设置的相符。
pwm_count:产生一组PWM波,占空比与周期以及脉冲数量与设置相
pwm_ir: 四组相同周期、占空比、脉冲数量相同的PWM波组成一个PWM波。
pwm_ir_fifo: 两组循环不连续,数量不同占空比相反的的pwm波,其数量、周期、占空比与设置相符。

pwm_ir_dma_fifo: 结合代码可以先看到两大组相同规律的数量相同但周期不同的不完全连续的pwm波,后面就看到一组不连续相同间隔的pwm波。
UART
UART是一种异步全双工串行通信协议,它有如下表4个相关例程。
| 例程 | 功能简介 |
|---|---|
| uart_dma | 串口通过dma收发数据 |
| uart_ndma | 串口不通过dma收发数据,且可控制触发收发中断的数据量 |
| uart_soft_rx | 软件gpio模拟串口接收数据 |
| uart_io_printf | 软件gpio模拟串口发送数据 |
用法步骤
uart_dma:
uart_recbuff_init((unsigned char *)&rec_buff, sizeof(rec_buff));//初始化接收buffer
uart_gpio_set(UART_TX_PA2, UART_RX_PD6);//设置收发引脚
uart_reset(); //复位uart数字寄存器,所以有关uart的设置必须在这之后
uart_init_baudrate(115200,CLOCK_SYS_CLOCK_HZ,PARITY_NONE, STOP_BIT_ONE);//设置波特率、时钟、奇偶校验位以及停止位
uart_dma_enable(1, 1);//uart的数据会搬到dma去发送,所以需要使能uart dma
irq_set_mask(FLD_IRQ_DMA_EN);//打开dma的中断标志位
dma_chn_irq_enable(FLD_DMA_CHN_UART_RX | FLD_DMA_CHN_UART_TX, 1);//使能uart rx/tx dma的中断
uart_mask_tx_done_irq_enable();//打开tx_done的中断标志位
uart_mask_error_irq_enable();// 打开错误标志位,当奇偶校验与停止位发生错误时会进入中断
irq_enable_type(FLD_IRQ_UART_EN);//使能uart中断
uart_ndma:
uart_gpio_set(UART_TX_PA2, UART_RX_PD6);//设置收发引脚
uart_reset(); //复位uart数字寄存器,所以有关uart的设置必须在这之后
uart_init_baudrate(115200,CLOCK_SYS_CLOCK_HZ,PARITY_NONE, STOP_BIT_ONE);//设置波特率、时钟、奇偶校验位以及停止位
uart_dma_enable(0, 0);//uart的数据会搬到dma去发送,所以需要使能uart dma
irq_disable_type(FLD_IRQ_DMA_EN);//失能DMA中断
dma_chn_irq_enable(FLD_DMA_CHN_UART_RX | FLD_DMA_CHN_UART_TX, 0);//使能uart rx/tx dma的中断
uart_irq_enable(1,0); //使能uart rx中断
uart_ndma_irq_triglevel(1,0);//设置触发阈值,1表示收发一个byte就会触发接收中断
uart_mask_error_irq_enable();//打开错误标志位,当奇偶校验与停止位发生错误时会进入中断
uart_soft_rx:
soft_uart_rx_init(GPIO_PA4,9600); // 设置接收引脚与比特率
soft_uart_rx_enable();//使能软件接收功能
soft_uart_rx_data_process(soft_uart_rx_callback)//连续接收数据
uart_io_printf:
array_printf( (unsigned short*)&trans_buff,trans_buff_Len);//设置发送数据地址与长度
测试现象
烧录程序进开发板:
uart_dma:在一块开发板上把uart的tx跟rx连接后用BDT工具可以看到buffer数据收发正常,注意由于DMA是4byte搬运,所以接收到的数据可能会有无用数据。

uart_ndma:用两块开发板的tx与rx交叉相连后用BDT工具可以看到buffer数据收发正常。

uart_soft_rx:用串口转usb工具向单片机发送数据,会在串口助手工具上看到单片接收到的数据。第一行的第一个数据为接收数据的数组index,一共有两个数组,数组长度为200,当一次接收超过200的时候会在这行最后加N表示且下次接收数据放在下一个数组里;第二个数据为接收数据长度;第三个跟第四个数据分别表示收到的第一个与最后一个数据的assic码值。

uart_io_printf:用串口转usb工具接收单片机发送的数据可以看见连续接收到发送的数据。

IIC
I2C,由数据线SDA和时钟SCL构成的串行总线,可发送和接收数据,是半双工通信方式。它有如下表4个相关例程。
| 例程 | 功能简介 |
|---|---|
| i2c_slave_dma | i2c通过dma收发数据 |
| i2c_master_dma | i2c通过dma收发数据 |
| i2c_slave_mapping | i2c通过mcu收发数据 |
| i2c_master_mapping | i2c通过mcu收发数据 |
用法步骤
i2c_slave_dma:
i2c_gpio_set(I2C_GPIO_SDA_A3,I2C_GPIO_SCL_A4);// 设置i2c的数据与时钟引脚
//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);//设置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);//设置i2c的写寄存器
i2c_master_dma:
i2c_gpio_set(I2C_GPIO_SDA_A3,I2C_GPIO_SCL_A4);// 设置i2c的数据与时钟引脚
//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)) );//设置i2c master的写地址、时钟分频系数及模式
i2c_write_series(SLAVE_DMA_MODE_ADDR_WRITE, 3, (unsigned char *)i2c_master_tx_buff, DBG_DATA_LEN);//i2c master 写数据
i2c_read_series(SLAVE_DMA_MODE_ADDR_READ, 3, (unsigned char *)i2c_master_rx_buff, DBG_DATA_LEN);//i2c master 读数据
i2c_slave_mapping:
i2c_gpio_set(I2C_GPIO_SDA_A3,I2C_GPIO_SCL_A4);// 设置i2c的数据与时钟引脚
i2c_slave_init(0x5C, I2C_SLAVE_MAP, (unsigned char *)slave_mapping_buff);//初始化slave的写地址、模式及写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);//初始化slave的写buffer
reg_i2c_map_host_status = (FLD_HOST_CMD_IRQ | FLD_HOST_READ_IRQ); //清iic中断状态位
reg_irq_mask |= FLD_IRQ_HOST_CMD_EN; //使能iic中断
irq_enable(); //使能系统中断
i2c_master_mapping:
i2c_gpio_set(I2C_GPIO_SDA_A3,I2C_GPIO_SCL_A4);// 设置i2c的数据与时钟引脚
//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)) );//设置i2c master的写地址、时钟分频系数及模式
i2c_write_series(0, 0, (unsigned char*)i2c_master_tx_buff, DBG_DATA_LEN);//i2c master 写数据
i2c_read_series(0, 0, (unsigned char*)i2c_master_rx_buff, DBG_DATA_LEN);//i2c master 读数据
测试现象
烧录程序进开发板:
i2c_master_dma/i2c_slave_dma:用BDT工具可以看到master的读写buffer以及debug的buffer的数据都如程序所写,slave被读写的次数以及debug的buffer数据也正常。


i2c_master_mapping/i2c_slave_mapping: 用BDT工具看到的现象可参考i2c的dma模式。
SPI
串行外设接口(Serial Peripheral Interface)简称SPI接口,是一种同步串行外设接口,允许嵌入式处理器与各种外围设备通过串行的方式进行通信、数据交换等。它有如下表2个相关例程。
| 例程 | 功能简介 |
|---|---|
| spi_master | spi通过mcu收发数据 |
| spi_slave | spi通过mcu收发数据 |
用法步骤
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);//设置spi时钟速率及模式0
spi_master_gpio_set(GPIO_PA4,GPIO_PD6,GPIO_PA2,GPIO_PA3);//设置spi相关引脚
spi_write(slaveRegAddr_WriteCMD, 4,(unsigned char*)spi_master_tx_buff, DBG_DATA_LEN,SPI_CS_PIN);//通过地址与写命令写一定长度数据
spi_read( slaveRegAddr_ReadCMD , 4,(unsigned char*)spi_master_rx_buff,DBG_DATA_LEN,SPI_CS_PIN);//通过地址与写命令写一定长度数据
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);//设置spi时钟速率及模式0
spi_master_gpio_set(GPIO_PA4,GPIO_PD6,GPIO_PA2,GPIO_PA3);//设置spi相关引脚
reg_spi_slave_irq_status = (FLD_HOST_CMD_IRQ | FLD_HOST_READ_IRQ);//清除spi中断状态位
reg_irq_mask |= FLD_IRQ_HOST_CMD_EN;//使能spi中断标志位
测试现象
烧录程序进开发板:
用BDT工具可以看到spi slave被读的次数在增加,master读取的数据与写入的数据一样。


USB
USB (Universal Serial Bus) 即通用串行总线,是一个外部总线标准,用于规范电脑与外部设备的连接和通讯,是应用在PC领域的接口技术。相关例程为usb_demo。
用法步骤
(1) 在app.config.h中选择应用模式。
(2) 根据选择的不同应用模式选择不同应用的.c文件。
mouse_app.c:
//初始化
irq_enable();//使能总中断
usb_init_interrupt();//启用USB手动中断(在自动中断模式下,USB设备将是USB打印机设备)
usb_set_pin_en();//使能并上拉 1.5k usb的dp引脚
//main_loop
usb_handle_irq();//usb中断处理
usbmouse_hid_report(USB_HID_MOUSE,(unsigned char*)mouse,4);//usb 上报信息
keyboard_app.c:
//初始化
irq_enable();//使能总中断
usb_init_interrupt();//启用USB手动中断(在自动中断模式下,USB设备将是USB打印机设备)
usb_set_pin_en();//使能并上拉 1.5k usb的dp引脚
//main_loop
usb_handle_irq();//usb中断处理
usbkb_hid_report_normal(0x00,kb_data);//上报键值信息
mic_app.c:
//选择模式
#define AUDIO_AMIC_TO_USB 1//模拟麦克风
#define AUDIO_DMIC_TO_USB 2//数字麦克风
#define AUDIO_CODEC_TO_USB 3//编码数据
#define AUDIO_MIC_MODE AUDIO_CODEC_TO_USB
//初始化
irq_enable();//使能总中断
usb_init_interrupt();//启用USB手动中断(在自动中断模式下,USB设备将是USB打印机设备)
usb_set_pin_en();//使能并上拉 1.5k usb的dp引脚
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);//设置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中断处理
if(usb_audio_mic_cnt)//发送数据
{
#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:
//初始化
irq_enable();//使能总中断
usb_init_interrupt();//启用USB手动中断(在自动中断模式下,USB设备将是USB打印机设备)
usb_set_pin_en();//使能并上拉 1.5k usb的dp引脚
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);//设置buffer
audio_usb_init(AUDIO_RATE_VAL);//设置速率
audio_set_sdm_output(GPIO_PB6_PB7,USB_IN,AUDIO_RATE_VAL,1);//设置sdm输出
audio_set_usb_output();//设置sdm输出
//main_loop
usb_handle_irq();//中断处理
if(usb_audio_speaker_cnt)
{
audio_rx_data_from_usb();//处理来自usb的数据
usb_audio_speaker_cnt=0;
}
cdc_app.c:
//初始化
irq_enable();//使能总中断
usb_init_interrupt();//启用USB手动中断(在自动中断模式下,USB设备将是USB打印机设备)
usb_set_pin_en();//使能并上拉 1.5k usb的dp引脚
//main_loop
if(usb_cdc_data_len!=0)
{
usb_cdc_tx_data_to_host(usb_cdc_data,usb_cdc_data_len);//发送数据给host
usb_cdc_data_len = 0;
}
测试现象
烧录程序进开发板:
mouse_app.c:
- 按下测试。
- 将开发板上的PD1接地后再拔出,会执行函数usbmouse_hid_report(USB_HID_MOUSE,mouse,4)。可以观测到桌面上的鼠标右键按下,鼠标光标向左下移动。
- 释放测试。
- 对PD2进行和PD1一样的操作,mouse数组清零,按键释放。
keyboard_app.c:
- 按下测试。
- 将开发板上的PD1接地后再拔出,会执行函数usbkb_hid_report_normal(0x10,kb_data)。会观测到下键一直被按下的效果。
- 释放测试。
- 对PD2进行和PD1一样的操作,特殊键与kb_data数组清零,按键释放。
mic_app.c:借助Audacity软件,麦克风选择Telink Audio16,扬声器选择pc扬声器。Device Mic收音,PC扬声器播放,如录音的人声能不失真的从扬声器播放出来,说明Mic工作正常。

spk_app.c:在Audacity软件中,麦克风选择pc麦克风,扬声器选择Telink Audio16,通过device SDM输出音频(3.5mm耳机插孔),如录音的人声能不失真的从耳机播放出来,说明speaker工作正常。

cdc_app.c:host首次识别成CDC设备需要手动安装telink_usb_driver_cdc.inf文件。会观测到将串口助手发送的内容返回。

Uart_fw_update
串口更新固件技术,使用Uart_fw_update例程可以有线更新固件,相关例程如下表:
| 例程 | 功能简介 |
|---|---|
| uart_fw_update_master | 用于搬运最新的固件 |
| uart_fw_update_slave | 用于接收更新的固件程序 |
| uart_fw_update_slave2 | 最新的固件程序且可再接收 |
用法步骤
uart_fw_update_master:
//main_loop
if (FW_UPDATE_MasterTrig)//触发串口升级固件并闪蓝灯
{
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低电压操作检测
if(!battery_power_check())
{
while(1)
{
gpio_toggle(RED_LED_PIN);
WaitMs(50);
}
}
#endif
FW_UPDATE_PHY_Init(FW_UPDATE_RxIrq);//串口协议初始化
FW_UPDATE_MasterInit(FW_UPDATE_MASTER_BIN_ADDR, FW_UPDATE_FW_VERSION);//串口升级固件初始化
while (1)
{
FW_UPDATE_MasterStart();//开始开始串口升级固件
ev_process_timer();//开启定时处理
}
}
gpio_toggle(GREEN_LED_PIN);//IDLE状态时亮的是绿灯
WaitMs(1000);
uart_fw_update_slave:
//main_loop
if (FW_UPDATE_SlaveTrig) {//触发串口升级固件并闪蓝灯
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低电压操作检测
{
while(1)
{
gpio_toggle(RED_LED_PIN);
WaitMs(50);
}
}
#endif
FW_UPDATE_PHY_Init(FW_UPDATE_RxIrq);//串口协议初始化
flash_read_page(Flash_Addr,Flash_Buff_Len,(unsigned char *)Flash_Read_Buff);
if(Flash_Read_Buff[0]==0x4b)//检测启动标志位
{
FW_UPDATE_SlaveInit(FW_UPDATE_SLAVE_BIN_ADDR, FW_UPDATE_FW_VERSION);//串口协议初始化
}
else
{
FW_UPDATE_SlaveInit(0, FW_UPDATE_FW_VERSION);//串口协议初始化
}
while (1) {
FW_UPDATE_SlaveStart();//开始开始串口升级固件
ev_process_timer();//开启定时处理
}
}
gpio_toggle(GREEN_LED_PIN);//IDLE状态时亮的是绿灯
WaitMs(1000);
uart_fw_update_slave2:
跟uart_fw_update_slave一样,除了IDLE状态时亮的是白灯而uart_fw_update_slave亮的是绿灯。
测试现象
(1) 首先编译烧录好master、slave以及待升级的固件,待升级的固件编译好后还需要配置调用bin_append.exe脚本编译待升级原生固件,其作用是在待升级固件之后追加计算好的TargetFwCRC结果,再输出一个新的固件作为真正的待升级固件,如图所示:


(2) 分别用两个板子 烧录SDK中的UART_FW_UPDATE_MASTER.bin作为master板以及 UART_FW_UPDATE _SLAVE.bin 作为slave板,在master端 0x20000的地址处烧录待升级的固件 UART_FW_UPDATE_SLAVE2 _NEW.bin,可以通过点击Telink BDT.exe上的setting选择烧录地址。
(3) 使用杜邦线连接Master、Slave,为串口通信做准备;demo中UART_TX(GPIO_PA2), UART_RX(GPIO_PD6),将slave板的PB1用串口模块与电脑连接,打开串口助手,比特率设为10000,方便后续查看打印信息。
(4) 将Master板和 Slave板上电,可以看到两块板子上的绿色LED慢闪,先按下Slave板上的SW1按键,触发Slave进入升级就绪状态,稍等片刻,待Slave板上蓝色LED快闪后,表示 Slave已进入升级状态,紧接着按下 Master板上的SW1按键,触发Master进入升级就绪状态,会看到Master板上蓝色LED也会闪烁一次,表示进入升级状态,随后升级过程开始,这个时候Slave端进行升级过程中会把新固件的每一分包的PktCRC通过串口打印出来,本次分包PktCRC结果会作为下一次的PktCRC计算的初始值,循环迭代直到接收到最后一包,会得到一个最终的PktCRC校验值,片刻后即可完。固件传输结束后,Slave端会对这个新的固件重新循环迭代进行CRC计算,将得到的CRC结果FwCRC与传输过程中的CRC最终结果PktCRC以及附加在bin文件后的TargetFwCRC做比较并且此时串口将三者以及当前启动地址与即将重启的启动地址打印出来,三者值一致则认为升级传输成功,固件完整,且Slave板上绿色LED快闪后白色慢闪, Master板上白色LED快闪后绿色慢闪,则表示更新成功。此时Slave上运行的是UART_FW_UPDATE_NEW_SLAVE.bin,所以就绪态时白色LED闪烁。若完成后,Slave板上依然绿色LED慢闪且Master板上红色LED闪烁,则表示OTA失败,OTA Slave板上仍然运行UART_FW_UPDATE_NEW_SLAVE.bin。
(5) 固件传输成功后串口打印如图:

(6) 固件传输成功后再重复升级固件,slave的升级固件启动地址会在0x00与0x40000/0x20000之间循环,是在0与0x20000还是0x40000之间循环,取决于代码中的宏控制,将待升级固件放置在那个启动地址,串口打印如图。

epd_driver_demo
epd_driver_demo是墨水屏的显示驱动例程,用户可以参考该例程进行开发应用。
用法步骤
//1.main_loop处理
GUI_Clear(data_buf, 1);//清屏
GUI_DispStr(data_buf, 6, 2, " ESL DEMO", 1);//在坐标(6,2)的地方横向输出" ESL DEMO"字符串
GUI_DispPic(data_buf, 220, 0, telink_log, 48, 128);//在坐标(220,0)的地方显示telink图标
GUI_DispStr(data_buf, 6, 4, "IEEE ADDR", 1);//在坐标(6,4)的地方横向输出"IEEE ADDR"字符串
GUI_DispStr(data_buf, 6, 6, "0X", 1);//在坐标(6,6)的地方横向输出"0X"字符串
unsigned char prompt_str[20];
unsigned char ieee_addr[]={0,0,0,0};
GUI_BytesToHexStr(ieee_addr, sizeof(ieee_addr), prompt_str);//将16进制数转为字符串存入prompt_str
GUI_DispStr(data_buf, 6+strlen("0X")*GUI_FONT_WIDTH, 6, prompt_str, 1);//在字符串"0X"后面显示字符串prompt_str
GUI_DispPic(data_buf, 0, 8, bar_code, 200, 64);//在坐标(0,8)的地方显示二维码
EPD_Init();//墨水屏初始化
EPD_Display(data_buf, 4736);//存入大小为4736byte的data_buf信息显示在屏幕上
EPD_Close();//关闭墨水屏
测试与现象
将此代码烧录进水墨屏的电路板可以看到水墨屏显示相应信息。

ble_beacon_tx
ble_beacon_tx例程是在2.4GHz工程里用来发ble广播beacon包的,用户可按需求开发应用。
用法步骤
//1.初始化设置
rf_irq_disable(FLD_RF_IRQ_ALL);//清除所有中断标志位
rf_irq_enable(FLD_RF_IRQ_TX);//使能tx中断标志位
irq_enable_type(FLD_IRQ_ZB_RT_EN); //使能rf中断标志位
irq_enable(); //使能中断
ble_adv_init(37, RF_POWER);//初始化ble的通信频点及通信强度
flash_read_page(CAP_VALUE,1,&cap);//读取cap值
if(cap!=0xff)
{
rf_update_internal_cap(cap);//更新cap值
}
//2.中断处理
if (rf_irq_src & FLD_RF_IRQ_TX)//当发送完成
{
rf_irq_clr_src(FLD_RF_IRQ_TX);//清除相应中断标志位
tx_done_flag = 1;//相应flag置1
tx_cnt++;//相应计数
}
irq_clr_src2(FLD_IRQ_ALL);//清除所有中断标志位
//3.main_loop处理
ble_adv_send(test_pdu, sizeof(test_pdu));//发送广播包
while(0 == tx_done_flag);//当发送完成
tx_done_flag = 0;
测试与现象
将代码烧录进开发板里,打开连接软件就可以搜索到相应广播包。
![]()
OTA
空中更新固件技术,使用ota例程可以无线更新固件,相关例程如下表:
| 例程 | 功能简介 |
|---|---|
| ota_master | 用于搬运最新的固件 |
| ota_slave | 用于接收更新的固件程序 |
| ota_slave2 | 最新的固件程序且可再接收 |
用法步骤
ota_master:
//main_loop
if (OTA_MasterTrig)//触发ota
{
OTA_MasterTrig = 0;
#if(BATT_CHECK_ENABLE)//flash低电压操作检测
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);//协议初始化
OTA_MasterInit(OTA_MASTER_BIN_ADDR, OTA_FW_VERSION);//ota初始化
gpio_write(BLUE_LED_PIN,1);//开始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();//开始ota
}
}
gpio_toggle(GREEN_LED_PIN);//IDLE状态时亮的是绿灯
WaitMs(1000);
ota_slave:
//main_loop
if (OTA_SlaveTrig) {//触发ota
OTA_SlaveTrig = 0;
#if(BATT_CHECK_ENABLE)//flash低电压操作检测
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);//协议初始化
flash_read_page(Flash_Addr,Flash_Buff_Len,(unsigned char *)Flash_Read_Buff);
if(Flash_Read_Buff[0]==0x4b)//检测启动标志位
{
OTA_SlaveInit(OTA_SLAVE_BIN_ADDR, OTA_FW_VERSION);//ota初始化
}
else
{
OTA_SlaveInit(0, OTA_FW_VERSION);//ota初始化
}
gpio_write(BLUE_LED_PIN,1);//开始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();//开始OTA
}
}
gpio_toggle(GREEN_LED_PIN);//IDLE状态时亮的是绿灯
WaitMs(1000);
ota_slave2:
跟ota_slave一样,除了IDLE状态时亮的是白灯而不是绿灯。
测试与现象
(1) 首先编译烧录好master、slave以及待升级的固件,待升级的固件编译好后还需要配置调用bin_append.exe脚本编译待升级原生固件,其作用是在待升级固件之后追加计算好的TargetFwCRC结果,再输出一个新的固件作为真正的待升级固件,如图所示:


(2) 通过Burning EVK分别将OTA_MASTER.bin和OTA_SLAVE.bin烧录到两块不同的开发板上,其中烧录OTA_MASTER.bin的为OTA Master,烧录OTA_SLAVE.bin的为OTA Slave。
(3) 将OTA待升级固件OTA_SLAVE2_NEW.bin烧录到OTA Master FLASH中0x20000开始的空间,可以通过点击Telink BDT.exe上的setting选择烧录地址。
(4) 使用杜邦线连接Master、Slave,为串口通信做准备;demo中UART_TX(GPIO_PA2),UART_RX(GPIO_PD6),将OTA slave板的PB1用串口模块与电脑连接,打开串口助手,比特率设为10000,方便后续查看打印信息。
(5) 将OTA Master板和OTA Slave板上电,可以看到两块板子上的绿色LED慢闪,先按下OTA Slave板上的SW1按键,触发OTA Slave进入OTA模式,稍等片刻,待OTA Slave板上蓝色LED闪烁一次后,表示OTA Slave已进入OTA模式,紧接着按下OTA Master板上的SW1按键,触发OTA Master进入OTA模式,会看到OTA Master板上蓝色LED也会闪烁一次,表示进入OTA模式,随后OTA过程开始,这个时候Slave端进行升级过程中会把新固件的每一分包的PktCRC通过串口打印出来,本次分包PktCRC结果会作为下一次的PktCRC计算的初始值,循环迭代直到接收到最后一包,会得到一个最终的PktCRC校验值,片刻后即可完。固件传输结束后,Slave端会对这个新的固件重新循环迭代进行CRC计算,将得到的CRC结果FwCRC与传输过程中的CRC最终结果PktCRC以及附加在bin文件后的TargetFwCRC做比较并且此时串口将三者以及当前启动地址与即将重启的启动地址打印出来,三者值一致则认为升级传输成功,且OTA Slave板上白色LED慢闪,OTA Master板上白色LED闪烁,则表示OTA更新成功。此时OTA Slave上运行的是OTA_SLAVE2_NEW.bin,所以是就绪态白色LED慢闪。若完成后,OTA Slave板上依然绿色LED慢闪且OTA Master板上红色LED闪烁,则表示OTA失败,OTA Slave板上仍然运行OTA_Slave.bin。
(6) 固件传输成功后串口打印如图:

(7) 固件传输成功后再重复升级固件,slave的升级固件启动地址会在0x00与0x40000/0x20000之间循环,是在0与0x20000还是0x40000之间循环,取决于代码中的宏控制,将待升级固件放置在那个启动地址,串口打印如图。

链路层介绍
数据包格式
灵活可变长包格式(TL721x/TL321x)。

传统既有9bit header可变长包包格式(TLSR8系列/B91/TL721x/TL321x)

传统既有定长包包格式(8系列/B91/TL721x/TL321x)

传统既有ble可变长包格式(8系列/B91)

Preamble
Preamble格式为0x55或0xAA。Sync Word紧跟在preamble后面,来决定这2个preamble哪一个被硬件选择:硬件层根据Sync Word的第一个传输比特来选择preamble格式,使得preamble的最后一个比特与Sync Word的第一个比特极性相反。preamble的长度是可编程的,范围是1 ~ 31个字节。
注意
- 31个字节是允许的最大长度,额外的长度用于调试,典型的应用程序只需要1 ~ 8个字节。
Sync Word
Sync Word是第二个数据包元素,并且在关联设备的网络中,所有设备通常共享一个公共的Sync Word。Sync Word的长度通过特定寄存器进行编程,范围通常是3 ~ 5个字节。Sync Word的内容也是通过特定寄存器而不是TX Packet Buffer进行配置。其他芯片支持同时搜索不只唯一一个Sync Word,而B85只支持搜索一个Sync Word。此外,还有一个寄存器用于设置Sync Word匹配阈值。在RX模式下的PHY's Sync Word同步过程中,一旦解调数据的匹配比特位数达到预设阈值,baseband会确定一个预期的Sync Word已被同步。换句话说,匹配阈值决定了Sync Word相关过程中PHY所能容忍的误码数量。注意,接收到的Sync Word没有存储在RX Packet Buffer中。B85没有专用的状态位或中断信号作为Sync Word匹配的指示,而RX DONE irq可以作为替代。无论CRC检查结果如何,在Sync Word匹配成功之后,接收到预设长度的payload时,就会产生RX DONE irq。
注意
- 当Sync Word匹配阈值小于Sync Word的长度时(即,允许Sync Word中有误码),CRC计算使用的是payload和“正确”的Sync Word,而不是实际接收到的Sync Word,虽然Sync Word通过了阈值,但仍可能存在误码。例如,对于0x11223344的Sync Word,接收到的Sync Word是通过阈值的0x00223344。CRC计算将使用0x11223344,而不是0x00223344。也就是说,即使Sync Word匹配阈值小于Sync Word的长度,CRC仍然可以识别payload字段中可能存在误码。
Header
传统既有9bit header可变长包包格式中的header如下图。header的长度为9 bits,包含6 bits长度的子字段,为payload字段的长度。

Payload Length: payload长度,单位为octet,最大长度为2^6-1 = 63 byte。
PID: 为Telink Proprietary TX/RX链路层保留,对Telink Generic FSK链路层无意义,此字段内容可由软件控制。
NO_ACK: 为Telink Proprietary TX/RX链路层保留,对Telink Generic FSK链路层无意义,此字段内容可由软件控制。
传统既有ble可变长包包格式中的header如下图。有两byte header,header0用户自定义,header1为payload长度。

灵活可变长包格式中的header如下图。

H0、H1:提供 MASK | MATCH | SZ 寄存器控制,赋值意义由客户软件决定,在tpll协议中会把PID与NO_ACK位放在该字段里。
-
MASK:指定MATCH寄存器的对应bit必须匹配才处理这一包,否则跳过后续处理。
-
MATCH:指定PACKET格式上需要匹配的内容。
-
SZ:指定H0/H1字段的长度,单位为octet,范围为0~16bit即最长为65535。
length:提供 SZ | ORD | ADJ 寄存器控制,赋值为payload的长度。
-
SZ:指定LENGTH的长度,单位为octet,范围为0~16bit即最长为65535。
-
ORD:指定LENGTH的比特序。
-
ADJ:指定LENGTH的计算方式,PAYLOAD_LEN + ADJ(偏移量)[-31 <= LENGTH_ADJ <=31]。
注意
- H0、H1、LENGTH的长度之和必须是8字节对齐,因此该包格式无法与9bit header的包格式进行互通。
Payload
Telink链路层有三种包格式。分别为灵活可变长包格式(目前只在TL721x/TL321x上可用)、传统既有9bit header可变长包包格式、传统既有定长包包格式。
在传统既有定长包的结构中,payload直接跟在Sync Word之后,而在变长payload包中,它跟在Header字段之后。在传输过程中,payload通过LSByte、MSBit先行规则进行传输。在接收过程中,payload也是通过LSByte、MSBit先行规则进行接收。对于TX和RX来说,在射频收发器和链路层之间通过DMA模块来交换payload。对于TX,必须在SRAM中定义一个专用的TX Packet Buffer(即unsigned char元素类型的数组),其格式如下所示。

在PHY层开始传输前,必须将dma size、header、payload写入TX Packet Buffer,注意在9系列中tx buffer中的dma size部分需要将实际size经过换算接口填入。
对于RX,必须在SRAM中定义一个专用的RX Packet Buffer(即unsigned char元素类型的数组),其格式如下:




需要注意的是,对于定长payload包,在开启PHY接收之前,必须通过特定寄存器预设一个期望的payload长度。一旦Sync Word被成功关联,链路层控制器将加载预设长度的payload以及RX信息到RX Packet buffer中,然后立即产生RX packet irq。对于变长payload包,链路层控制器根据包含在Header字段中的payload长度子字段来确定payload长度。
CRC
CRC是数据包中的检错机制,它紧跟着payload。不同芯片crc配置不同可参考如下表格:
| 芯片 | 计算范围 | 长度范围 | 初始值 | 多项式 | 比特序 |
|---|---|---|---|---|---|
| 8系列/B91 | sync word ~ payload (ble包格式只算header+payload) | 0~3byte | 固定0xff/0xffff(ble包格式可自定义) | 固定(1bye:X^8 + X^2 + X + 1)(2byte:X^16 + X^12 + X^5 + 1) | 不可调 |
| TL721x/TL321x | sync word ~ payload(可选择起始计算位置,ble包格式只算header+payload) | 0~4byte | 自定义 | 自定义 | 可调 |
RSSI
Telink链路层有两种类型的RSSI。一种是RX数据包的RSSI,一旦收到数据包后,就会存储在RX Packet Buffer中,其值等于Sync Word被识别成功时的RSSI瞬间值加上110。另一种是给定信道的实时RSSI。如果收发器在给定信道上处于RX状态,则可以从特定寄存器中读出该信道的实时功率测量值,该值减去110即为该信道的实时RSSI。实时RSSI通常用来判断给定信道是否繁忙。
Data Rate
Telink链路层控制器提供4个比特率选项,2Mbps、1Mbps、500Kbps和250Kbps,可通过特定寄存器进行编程。
Timebase
Telink链路层控制器可以使用以16MHz(9系列24M)速率运行的32-bit多功能系统定时器,来调度其所有的TX和RX活动,低功耗模式(suspend/deep sleep)期间除外。时钟源为高精度24MHz外置的晶体振荡器。以16MHz速率运行的32-bit系统定时器大约每268.4秒滚动(环绕)一次。系统定时器的计数值可以随时通过相应的API读取和更改。
B85中提供了两个32-bit定时器比较寄存器T1_CMP和T2_CMP。当系统定时器达到T1_CMP值时,链路层控制器可以自动开启收发操作。同样,一旦系统定时器达到T2_CMP值,链路层控制器就可以自动停止收发操作。为了方便用户使用,这里提供了6种自动收发操作模式,分别由各自的硬件状态机实现:Single TX、Single RX、Single TX to RX、Single RX to TX、PTX和PRX,前四种为Telink Generic FSK链路层,后两种为Telink Proprietary TX/RX链路层。
Telink Generic FSK链路层(genfsk_ll)
Single TX
在该模式中,仅涉及T1_CMP,即在系统定时器达到T1_CMP值时开始传输准备好的数据包。一旦传输完成,就会产生TX DONE irq。

Single RX
在该模式中,T1_CMP和T2_CMP都会涉及到。当系统定时器达到T1_CMP值时,收发器进入RX状态,开始搜索预期的Sync Word。如果在系统定时器达到T2_CMP值之前未成功关联Sync Word,则会产生RX FIRST TIMEOUT irq,收发器回到IDLE状态。如果在系统定时器达到T2_CMP值之前收到数据包,则产生RX DONE irq,收发器就回到IDLE状态。也可以禁用T2_CMP,这意味着收发器将始终处于RX状态,直到收到数据包。

Single TX to RX
在该模式中,涉及到两个比较定时器。在系统定时器达到T1_CMP值时开始发送准备好的数据包。发送完成后,产生TX DONE irq,收发器等待片刻(这个等待时间是可编程的),然后进入RX状态。如果在系统定时器达到T2_CMP值之前未成功关联Sync Word,则会产生RX TIMEOUT irq,收发器回到IDLE状态。如果在T2_CMP匹配之前收到数据包,则产生RX DONE irq,收发器回IDLE状态。该自动模式适用于发送数据包后需要ACK或响应数据包的场景。

Single RX to TX
在该模式下,收发器在T1_CMP匹配时首先进入RX状态,等待数据包到达。如果在系统定时器到达T2_CMP值前都没有收到包,则会产生RX FIRST TIMEOUT irq,收发器回到IDLE状态。如果数据包在T2_CMP匹配之前到达,则产生RX DONE irq,收发器等待片刻(这个等待时间是可编程的),然后转换为TX状态并开始传输准备好的数据包。该自动模式适用于接收到数据包后需要发送ACK或响应数据包的情况。

中断描述
Telink通用FSK链路层控制器共有六个相关的中断信号:TX DONE、RX DONE、RX FIRST TIMEOUT、RX TIMEOUT、PKT_MATCH、PKT_UNMATCH。
TX DONE
每完成一次数据包的传输,就会产生一个TX DONE irq。
RX DONE
无论CRC校验结果如何,一旦接收到数据包,就会产生一个RX DONE irq,这意味着只要Sync Word匹配,就会在接收到整个数据包后产生一个RX DONE irq。
RX FIRST TIMEOUT
当系统定时器达到T2_CMP值时,RX FIRST TIMEOUT irq产生在上述的两种自动模式Single RX和Single RX to TX中。
RX TIMEOUT
当系统定时器达到T2_CMP值时,RX TIMEOUT irq仅产生在Single TX to RX模式。
PKT_MATCH
只要从match开始到match结束的字段中任何位置有符合匹配的字段则产生PKT_MATCH中断。
PKT_UNMATCH
从match开始到match结束的字段中任何位置都没有符合匹配的字段或者没有需要匹配的字段即MASK寄存器为0则产生PKT_UNMATCH中断。
Telink Proprietary 链路层 (tpll)
PRX
在该模式下,以8系列芯片寄存器为例,假设在进入PRX RX模式之前已经完成了所有与RF相关的配置,并且通过向寄存器"0xf00"写入"0x84"来触发RF实施接收。
首先,状态机进入RX Settle阶段,等待RF RX Settle阶段的PLL趋于稳定(至少需要85us)。可通过寄存器0xf04<7:0>和0xf05<3:0>配置“RX Settle”的持续时间(单位:us)。RX Settle阶段结束后,状态机进入实际RX阶段。
实际RX阶段是数据包接收的实际阶段,其持续时间是可配置的。
-
当寄存器0xf03<2>设置为1'b1时,实际RX阶段的持续时间等于RX Time减去RX Settle阶段的持续时间,可通过寄存器0xf0a和0xf0b配置RX时间(单位:us)。
-
当寄存器0xf03<2>被清零时,状态机将停留在实际RX阶段,直到收到数据包进入下一个阶段。
当RX阶段收到一个数据包时,状态机会判断这个数据包是否为重复数据包。
-
如果是重复数据包,则不会存储在相关寄存器中。
-
如果不是重复数据包,射频硬件会解析数据包Header的Packet Control字段中的“NO _ ACK”位,并对该位和本地0xf15<5>位进行XOR操作。
如果XOR操作结果为1,则认为对方不需要ACK,状态机将返回RX Settle阶段等待接收下一个数据包。
如果XOR操作结果为0,则认为对方需要ACK,状态机会向对方发送一个ACK数据包。
当状态机需要应答ACK时,首先等待“TX wait”的持续时间(单位:us),这个时间可通过寄存器0xf0e<7:0>和0xf0f<3:0>进行配置。然后状态机进入TX Settle阶段,以便等待TX阶段的PLL稳定(至少需要112.5us)。TX Settle阶段的持续时间可通过寄存器0xf04<7:0>和0xf05<3:0>进行配置。
TX Settle阶段结束后,状态机进入实际TX阶段发送ACK。实际TX阶段的持续时间由ACK数据包的长度决定。
发送ACK数据包后,状态机等待“RX wait”的持续时间(单位:us),这个时间可通过寄存器0xf06<7:0>和0xf07<3:0>配置,然后返回到RX Settle阶段接收下一个数据包。
下图显示了状态机的PRX时序流程。
PTX
在该模式下,假设在进入PTX TX模式之前已经完成了所有的配置,并且TX缓冲区已经填充,然后通过将"0x83"写入寄存器"0xf00"来触发RF实施传输。
首先,状态机等待通过寄存器0xf04<7:0>和0xf05<3:0>配置的TX Settle阶段,以便RF TX Settle阶段的PLL趋于稳定(至少需要112.5us)(单位:us)。
然后状态机进入实际TX阶段,RF将在空中发送数据包。数据包传输的持续时间由数据包长度决定。
数据包传输结束后,首先RF状态机会计算寄存器0xf15<5>和0x404<6>的XOR结果,然后据此判断是否需要等待对方的ACK。
-
当XOR结果为1时,状态机将认为不需要等待来自对方的ACK,从而结束当前数据包传输,等待下一次传输被触发。
-
当XOR结果为0时,状态机将认为需要等待对方的ACK。状态机将等待通过寄存器0xf06<7:0>和0xf07<3:0>配置的“RX Wait”时间(单位:us),然后切换到RX模式,等待PRX从RX切换到TX并响应ACK。
PTX将在通过寄存器0xf0a<7:0>和0xf0b<3:0>配置的“RX Time”内保持在RX模式(单位:us),等待对方回应ACK。
注意
- 在PRX模式的“RX Settle”期间(通过0xf0c<7:0>和0xf0d<11:8>配置,单位:us),状态机保持在RX Settle状态,等待RF PLL变得稳定(至少需要85us)。在RX Settle阶段,由于实际上未启用RF RX功能,无法接收任何即将到来的数据包,即RX时间内的有效接收周期为实际RX阶段。
如果在实际RX阶段接收到ACK数据包,则完成当前数据包的传输。
如果在RX时间到期时没有收到对方的ACK,PTX将尝试重传。首先判断重试次数是否达到最大值:若未达到,则执行“retry cnt++”。然后等待通过0xf10<7:0>和0xf11<3:0>配置的“ARD”持续时间(自动重传延迟,单位:us)。
然后状态机等待“TX Settle”的持续时间,进入TX模式,发送数据包,等待“RX Wait”的持续时间,进入RX,然后等待ACK。状态机重复该流程,直到重试次数达到最大值。
下图显示了状态机的PTX时序流程。

中断描述
对于PTX端,每次发送数据包时都会触发TX中断。用户可以通过检查标志位来判断数据包是否发送成功。
如果PTX启用ACK,则当接收到来自PRX的ACK时,适用以下两种情况:
-
如果PRX响应的是空ACK,PTX将只触发TX_DS中断。
-
如果PRX响应的是一个带有有效载荷的ACK,PTX将产生RX_DR和TX_DS中断。
如果PTX在指定的持续时间内没有接收到任何ACK,它将尝试重传同一个数据包,直到接收到ACK或者重试次数达到预设的阈值。如果PTX在重试次数达到阈值后没有收到任何ACK,则会产生RETRY_HIT中断。
对于PRX端,每次接收到数据包都会产生RX中断,但只有当接收到的数据包具有正确的CRC结果且Payload_Len不等于0时,才能产生RX_DR中断。如果是带有Payload的ACK模式,则从接收第二个数据包开始就会产生TX_DS中断。
如果PRX启用ACK,在一次传输中产生TX和RX中断,但不产生RX_DR中断。原因可能如下:
-
收到有错误CRC结果的数据包。
-
收到Payload_Len等于0的空包。
-
收到与前一个数据包具有相同PID的数据包。
注意
- 当接收到的数据包PID既不是local_pid也不是local_pid + 1时,PTX和PRX都会产生INVALID_PID中断。PTX\PRX在连续接收两个CRC结果错误的数据包时会产生CRC_2中断。
- PRX可配置为连续接收数据包或仅在指定持续时间内接收数据包。如果PRX配置为在特定的持续时间内接收数据包,而在该持续时间内没有接收到任何数据包,则会产生RX_TIMEOUT中断。
传输图
非空ACK的正常通信过程

空ACK的正常通信过程

丢失数据包的单独通信过程

丢失ACK包的单独通信过程

具有最大重传次数的多个通信过程
在该场景中,预设ARC为2。如果PTX在重试次数达到阈值(ARC = 2)后没有收到任何ACK,则会产生RETRY_HIT中断。

经典通信过程
在通信过程中,收发器会出现产生TX和RX中断但不产生RX_DR中断的情况。出现这种现象的原因是非唯一性的,主要由以下问题造成:
-
收到有错误CRC结果的数据包。
-
收到没有payload(payload_len = 0)的数据包。
-
收到的数据包PID与本地PID相同。

注意
- 通常情况下,Telink PTX只有在丢失ACK数据包时才会连续发送同一PID的数据包。
tpll数据包格式示例
PTX TX缓冲区的数据包格式(payload len=8)
例:
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
数据包数据的描述:
-
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缓冲区的数据包格式(payload len=8)
例:
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
数据包数据的描述:
-
rx_packet[0]: 0x13(收到并传输到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