开发板提供了丰富的开发例程,经认真阅读,在3. TK8620 开发板\3. TKB-620 SDK 代码\STM32 ( 开发板底板MCU) Example\TKB-620\Projects\p2p_demo下在stm32f103的示例代码

打开工程后,可以看到工程结构如下:

在工程中RF_API为模块的SDK,下面有四个文件,为项目APP下的文件,在文档中,如果移植到其他的开发板,只需要把这四个文件拿过去就行。
再看这块底板的外设驱动,有key、oled两个驱动。
在key.c+app_key中,实现了一个简单的key的状态机,用于获取5个按键的状态。同时在KeyInfoUpdate中,实现各个按键的功能,实现了上下左右以及确认五个状态机的实现。
uint8_t KeyInfoUpdate(void)
{
uint8_t keyName = keyDetect();
WORCfg wORCfg = {4, 0, 473200000, 1000};
SleepCfg sleepCfg = {
.wakeUpSrc = WAKEUP_WIRELESS,
.wORCfg = {4, 0, 473200000, 1000},
.wakeUpCb = {0, 3},
};
sys_param.screen_refresh = 0;
switch (keyName) {
case KEY_UP:
if (sys_param.rowSelected && (sys_param.screenIdx == 0)) {
sys_param.rowIdx--;
sys_param.rowIdx %= 8;
printf("row:%d\r\n", sys_param.rowIdx);
} else {
sys_param.rowIdx -= SHOW_LINE_MAX;
sys_param.screenIdx = (sys_param.rowIdx / SHOW_LINE_MAX) % SHOW_SCREEN_MAX;
printf("row:%d\r\n", sys_param.rowIdx);
}
break;
case KEY_DOWN:
if (sys_param.rowSelected && (sys_param.screenIdx == 0)) {
sys_param.rowIdx++;
sys_param.rowIdx %= 8;
printf("row:%d\r\n", sys_param.rowIdx);
} else {
sys_param.rowIdx += SHOW_LINE_MAX;
printf("row:%d\r\n", sys_param.rowIdx);
sys_param.screenIdx = (sys_param.rowIdx / SHOW_LINE_MAX) % SHOW_SCREEN_MAX;
}
break;
case KEY_LEFT:
case KEY_RIGHT:
if (sys_param.rowSelected) {
if (KEY_LEFT == keyName) {
ParamUpdate(0);
} else if (KEY_RIGHT == keyName) {
ParamUpdate(1);
}
if (!sys_param.screenIdx && ((sys_param.rowIdx % SHOW_LINE_MAX) < 3)) {
#if 1
RF_InitAsyncP2p();
#else
ret = Tk86xxSetSlot(0, 1, RF_slotCfg);
if (API_SUCCESS != ret) {
printf("set slot fail:%d\r\n", ret);
}
ret = Tk86xxOpenRadio();
if (API_SUCCESS != ret) {
printf("open radio fail:%d\r\n", ret);
}
#endif
}
}
break;
case KEY_ENTER:
if (sys_param.screenIdx == 0) {
sys_param.rowSelected = sys_param.rowSelected ^ 0x01;
if (!sys_param.rowSelected) {
if ((sys_param.rowIdx % SHOW_LINE_MAX) == 4) {
if (1 == sys_param.worType) {
wORCfg.freq = RF_slotCfg[0].freq;
if (API_SUCCESS != Tk86xxWakeUp(&wORCfg)) {
printf("fail too wakeup\r\n");
}
} else if (2 == sys_param.worType) {
sleepCfg.wORCfg.freq = RF_slotCfg[0].freq;
if (API_SUCCESS != Tk86xxSleep(&sleepCfg)) {
printf("fail to sleep\r\n");
DelayMs(300);
} else {
sys_param.rx_count = 0;
}
}
}
sys_param.txSentCnt = 0;
sys_param.rx_count = 0;
}
}
break;
case KEY_SEND:
if (sys_param.txTargetCnt) {
TimerInit(TIM3, &(TimerInitCfg){.psc = 36000, .arr = 2000 * SEND_TIMEOUT, .mode = TIMER_CNTMODE_ONESHOT});
} else {
RF_SendDataOnce();
}
break;
default:
break;
}
if (keyName != KEY_NONE) {
sys_param.screen_refresh = 1;
printf("%s, page:%d\r\n", keyNameStr[keyName], sys_param.screenIdx);
}
sys_param.key_name = keyName;
return keyName;
}
同时,通过oled.c+app_show.c实现了二级菜单,用于各种功能与用户的交互。
在main.c中通过菜单的刷新,按键的状态获取。同时使用Tk86xxCheckStatus来检查模块的状态。
最后主动的信息发送为RF_SendDataOnce,来实现发送一组数据。
void RF_SendDataOnce(void)
{
APIRet ret = API_SUCCESS;
sys_param.txSentCnt++;
RF_slotCfg[0].slotState = SLOT_TX;
ret = Tk86xxSetSlot(0, 1, RF_slotCfg);
if (API_SUCCESS != ret) {
printf("set slot fail:%d\r\n", ret);
}
ret = Tk86xxSendData(0, sendData, strlen((char *)sendData) + 3);
if (API_SUCCESS != ret) {
printf("send data fail:%d\r\n", ret);
} else {
printf("send: %s, len:%d\r\n", (char *)sendData, strlen((char *)sendData));
}
ret = Tk86xxOpenRadio();
if (API_SUCCESS != ret) {
printf("open radio fail:%d\r\n", ret);
}
}
在这个函数中,这现了通过模块发送一个9个字节的功能,如果我们需要向对应的节点发送数,就只需要在这个函数中包装数据就行了。
在接收回调函数中PhyIrqHandler(Status *phyStatus),
如果状态机为RX_DONE,则对接收数据包进行解析:
rcvdLen = Tk86xxRcvData(rcvdData, BUFLEN, &sigQuality);
sys_param.rssi = sigQuality.rssi;
sys_param.snr = sigQuality.snr;
if (!strncmp((char *)rcvdData, (char *)sendData, strlen((char *)sendData))) sys_param.rx_count++;
if (sys_param.rx_count > *(uint16_t *)&rcvdData[9]) sys_param.rx_count = *(uint16_t *)&rcvdData[9];
sys_param.rxPktLossRate = 100 - (((float)sys_param.rx_count / *(uint16_t *)&rcvdData[9]) * 100);
sys_param.screen_refresh = 1;
if (rcvdLen) printf("%s\r\n", rcvdData);
那么我们接收数据就可以在这个函数中实现自己的功能代码。
【总结】
经过上述代码的解读,理清楚了数据收发的关键点,下次我将读取数据,并进行组装,在接收端,也可以解析数据进行接收的处理。