mirror of
https://github.com/OpenEPaperLink/OpenEPaperLink.git
synced 2026-03-21 05:06:39 +01:00
Added SubGHz Alpha
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
#include "led.h"
|
||||
#include "proto.h"
|
||||
#include "radio.h"
|
||||
#include "subGhz.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "driver/uart.h"
|
||||
#include "esp_err.h"
|
||||
@@ -39,8 +40,8 @@ uint16_t version = 0x0019;
|
||||
|
||||
#define RAW_PKT_PADDING 2
|
||||
|
||||
uint8_t radiotxbuffer[128];
|
||||
uint8_t radiorxbuffer[128];
|
||||
uint8_t radiotxbuffer[135];
|
||||
uint8_t radiorxbuffer[135];
|
||||
|
||||
static uint32_t housekeepingTimer;
|
||||
|
||||
@@ -69,8 +70,8 @@ uint8_t curNoUpdate = 0;
|
||||
|
||||
bool highspeedSerial = false;
|
||||
|
||||
void sendXferCompleteAck(uint8_t *dst);
|
||||
void sendCancelXfer(uint8_t *dst);
|
||||
void sendXferCompleteAck(uint8_t *dst, bool isSubGHz);
|
||||
void sendCancelXfer(uint8_t *dst, bool isSubGHz);
|
||||
void espNotifyAPInfo();
|
||||
|
||||
// tools
|
||||
@@ -115,7 +116,7 @@ uint8_t getBlockDataLength() {
|
||||
// pendingdata slot stuff
|
||||
int8_t findSlotForMac(const uint8_t *mac) {
|
||||
for (uint8_t c = 0; c < MAX_PENDING_MACS; c++) {
|
||||
if (memcmp(mac, ((uint8_t *)&(pendingDataArr[c].targetMac)), 8) == 0) {
|
||||
if (memcmp(mac, ((uint8_t *) & (pendingDataArr[c].targetMac)), 8) == 0) {
|
||||
if (pendingDataArr[c].attemptsLeft != 0) {
|
||||
return c;
|
||||
}
|
||||
@@ -133,7 +134,7 @@ int8_t findFreeSlot() {
|
||||
}
|
||||
int8_t findSlotForVer(const uint8_t *ver) {
|
||||
for (uint8_t c = 0; c < MAX_PENDING_MACS; c++) {
|
||||
if (memcmp(ver, ((uint8_t *)&(pendingDataArr[c].availdatainfo.dataVer)), 8) == 0) {
|
||||
if (memcmp(ver, ((uint8_t *) & (pendingDataArr[c].availdatainfo.dataVer)), 8) == 0) {
|
||||
if (pendingDataArr[c].attemptsLeft != 0) return c;
|
||||
}
|
||||
}
|
||||
@@ -431,7 +432,7 @@ void espNotifyTagReturnData(uint8_t *src, uint8_t len) {
|
||||
}
|
||||
|
||||
// process data from tag
|
||||
void processBlockRequest(const uint8_t *buffer, uint8_t forceBlockDownload) {
|
||||
void processBlockRequest(const uint8_t *buffer, uint8_t forceBlockDownload, bool isSubGHz) {
|
||||
struct MacFrameNormal *rxHeader = (struct MacFrameNormal *)buffer;
|
||||
struct blockRequest *blockReq = (struct blockRequest *)(buffer + sizeof(struct MacFrameNormal) + 1);
|
||||
if (!checkCRC(blockReq, sizeof(struct blockRequest))) return;
|
||||
@@ -448,7 +449,7 @@ void processBlockRequest(const uint8_t *buffer, uint8_t forceBlockDownload) {
|
||||
} else {
|
||||
// we're talking to another mac, let this mac know we can't accomodate another request right now
|
||||
pr("BUSY!\n");
|
||||
sendCancelXfer(rxHeader->src);
|
||||
sendCancelXfer(rxHeader->src, isSubGHz);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -456,7 +457,7 @@ void processBlockRequest(const uint8_t *buffer, uint8_t forceBlockDownload) {
|
||||
// check if we have data for this mac
|
||||
if (findSlotForMac(rxHeader->src) == -1) {
|
||||
// no data for this mac, politely tell it to fuck off
|
||||
sendCancelXfer(rxHeader->src);
|
||||
sendCancelXfer(rxHeader->src, isSubGHz);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -512,7 +513,7 @@ void processBlockRequest(const uint8_t *buffer, uint8_t forceBlockDownload) {
|
||||
|
||||
addCRC((void *)blockRequestAck, sizeof(struct blockRequestAck));
|
||||
|
||||
radioTx(radiotxbuffer);
|
||||
radioTx(radiotxbuffer, isSubGHz);
|
||||
|
||||
// save the target for the blockdata
|
||||
memcpy(dstMac, rxHeader->src, 8);
|
||||
@@ -525,7 +526,7 @@ void processBlockRequest(const uint8_t *buffer, uint8_t forceBlockDownload) {
|
||||
}
|
||||
}
|
||||
|
||||
void processAvailDataReq(uint8_t *buffer) {
|
||||
void processAvailDataReq(uint8_t *buffer, bool isSubGHz) {
|
||||
struct MacFrameBcast *rxHeader = (struct MacFrameBcast *)buffer;
|
||||
struct AvailDataReq *availDataReq = (struct AvailDataReq *)(buffer + sizeof(struct MacFrameBcast) + 1);
|
||||
|
||||
@@ -562,13 +563,13 @@ void processAvailDataReq(uint8_t *buffer) {
|
||||
txHeader->fcs.srcAddrType = 3;
|
||||
txHeader->seq = seq++;
|
||||
addCRC(availDataInfo, sizeof(struct AvailDataInfo));
|
||||
radioTx(radiotxbuffer);
|
||||
radioTx(radiotxbuffer, isSubGHz);
|
||||
memset(lastAckMac, 0, 8); // reset lastAckMac, so we can record if we've received exactly one ack packet
|
||||
espNotifyAvailDataReq(availDataReq, rxHeader->src);
|
||||
}
|
||||
void processXferComplete(uint8_t *buffer) {
|
||||
void processXferComplete(uint8_t *buffer, bool isSubGHz) {
|
||||
struct MacFrameNormal *rxHeader = (struct MacFrameNormal *)buffer;
|
||||
sendXferCompleteAck(rxHeader->src);
|
||||
sendXferCompleteAck(rxHeader->src, isSubGHz);
|
||||
if (memcmp(lastAckMac, rxHeader->src, 8) != 0) {
|
||||
memcpy((void *)lastAckMac, (void *)rxHeader->src, 8);
|
||||
espNotifyXferComplete(rxHeader->src);
|
||||
@@ -577,7 +578,7 @@ void processXferComplete(uint8_t *buffer) {
|
||||
}
|
||||
}
|
||||
|
||||
void processTagReturnData(uint8_t *buffer, uint8_t len) {
|
||||
void processTagReturnData(uint8_t *buffer, uint8_t len, bool isSubGHz) {
|
||||
struct MacFrameBcast *rxframe = (struct MacFrameBcast *)buffer;
|
||||
struct MacFrameNormal *frameHeader = (struct MacFrameNormal *)(radiotxbuffer + 1);
|
||||
|
||||
@@ -592,13 +593,13 @@ void processTagReturnData(uint8_t *buffer, uint8_t len) {
|
||||
radiotxbuffer[2] = 0xCC; // normal frame
|
||||
frameHeader->seq = seq++;
|
||||
frameHeader->pan = rxframe->srcPan;
|
||||
radioTx(radiotxbuffer);
|
||||
radioTx(radiotxbuffer, isSubGHz);
|
||||
|
||||
espNotifyTagReturnData(rxframe->src, len - (sizeof(struct MacFrameBcast) + 1));
|
||||
}
|
||||
|
||||
// send block data to the tag
|
||||
void sendPart(uint8_t partNo) {
|
||||
void sendPart(uint8_t partNo, bool isSubGHz) {
|
||||
struct MacFrameNormal *frameHeader = (struct MacFrameNormal *)(radiotxbuffer + 1);
|
||||
struct blockPart *blockPart = (struct blockPart *)(radiotxbuffer + sizeof(struct MacFrameNormal) + 2);
|
||||
memset(radiotxbuffer + 1, 0, sizeof(struct blockPart) + sizeof(struct MacFrameNormal));
|
||||
@@ -616,9 +617,9 @@ void sendPart(uint8_t partNo) {
|
||||
frameHeader->fcs.srcAddrType = 3;
|
||||
frameHeader->seq = seq++;
|
||||
frameHeader->pan = dstPan;
|
||||
radioTx(radiotxbuffer);
|
||||
radioTx(radiotxbuffer, isSubGHz);
|
||||
}
|
||||
void sendBlockData() {
|
||||
void sendBlockData(bool isSubGHz) {
|
||||
if (getBlockDataLength() == 0) {
|
||||
pr("Invalid block request received, 0 parts..\n");
|
||||
requestedData.requestedParts[0] |= 0x01;
|
||||
@@ -639,13 +640,13 @@ void sendBlockData() {
|
||||
while (partNo < BLOCK_MAX_PARTS) {
|
||||
for (uint8_t c = 0; (c < BLOCK_MAX_PARTS) && (partNo < BLOCK_MAX_PARTS); c++) {
|
||||
if (requestedData.requestedParts[c / 8] & (1 << (c % 8))) {
|
||||
sendPart(c);
|
||||
sendPart(c, isSubGHz);
|
||||
partNo++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
void sendXferCompleteAck(uint8_t *dst) {
|
||||
void sendXferCompleteAck(uint8_t *dst, bool isSubGHz) {
|
||||
struct MacFrameNormal *frameHeader = (struct MacFrameNormal *)(radiotxbuffer + 1);
|
||||
memset(radiotxbuffer + 1, 0, sizeof(struct blockPart) + sizeof(struct MacFrameNormal));
|
||||
radiotxbuffer[sizeof(struct MacFrameNormal) + 1] = PKT_XFER_COMPLETE_ACK;
|
||||
@@ -658,9 +659,9 @@ void sendXferCompleteAck(uint8_t *dst) {
|
||||
frameHeader->fcs.srcAddrType = 3;
|
||||
frameHeader->seq = seq++;
|
||||
frameHeader->pan = dstPan;
|
||||
radioTx(radiotxbuffer);
|
||||
radioTx(radiotxbuffer, isSubGHz);
|
||||
}
|
||||
void sendCancelXfer(uint8_t *dst) {
|
||||
void sendCancelXfer(uint8_t *dst, bool isSubGHz) {
|
||||
struct MacFrameNormal *frameHeader = (struct MacFrameNormal *)(radiotxbuffer + 1);
|
||||
memset(radiotxbuffer + 1, 0, sizeof(struct blockPart) + sizeof(struct MacFrameNormal));
|
||||
radiotxbuffer[sizeof(struct MacFrameNormal) + 1] = PKT_CANCEL_XFER;
|
||||
@@ -673,9 +674,9 @@ void sendCancelXfer(uint8_t *dst) {
|
||||
frameHeader->fcs.srcAddrType = 3;
|
||||
frameHeader->seq = seq++;
|
||||
frameHeader->pan = dstPan;
|
||||
radioTx(radiotxbuffer);
|
||||
radioTx(radiotxbuffer, isSubGHz);
|
||||
}
|
||||
void sendPong(void *buf) {
|
||||
void sendPong(void *buf, bool isSubGHz) {
|
||||
struct MacFrameBcast *rxframe = (struct MacFrameBcast *)buf;
|
||||
struct MacFrameNormal *frameHeader = (struct MacFrameNormal *)(radiotxbuffer + 1);
|
||||
radiotxbuffer[sizeof(struct MacFrameNormal) + 1] = PKT_PONG;
|
||||
@@ -687,9 +688,10 @@ void sendPong(void *buf) {
|
||||
radiotxbuffer[2] = 0xCC; // normal frame
|
||||
frameHeader->seq = seq++;
|
||||
frameHeader->pan = rxframe->srcPan;
|
||||
radioTx(radiotxbuffer);
|
||||
radioTx(radiotxbuffer, isSubGHz);
|
||||
}
|
||||
|
||||
extern uint8_t mSelfMac[8];
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
@@ -710,10 +712,20 @@ void setup() {
|
||||
housekeepingTimer = millis();
|
||||
}
|
||||
|
||||
bool isSubGhzRx = false;
|
||||
void loop() {
|
||||
while ((millis() - housekeepingTimer) < ((1000 * HOUSEKEEPING_INTERVAL) - 100)) {
|
||||
int8_t ret = commsRxUnencrypted(radiorxbuffer);
|
||||
int8_t ret = commsRxUnencrypted(radiorxbuffer, &isSubGhzRx);
|
||||
if (ret > 1) {
|
||||
if (0)
|
||||
{
|
||||
Serial.printf("RXed packet len %u :", ret);
|
||||
for (int t = 0; t < ret; t++) {
|
||||
Serial.printf(" %02x", radiorxbuffer[t]);
|
||||
}
|
||||
Serial.printf("\n");
|
||||
}
|
||||
|
||||
led_flash(0);
|
||||
// received a packet, lets see what it is
|
||||
switch (getPacketType(radiorxbuffer)) {
|
||||
@@ -722,23 +734,23 @@ void loop() {
|
||||
// old version of the AvailDataReq struct, set all the new fields to zero, so it will pass the CRC
|
||||
memset(radiorxbuffer + 1 + sizeof(struct MacFrameBcast) + sizeof(struct oldAvailDataReq), 0,
|
||||
sizeof(struct AvailDataReq) - sizeof(struct oldAvailDataReq) + 2);
|
||||
processAvailDataReq(radiorxbuffer);
|
||||
processAvailDataReq(radiorxbuffer, isSubGhzRx);
|
||||
} else if (ret == 40) {
|
||||
// new version of the AvailDataReq struct
|
||||
processAvailDataReq(radiorxbuffer);
|
||||
processAvailDataReq(radiorxbuffer, isSubGhzRx);
|
||||
}
|
||||
break;
|
||||
case PKT_BLOCK_REQUEST:
|
||||
processBlockRequest(radiorxbuffer, 1);
|
||||
processBlockRequest(radiorxbuffer, 1, isSubGhzRx);
|
||||
break;
|
||||
case PKT_BLOCK_PARTIAL_REQUEST:
|
||||
processBlockRequest(radiorxbuffer, 0);
|
||||
processBlockRequest(radiorxbuffer, 0, isSubGhzRx);
|
||||
break;
|
||||
case PKT_XFER_COMPLETE:
|
||||
processXferComplete(radiorxbuffer);
|
||||
processXferComplete(radiorxbuffer, isSubGhzRx);
|
||||
break;
|
||||
case PKT_PING:
|
||||
sendPong(radiorxbuffer);
|
||||
sendPong(radiorxbuffer, isSubGhzRx);
|
||||
break;
|
||||
case PKT_AVAIL_DATA_SHORTREQ:
|
||||
// a short AvailDataReq is basically a very short (1 byte payload) packet that requires little preparation on the tx side, for optimal
|
||||
@@ -746,11 +758,11 @@ void loop() {
|
||||
// sent
|
||||
if (ret == 18) {
|
||||
memset(radiorxbuffer + 1 + sizeof(struct MacFrameBcast), 0, sizeof(struct AvailDataReq) + 2);
|
||||
processAvailDataReq(radiorxbuffer);
|
||||
processAvailDataReq(radiorxbuffer, isSubGhzRx);
|
||||
}
|
||||
break;
|
||||
case PKT_TAG_RETURN_DATA:
|
||||
processTagReturnData(radiorxbuffer, ret);
|
||||
processTagReturnData(radiorxbuffer, ret, isSubGhzRx);
|
||||
break;
|
||||
default:
|
||||
Serial.printf("t=%02X\r\n", getPacketType(radiorxbuffer));
|
||||
@@ -765,7 +777,7 @@ void loop() {
|
||||
|
||||
if (blockStartTimer) {
|
||||
if (millis() > blockStartTimer) {
|
||||
sendBlockData();
|
||||
sendBlockData(isSubGhzRx);
|
||||
blockStartTimer = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "freertos/queue.h"
|
||||
#include "freertos/task.h"
|
||||
#include "led.h"
|
||||
#include "subGhz.h"
|
||||
#include "proto.h"
|
||||
#include "sdkconfig.h"
|
||||
// if you get an error about soc/lp_uart_reg.h not being found,
|
||||
@@ -22,6 +23,8 @@
|
||||
#include <string.h>
|
||||
#include <Arduino.h>
|
||||
|
||||
bool has_sub_ghz = false;
|
||||
|
||||
uint8_t mSelfMac[8];
|
||||
volatile uint8_t isInTransmit = 0;
|
||||
QueueHandle_t packet_buffer = NULL;
|
||||
@@ -83,34 +86,70 @@ void radio_init(uint8_t ch) {
|
||||
mSelfMac[0], mSelfMac[1], mSelfMac[2], mSelfMac[3],
|
||||
mSelfMac[4], mSelfMac[5], mSelfMac[6], mSelfMac[7],
|
||||
esp_ieee802154_get_short_address());
|
||||
|
||||
|
||||
// Lets here take care of the SubGhz Init
|
||||
if (!init_subGhz())
|
||||
Serial.printf("Sub-GHz radio init failed\r\n");
|
||||
else if (!tiRadioSetChannel(ch))
|
||||
Serial.printf("SubGHz radio channel fail\r\n");
|
||||
else
|
||||
has_sub_ghz = true;
|
||||
|
||||
Serial.printf("SubGhz %s\r\n", has_sub_ghz ? "Active" : "Not Found");
|
||||
if (has_sub_ghz) {
|
||||
tiRadioRxFilterCfg(mSelfMac, SHORT_MAC_UNUSED, PROTO_PAN_ID, true);
|
||||
tiRadioTxConfigure(mSelfMac, SHORT_MAC_UNUSED, PROTO_PAN_ID);
|
||||
tiRadioRxEnable(true, false);
|
||||
}
|
||||
}
|
||||
|
||||
// uint32_t lastZbTx = 0;
|
||||
bool radioTx(uint8_t *packet) {
|
||||
static uint8_t txPKT[130];
|
||||
bool radioTx(uint8_t *packet, bool subGhz) {
|
||||
led_flash(1);
|
||||
while (isInTransmit) {
|
||||
if (has_sub_ghz && subGhz) {
|
||||
tiRadioTxLL(packet);
|
||||
} else {
|
||||
static uint8_t txPKT[130];
|
||||
while (isInTransmit) {
|
||||
}
|
||||
// while (millis() - lastZbTx < 6) {
|
||||
// }
|
||||
// lastZbTx = millis();
|
||||
memcpy(txPKT, packet, packet[0]);
|
||||
isInTransmit = 1;
|
||||
esp_ieee802154_transmit(txPKT, false);
|
||||
return true;
|
||||
}
|
||||
// while (millis() - lastZbTx < 6) {
|
||||
// }
|
||||
// lastZbTx = millis();
|
||||
memcpy(txPKT, packet, packet[0]);
|
||||
isInTransmit = 1;
|
||||
esp_ieee802154_transmit(txPKT, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
void radioSetChannel(uint8_t ch) {
|
||||
radio_init(ch);
|
||||
if (has_sub_ghz)
|
||||
tiRadioSetChannel(ch);
|
||||
}
|
||||
|
||||
void radioSetTxPower(uint8_t power) {}
|
||||
|
||||
int8_t commsRxUnencrypted(uint8_t *data) {
|
||||
static uint8_t inner_rxPKT_out[130];
|
||||
int8_t commsRxUnencrypted(uint8_t *data, bool *subGhzRx) {
|
||||
int8_t rssi_sub_rx = 0;
|
||||
uint8_t lqi_sub_rx = 0;
|
||||
|
||||
static uint8_t inner_rxPKT_out[135];
|
||||
if (xQueueReceive(packet_buffer, (void *)&inner_rxPKT_out, pdMS_TO_TICKS(100)) == pdTRUE) {
|
||||
memcpy(data, &inner_rxPKT_out[1], inner_rxPKT_out[0] + 1);
|
||||
*subGhzRx = false; // This is Normal data
|
||||
return inner_rxPKT_out[0] - 2;
|
||||
}
|
||||
if (has_sub_ghz) {
|
||||
int32_t ret_sub_rx_len = tiRadioRxDequeuePkt(inner_rxPKT_out, sizeof(inner_rxPKT_out), &rssi_sub_rx, &lqi_sub_rx);
|
||||
if (ret_sub_rx_len > 0)
|
||||
{
|
||||
//Serial.printf("Got Sub Ghz Len %i data: %i %u\r\n", ret_sub_rx, rssi_sub_rx, lqi_sub_rx);
|
||||
memcpy(data, inner_rxPKT_out, ret_sub_rx_len);
|
||||
*subGhzRx = true; // This is SubGHz data
|
||||
return ret_sub_rx_len;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
extern uint8_t mSelfMac[8];
|
||||
|
||||
void radio_init(uint8_t ch);
|
||||
bool radioTx(uint8_t *packet);
|
||||
bool radioTx(uint8_t *packet, bool subGhz);
|
||||
void radioSetChannel(uint8_t ch);
|
||||
void radioSetTxPower(uint8_t power);
|
||||
int8_t commsRxUnencrypted(uint8_t *data);
|
||||
int8_t commsRxUnencrypted(uint8_t *data, bool *subGhzRx);
|
||||
|
||||
609
ARM_Tag_FW/Arduino_OpenEPaperLink_C6_AP/subGhz.cpp
Normal file
609
ARM_Tag_FW/Arduino_OpenEPaperLink_C6_AP/subGhz.cpp
Normal file
@@ -0,0 +1,609 @@
|
||||
|
||||
// This code is heavily depending on Dmitrys wonderful Work !
|
||||
// https://dmitry.gr/?r=05.Projects&proj=29.%20eInk%20Price%20Tags
|
||||
// Ported and modified to fit the OpenEPaperLink Project by ATC1441 (ATCnetz.de) ~01.2024
|
||||
|
||||
#include "subGhz.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/timers.h"
|
||||
#include "proto.h"
|
||||
#include <Arduino.h>
|
||||
#include <SPI.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define SUB_GHZ_CH_OFST 11
|
||||
#define SUB_GHZ_NUM_CHANNELS 25
|
||||
|
||||
/*
|
||||
//we configure GDO_2 is for TX.has_fifo_space
|
||||
//we configure GDO_0 is for RX.has_data
|
||||
WIRING:
|
||||
*/
|
||||
#define sub_CS 4
|
||||
#define sub_GD0 5
|
||||
#define sub_GD2 6
|
||||
#define sub_MISO 7
|
||||
#define sub_CLK 0
|
||||
#define sub_MOSI 1
|
||||
|
||||
#define CMD_SRES 0x30
|
||||
#define CMD_SFSTXON 0x31
|
||||
#define CMD_SXOFF 0x32
|
||||
#define CMD_SCAL 0x33
|
||||
#define CMD_SRX 0x34
|
||||
#define CMD_STX 0x35
|
||||
#define CMD_SIDLE 0x36
|
||||
#define CMD_SWOR 0x38
|
||||
#define CMD_SPWD 0x39
|
||||
#define CMD_SFRX 0x3a
|
||||
#define CMD_SFTX 0x3b
|
||||
#define CMD_SWORRST 0x3c
|
||||
#define CMD_SNOP 0x3d
|
||||
|
||||
#define REG_IOCFG2 0x00
|
||||
#define REG_IOCFG1 0x01
|
||||
#define REG_IOCFG0 0x02
|
||||
#define REG_FIFOTHR 0x03
|
||||
#define REG_SYNC1 0x04
|
||||
#define REG_SYNC0 0x05
|
||||
#define REG_PKTLEN 0x06
|
||||
#define REG_PKTCTRL1 0x07
|
||||
#define REG_PKTCTRL0 0x08
|
||||
#define REG_ADDR 0x09
|
||||
#define REG_CHANNR 0x0a
|
||||
#define REG_FSCTRL1 0x0b
|
||||
#define REG_FSCTRL0 0x0c
|
||||
#define REG_FREQ2 0x0d
|
||||
#define REG_FREQ1 0x0e
|
||||
#define REG_FREQ0 0x0f
|
||||
#define REG_MDMCFG4 0x10
|
||||
#define REG_MDMCFG3 0x11
|
||||
#define REG_MDMCFG2 0x12
|
||||
#define REG_MDMCFG1 0x13
|
||||
#define REG_MDMCFG0 0x14
|
||||
#define REG_DEVIATN 0x15
|
||||
#define REG_MCSM2 0x16
|
||||
#define REG_MCSM1 0x17
|
||||
#define REG_MCSM0 0x18
|
||||
#define REG_FOCCFG 0x19
|
||||
#define REG_BSCFG 0x1a
|
||||
#define REG_AGCTRL2 0x1b
|
||||
#define REG_AGCTRL1 0x1c
|
||||
#define REG_AGCTRL0 0x1d
|
||||
#define REG_WOREVT1 0x1e
|
||||
#define REG_WOREVT0 0x1f
|
||||
#define REG_WORCTRL 0x20
|
||||
#define REG_FREND1 0x21
|
||||
#define REG_FREND0 0x22
|
||||
#define REG_FSCAL3 0x23
|
||||
#define REG_FSCAL2 0x24
|
||||
#define REG_FSCAL1 0x25
|
||||
#define REG_FSCAL0 0x26
|
||||
#define REG_RCCTRL1 0x27
|
||||
#define REG_RCCTRL0 0x28
|
||||
#define REG_FSTEST 0x29
|
||||
#define REG_PTEST 0x2a
|
||||
#define REG_AGCTEST 0x2b
|
||||
#define REG_TEST2 0x2c
|
||||
#define REG_TEST1 0x2d
|
||||
#define REG_TEST0 0x2e
|
||||
|
||||
#define REG_PATABLE 0x3e
|
||||
#define REG_FIFO 0x3f
|
||||
|
||||
#define REG_PARTNUM 0xf0
|
||||
#define REG_VERSION 0xf1
|
||||
#define REG_FREQEST 0xf2
|
||||
#define REG_LQI 0xf3
|
||||
#define REG_RSSI 0xf4
|
||||
#define REG_MARCSTATE 0xf5
|
||||
#define REG_WORTIME1 0xf6
|
||||
#define REG_WORTIME0 0xf7
|
||||
#define REG_PKTSTATUS 0xf8
|
||||
#define REG_VCO_VC_DAC 0xf9
|
||||
#define REG_TXBYTES 0xfa
|
||||
#define REG_RXBYTES 0xfb
|
||||
#define REG_RCCTRL1_STA 0xfc
|
||||
#define REG_RCCTRL0_STA 0xfd
|
||||
|
||||
|
||||
|
||||
#define MAX_RX_PKTS 80
|
||||
|
||||
static volatile uint8_t mRxBufs[MAX_RX_PKTS][RADIO_MAX_PACKET_LEN + 1 /* length */ + 2 /* RSSI, LQI/STA */];
|
||||
static volatile uint8_t mRxNextWrite, mRxNextRead, mRxNumFree, mRxNumGot;
|
||||
static uint8_t mRxFilterLongMac[8], mTxLongMac[8];
|
||||
static uint32_t mRxFilterShortMac, mTxShortMac;
|
||||
static bool mRxEnabled, mAutoAck, mPromisc;
|
||||
static uint16_t mRxFilterPan, mTxPan;
|
||||
static volatile int16_t mLastAck;
|
||||
|
||||
struct MacHeaderGenericAddr {
|
||||
struct MacFcs fixed;
|
||||
uint8_t seq;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct MacHeaderShortAddr {
|
||||
struct MacFcs fixed;
|
||||
uint8_t seq;
|
||||
uint16_t pan;
|
||||
uint16_t shortDstAddr;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct MacHeaderLongAddr {
|
||||
struct MacFcs fixed;
|
||||
uint8_t seq;
|
||||
uint16_t pan;
|
||||
uint8_t longDstAddr[8];
|
||||
} __attribute__((packed));
|
||||
|
||||
|
||||
|
||||
void tiRadioTxConfigure(const uint8_t *myLongMac, uint32_t myShortMac, uint16_t pan) {
|
||||
memcpy(mTxLongMac, myLongMac, sizeof(mTxLongMac));
|
||||
mTxShortMac = myShortMac;
|
||||
mTxPan = pan;
|
||||
}
|
||||
|
||||
void tiRadioRxFilterCfg(const uint8_t *myMac, uint32_t myShortMac, uint16_t myPan, bool promisc) {
|
||||
mPromisc = promisc;
|
||||
mRxFilterShortMac = myShortMac;
|
||||
mRxFilterPan = myPan;
|
||||
memcpy(mRxFilterLongMac, myMac, sizeof(mRxFilterLongMac));
|
||||
}
|
||||
|
||||
static bool tiRadioPrvSelect(void) {
|
||||
digitalWrite(sub_CS, LOW);
|
||||
SPI.beginTransaction(SPISettings(6000000, MSBFIRST, SPI_MODE0));
|
||||
asm volatile("nop \n nop \n nop \n");
|
||||
return true;
|
||||
}
|
||||
|
||||
static void tiRadioPrvDeselect(void) {
|
||||
asm volatile("nop \n nop \n nop \n");
|
||||
SPI.endTransaction();
|
||||
digitalWrite(sub_CS, HIGH);
|
||||
asm volatile("nop \n nop \n nop \n");
|
||||
}
|
||||
|
||||
static void read_multiple(uint8_t *dst, uint_fast8_t rxLen) {
|
||||
for (int i = 0; i < rxLen; i++) {
|
||||
dst[i] = SPI.transfer(0xff);
|
||||
}
|
||||
}
|
||||
|
||||
static int_fast16_t tiRadioPrvStrobe(uint8_t cmd) //negative on error
|
||||
{
|
||||
if (!tiRadioPrvSelect())
|
||||
return -1;
|
||||
|
||||
cmd = SPI.transfer(cmd);
|
||||
|
||||
tiRadioPrvDeselect();
|
||||
|
||||
return (uint_fast16_t)cmd;
|
||||
}
|
||||
|
||||
static bool tiRadioPrvRegWrite(uint_fast8_t reg, uint_fast8_t val) {
|
||||
if (!tiRadioPrvSelect())
|
||||
return false;
|
||||
|
||||
SPI.transfer(reg);
|
||||
SPI.transfer(val);
|
||||
|
||||
tiRadioPrvDeselect();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int_fast16_t tiRadioPrvRegRead(uint_fast8_t reg) {
|
||||
uint8_t read_data = 0x00;
|
||||
|
||||
if (!tiRadioPrvSelect())
|
||||
return -1;
|
||||
|
||||
SPI.transfer(reg | 0x80);
|
||||
read_data = SPI.transfer(0xff);
|
||||
|
||||
tiRadioPrvDeselect();
|
||||
|
||||
return (uint_fast16_t)read_data;
|
||||
}
|
||||
|
||||
static bool tiRadioPrvRegWriteLong(uint8_t reg, const uint8_t *valP, uint8_t len) {
|
||||
if (!tiRadioPrvSelect())
|
||||
return false;
|
||||
|
||||
SPI.transfer(reg | 0x40);
|
||||
|
||||
for (int i = 0; i < len; i++) {
|
||||
SPI.transfer(valP[i]);
|
||||
}
|
||||
|
||||
tiRadioPrvDeselect();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool tiRadioRxEnable(bool on, bool autoAck) {
|
||||
bool ret = false;
|
||||
|
||||
if (on) {
|
||||
|
||||
mAutoAck = autoAck;
|
||||
if (mRxEnabled) {
|
||||
ret = true;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!tiRadioPrvStrobe(CMD_SRX))
|
||||
goto out;
|
||||
|
||||
mRxEnabled = true;
|
||||
} else if (mRxEnabled) {
|
||||
|
||||
if (!tiRadioPrvStrobe(CMD_SIDLE))
|
||||
goto out;
|
||||
|
||||
mRxEnabled = false;
|
||||
}
|
||||
ret = true;
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool radioPrvMacsEqual(const uint8_t *macA, const uint8_t *macB) {
|
||||
const uint32_t *a = (const uint32_t *)(const char *)macA;
|
||||
const uint32_t *b = (const uint32_t *)(const char *)macB;
|
||||
return a[0] == b[0] && a[1] == b[1];
|
||||
}
|
||||
|
||||
static uint_fast8_t tiRadioPrvGetState(void) {
|
||||
uint_fast8_t state;
|
||||
|
||||
do {
|
||||
state = tiRadioPrvRegRead(REG_MARCSTATE);
|
||||
} while (tiRadioPrvRegRead(REG_MARCSTATE) != state);
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
static void tiRadioPrvPacketRx(void) {
|
||||
uint8_t *rxedPkt = (uint8_t *)mRxBufs[mRxNextWrite];
|
||||
const struct MacHeaderShortAddr *withShortDst = (const struct MacHeaderShortAddr *)(rxedPkt + 1);
|
||||
const struct MacHeaderGenericAddr *generic = (const struct MacHeaderGenericAddr *)(rxedPkt + 1);
|
||||
const struct MacHeaderLongAddr *withLongDst = (const struct MacHeaderLongAddr *)(rxedPkt + 1);
|
||||
bool crcOk, acceptPacket, sendAck = false;
|
||||
int32_t t, lenNoCrc, lenNoMacFixed;
|
||||
uint32_t nWaitCycles = 10000;
|
||||
uint_fast8_t spiLen, now;
|
||||
|
||||
t = tiRadioPrvRegRead(REG_FIFO);
|
||||
if (t < 0)
|
||||
goto fail;
|
||||
|
||||
if (!mRxNumFree)
|
||||
goto fail;
|
||||
|
||||
spiLen = t;
|
||||
if (spiLen > RADIO_MAX_PACKET_LEN)
|
||||
goto fail;
|
||||
|
||||
t = 0;
|
||||
rxedPkt[t++] = lenNoCrc = spiLen;
|
||||
now = 31; //we just read one so 31 left for sure in the FIFO
|
||||
spiLen += 2; //we expect 2 more bytes
|
||||
|
||||
while (spiLen) {
|
||||
|
||||
uint8_t reg;
|
||||
|
||||
if (!tiRadioPrvSelect()) {
|
||||
tiRadioPrvDeselect();
|
||||
goto fail;
|
||||
}
|
||||
|
||||
reg = 0xc0 | REG_FIFO; //burst read
|
||||
reg = SPI.transfer(reg);
|
||||
now = reg & 0x0f;
|
||||
|
||||
if (now > spiLen)
|
||||
now = spiLen;
|
||||
|
||||
if (!now && !--nWaitCycles) {
|
||||
tiRadioPrvDeselect();
|
||||
//Serial.printf(" !!! RX timeout !!! \r\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
read_multiple(rxedPkt + t, now);
|
||||
t += now;
|
||||
spiLen -= now;
|
||||
|
||||
tiRadioPrvDeselect();
|
||||
}
|
||||
|
||||
rxedPkt++; //skip len;
|
||||
crcOk = !!(rxedPkt[lenNoCrc + 1] & 0x80);
|
||||
|
||||
lenNoMacFixed = lenNoCrc - sizeof(struct MacFcs) - sizeof(uint8_t);
|
||||
|
||||
if (mPromisc)
|
||||
acceptPacket = true;
|
||||
//otherwise, we need a valid crc
|
||||
else if (!crcOk) {
|
||||
acceptPacket = false;
|
||||
}
|
||||
//packet should be big enough to contain a header
|
||||
else if (lenNoMacFixed < 0)
|
||||
acceptPacket = false;
|
||||
else switch (generic->fixed.frameType) {
|
||||
|
||||
case FRAME_TYPE_ACK:
|
||||
mLastAck = (uint16_t)generic->seq;
|
||||
acceptPacket = false; //no need to save it as a packet
|
||||
break;
|
||||
|
||||
case FRAME_TYPE_DATA: //we are not the coordinator, so we demand to see our address as destination...
|
||||
|
||||
switch (generic->fixed.destAddrType) {
|
||||
case ADDR_MODE_SHORT:
|
||||
acceptPacket = (withShortDst->pan == 0xffff && withShortDst->shortDstAddr == 0xffff) || (withShortDst->pan == mRxFilterPan && (withShortDst->shortDstAddr == 0xffff || ((uint32_t)withShortDst->shortDstAddr) == mRxFilterShortMac));
|
||||
break;
|
||||
|
||||
case ADDR_MODE_LONG:
|
||||
acceptPacket = withLongDst->pan == mRxFilterPan && radioPrvMacsEqual(withLongDst->longDstAddr, mRxFilterLongMac);
|
||||
break;
|
||||
|
||||
default:
|
||||
acceptPacket = false;
|
||||
break;
|
||||
}
|
||||
sendAck = generic->fixed.ackReqd && mAutoAck;
|
||||
break;
|
||||
|
||||
default:
|
||||
//no riff-raff please
|
||||
acceptPacket = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (acceptPacket) { //other checks here too plz
|
||||
|
||||
if (sendAck) {
|
||||
|
||||
static struct {
|
||||
uint8_t len;
|
||||
struct MacFcs mac;
|
||||
uint8_t seq;
|
||||
} __attribute__((packed)) ack = {
|
||||
.len = sizeof(ack) + sizeof(uint16_t) - sizeof(ack.len),
|
||||
.mac = {
|
||||
.frameType = FRAME_TYPE_ACK,
|
||||
},
|
||||
};
|
||||
|
||||
ack.seq = generic->seq;
|
||||
|
||||
tiRadioTxLL(&ack);
|
||||
}
|
||||
|
||||
mRxNumFree--;
|
||||
if (++mRxNextWrite == MAX_RX_PKTS)
|
||||
mRxNextWrite = 0;
|
||||
mRxNumGot++;
|
||||
}
|
||||
|
||||
out:
|
||||
if (mRxEnabled && !sendAck) { //if ack is being TXed, TX irq will restart rx later
|
||||
uint32_t maxWait = 100000;
|
||||
uint8_t state;
|
||||
|
||||
(void)tiRadioPrvStrobe(CMD_SRX);
|
||||
|
||||
do {
|
||||
state = tiRadioPrvGetState();
|
||||
|
||||
if (!--maxWait) {
|
||||
//Serial.printf("too long wait for rx state. state is %d\n", state);
|
||||
break;
|
||||
}
|
||||
|
||||
} while (state != 13 && state != 14 && state != 15);
|
||||
}
|
||||
return;
|
||||
|
||||
fail:
|
||||
(void)tiRadioPrvStrobe(CMD_SFRX);
|
||||
goto out;
|
||||
}
|
||||
|
||||
void tiRadioRxAckReset(void) {
|
||||
mLastAck = -1;
|
||||
}
|
||||
|
||||
int16_t tiRadioRxAckGetLast(void) {
|
||||
return mLastAck;
|
||||
}
|
||||
|
||||
int32_t tiRadioRxDequeuePkt(void *dstBuf, uint32_t maxLen, int8_t *rssiP, uint8_t *lqiP) {
|
||||
uint32_t len, copyLen = maxLen;
|
||||
|
||||
if (!mRxNumGot)
|
||||
return -1;
|
||||
|
||||
len = mRxBufs[mRxNextRead][0];
|
||||
|
||||
if (copyLen > len)
|
||||
copyLen = len;
|
||||
memcpy(dstBuf, (const void *)(mRxBufs[mRxNextRead] + 1), copyLen);
|
||||
|
||||
if (lqiP) {
|
||||
|
||||
uint32_t lqi = 296 - ((mRxBufs[mRxNextRead][len + 2] & 0x7f) * 2);
|
||||
|
||||
//LQI: lower is better. 48 is very good (no lower value seen), 127 is very bad (max value possible)
|
||||
//we want LQI 0..255 so we scale as SATURATE(296 - 2 * val)
|
||||
|
||||
if (lqi > 255)
|
||||
lqi = 255;
|
||||
|
||||
*lqiP = lqi;
|
||||
}
|
||||
|
||||
if (rssiP)
|
||||
*rssiP = ((int8_t)mRxBufs[mRxNextRead][len + 1]) / 2 - 77;
|
||||
|
||||
if (++mRxNextRead == MAX_RX_PKTS)
|
||||
mRxNextRead = 0;
|
||||
|
||||
noInterrupts();
|
||||
mRxNumFree++;
|
||||
mRxNumGot--;
|
||||
interrupts();
|
||||
|
||||
//ti radio never stalls RX state machine so nothing to do even if we just freed up a buffer
|
||||
return len;
|
||||
}
|
||||
|
||||
void data_input_interrupt(void) {
|
||||
while (digitalRead(sub_GD0)) //while there is data (packets)
|
||||
tiRadioPrvPacketRx();
|
||||
}
|
||||
|
||||
static void tiRadioPrvIfInit(void) {
|
||||
//configure pins
|
||||
pinMode(sub_CS, OUTPUT);
|
||||
pinMode(sub_GD0, INPUT_PULLUP);
|
||||
pinMode(sub_GD2, INPUT_PULLUP);
|
||||
SPI.begin(sub_CLK, sub_MISO, sub_MOSI);
|
||||
|
||||
tiRadioPrvDeselect();
|
||||
|
||||
mRxNumFree = MAX_RX_PKTS;
|
||||
}
|
||||
|
||||
static void tiRadioPrvIrqInit(void) {
|
||||
attachInterrupt(sub_GD0, data_input_interrupt, RISING);
|
||||
}
|
||||
|
||||
bool tiRadioSetChannel(uint_fast8_t channel) {
|
||||
channel -= SUB_GHZ_CH_OFST;
|
||||
if (channel >= SUB_GHZ_NUM_CHANNELS)
|
||||
return false;
|
||||
|
||||
return tiRadioPrvRegWrite(REG_CHANNR, channel * 3);
|
||||
}
|
||||
|
||||
bool tiRadioTxLL(const void *pkt) {
|
||||
const uint8_t *data = (const uint8_t *)pkt;
|
||||
uint32_t len = 1 + *data, now;
|
||||
bool ret = false;
|
||||
|
||||
if (0) {
|
||||
Serial.printf("TX packet len %u :", len);
|
||||
for (int t = 0; t < len; t++) {
|
||||
Serial.printf(" %02x", data[1 + t]);
|
||||
}
|
||||
Serial.printf("\n");
|
||||
}
|
||||
|
||||
if (tiRadioPrvStrobe(CMD_SIDLE) < 0)
|
||||
goto out;
|
||||
|
||||
if (tiRadioPrvStrobe(CMD_SFTX) < 0)
|
||||
goto out;
|
||||
|
||||
now = (len > 64 ? 64 : len);
|
||||
if (!tiRadioPrvRegWriteLong(REG_FIFO, data, now))
|
||||
goto out;
|
||||
len -= now;
|
||||
data += now;
|
||||
|
||||
if (tiRadioPrvStrobe(CMD_STX) < 0)
|
||||
goto out;
|
||||
|
||||
while (len) {
|
||||
|
||||
now = 8;
|
||||
if (now > len)
|
||||
now = len;
|
||||
|
||||
while (digitalRead(sub_GD2))
|
||||
; //wait till there is space
|
||||
|
||||
if (!tiRadioPrvRegWriteLong(REG_FIFO, data, now))
|
||||
goto out;
|
||||
|
||||
data += now;
|
||||
len -= now;
|
||||
}
|
||||
|
||||
while (tiRadioPrvGetState() != 1)
|
||||
;
|
||||
|
||||
if (!tiRadioPrvStrobe(mRxEnabled ? CMD_SRX : CMD_SIDLE))
|
||||
goto out;
|
||||
|
||||
ret = true;
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool init_subGhz(void) {
|
||||
uint8_t regsCfg[] = {
|
||||
/* 0x00 */ 0x02, 0x2e, 0x01, 0x07, 0xd3, 0x91, 0x7f, 0x04,
|
||||
/* 0x08 */ 0x45, 0x22, 0x00, 0x0e, 0x00, 0x22, 0xbb, 0x13,
|
||||
/* 0x10 */ 0x1d, 0x3b, 0x13, 0x43, 0xa4, 0x65, 0x07, 0x30,
|
||||
/* 0x18 */ 0x1d, 0x1e, 0x1c, 0xc7, 0x00, 0xb0, 0x87, 0x6b,
|
||||
/* 0x20 */ 0xfb, 0xb6, 0x10, 0xea, 0x2a, 0x00, 0x1f, 0x41,
|
||||
/* 0x28 */ 0x00,
|
||||
};
|
||||
|
||||
uint8_t paTab[] = { 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0 };
|
||||
|
||||
tiRadioPrvIfInit();
|
||||
|
||||
if (tiRadioPrvRegRead(REG_PARTNUM) != 0x00) {
|
||||
Serial.printf("partnum is wrong\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (tiRadioPrvStrobe(CMD_SRES) < 0) {
|
||||
Serial.printf("res reply\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
delayMicroseconds(300);
|
||||
|
||||
if (tiRadioPrvStrobe(CMD_SIDLE) != 0x0f) {
|
||||
Serial.printf("idle reply\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!tiRadioPrvRegWriteLong(0, regsCfg, sizeof(regsCfg))) {
|
||||
Serial.printf("config issue\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!tiRadioPrvRegWriteLong(REG_PATABLE, paTab, sizeof(paTab))) {
|
||||
Serial.printf("PAtable issue\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
tiRadioPrvIrqInit();
|
||||
|
||||
Serial.printf("Sub-GHz radio inited\n");
|
||||
|
||||
tiRadioRxEnable(true, false);
|
||||
Serial.printf("Sub-GHz rx is on\n");
|
||||
return true;
|
||||
}
|
||||
32
ARM_Tag_FW/Arduino_OpenEPaperLink_C6_AP/subGhz.h
Normal file
32
ARM_Tag_FW/Arduino_OpenEPaperLink_C6_AP/subGhz.h
Normal file
@@ -0,0 +1,32 @@
|
||||
|
||||
// This code is heavily depending on Dmitrys wonderful Work !
|
||||
// https://dmitry.gr/?r=05.Projects&proj=29.%20eInk%20Price%20Tags
|
||||
// Ported and modified to fit the OpenEPaperLink Project by ATC1441 (ATCnetz.de) ~01.2024
|
||||
|
||||
#pragma once
|
||||
#include <Arduino.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
void tiRadioTxConfigure(const uint8_t *myLongMac, uint32_t myShortMac, uint16_t pan);
|
||||
void tiRadioRxFilterCfg(const uint8_t *myMac, uint32_t myShortMac, uint16_t myPan, bool promisc);
|
||||
static bool tiRadioPrvSelect(void);
|
||||
static void tiRadioPrvDeselect(void);
|
||||
static void read_multiple(uint8_t *dst, uint_fast8_t rxLen);
|
||||
static int_fast16_t tiRadioPrvStrobe(uint8_t cmd);
|
||||
static bool tiRadioPrvRegWrite(uint_fast8_t reg, uint_fast8_t val);
|
||||
static int_fast16_t tiRadioPrvRegRead(uint_fast8_t reg);
|
||||
static bool tiRadioPrvRegWriteLong(uint8_t reg, const uint8_t *valP, uint8_t len);
|
||||
bool tiRadioRxEnable(bool on, bool autoAck);
|
||||
static bool radioPrvMacsEqual(const uint8_t *macA, const uint8_t *macB);
|
||||
static uint_fast8_t tiRadioPrvGetState(void);
|
||||
static void tiRadioPrvPacketRx(void);
|
||||
void tiRadioRxAckReset(void);
|
||||
int16_t tiRadioRxAckGetLast(void);
|
||||
int32_t tiRadioRxDequeuePkt(void* dstBuf, uint32_t maxLen, int8_t *rssiP, uint8_t *lqiP);
|
||||
void data_input_interrupt(void);
|
||||
static void tiRadioPrvIfInit(void);
|
||||
static void tiRadioPrvIrqInit(void);
|
||||
bool tiRadioSetChannel(uint_fast8_t channel);
|
||||
bool tiRadioTxLL(const void* pkt);
|
||||
|
||||
bool init_subGhz(void);
|
||||
Reference in New Issue
Block a user