/******************************************************************************* * @note Copyright (C) 2020 Shanghai Panchip Microelectronics Co., Ltd. All rights reserved. * * @file pan3028.c * @brief * * @history - V3.0, 2021-11-05 *******************************************************************************/ #include "stdio.h" #include "math.h" #include "pan3028_port.h" #include "pan3028.h" #include "radio.h" uint8_t RadioRxPayload[255]; uint8_t plhd_buf[16]; uint8_t reg_agc_value[98] = {0x90,0xff,0x64,0x27,0x00,0x00,0x27,0x27,0x00,0x00,0x27,0x27,0x00,0x00,0x27,0x27,\ 0x00,0x00,0x27,0x27,0x00,0x00,0x27,0x27,0x00,0x00,0x27,0x27,0x00,0x00,0x27,0x27,\ 0x00,0x00,0x27,0x27,0x00,0x00,0x27,0x2B,0x00,0xF8,0x2B,0x31,0x00,0xFC,0x31,0x37,\ 0x00,0xFF,0x37,0x3C,0x20,0xFF,0x3C,0x42,0x40,0xFF,0x42,0x48,0x60,0xFF,0x48,0x4D,\ 0x80,0xFF,0x4D,0x53,0x84,0xFF,0x53,0x59,0x88,0xFF,0x59,0x5F,0x8C,0xFF,0x5F,0x64,\ 0x90,0xFF,0x64,0x06,0xFF,0x40,0x42,0x0F,0x00,0x00,0x01,0xF4,0x2F,0xF3,0x0F,0x00,0x00,0x00}; /** * @brief read one byte from register in current page * @param[in] register address to write * @return value read from register */ static uint8_t PAN3028_read_reg(uint8_t addr) { uint8_t temreg = 0x00; rf_port.spi_cs_low(); rf_port.spi_readwrite(0x00 | (addr<<1)); temreg=rf_port.spi_readwrite(0x00); rf_port.spi_cs_high(); return temreg; } /** * @brief write global register in current page and chick * @param[in] register address to write * @param[in] address value to write to rgister * @return result */ static uint32_t PAN3028_write_reg(uint8_t addr,uint8_t value) { uint16_t tmpreg = 0; uint16_t addr_w = (0x01 | (addr << 1)); rf_port.spi_cs_low(); rf_port.spi_readwrite(addr_w); rf_port.spi_readwrite(value); rf_port.spi_cs_high(); tmpreg = PAN3028_read_reg(addr); if(tmpreg == value) { return OK; } else { return FAIL; } } /** * @brief rf send data fifo,send bytes register * @param[in] register address to write * @param[in] send data buffer * @param[in] send data size * @return none */ static void PAN3028_write_fifo(uint8_t addr,uint8_t *buffer,int size) { int i; uint8_t addr_w = (0x01 | (addr << 1)); rf_port.spi_cs_low(); rf_port.spi_readwrite(addr_w); for(i =0;i register address to write * @param[in] receive data buffer * @param[in] receive data size * @return none */ static void PAN3028_read_fifo(uint8_t addr,uint8_t *buffer,int size) { int i; uint8_t addr_w = (0x00 | (addr<<1)); rf_port.spi_cs_low(); rf_port.spi_readwrite(addr_w); for(i =0;i page to switch * @return result */ static uint32_t PAN3028_switch_page(enum PAGE_SEL page) { uint8_t page_sel = 0x00; uint8_t tmpreg = 0x00; tmpreg = PAN3028_read_reg(REG_SYS_CTL); page_sel = (tmpreg & 0xfc )| page; PAN3028_write_reg(REG_SYS_CTL,page_sel); if((PAN3028_read_reg(REG_SYS_CTL) &0x03) == page) { return OK; }else { return FAIL; } } /** * @brief This function write a value to register in specific page * @param[in] the page of register * @param[in] register address * @param[in] value to write * @return result */ uint32_t PAN3028_write_spec_page_reg(enum PAGE_SEL page,uint8_t addr,uint8_t value) { if(PAN3028_switch_page(page) != OK) { return FAIL; } if(PAN3028_write_reg(addr,value) != OK) { return FAIL; } else { return OK; } } /** * @brief read a value to register in specific page * @param[in] the page of register * @param[in] register address * @return success(register value) or failure */ uint8_t PAN3028_read_spec_page_reg(enum PAGE_SEL page,uint8_t addr) { if(PAN3028_switch_page(page) != OK) { return FAIL; } return PAN3028_read_reg(addr); } /** * @brief write continue register valuies(buffer) in specific addr page * @param[in] the page of register * @param[in] register start address * @param[in] values to write * @param[in] buffer len * @return result */ uint32_t PAN3028_write_read_continue_regs(enum PAGE_SEL page,uint8_t addr,uint8_t *buffer,uint8_t len) { uint8_t i,temreg[256]; uint16_t addr_w; if(PAN3028_switch_page(page) != OK) { return FAIL; } addr_w = (0x01 | (addr << 1)); rf_port.spi_cs_low(); rf_port.spi_readwrite(addr_w); for(i=0;i * @return none */ void PAN3028_clr_irq(void) { PAN3028_write_spec_page_reg(PAGE0_SEL,0x6C,0x1f); } /** * @brief get irq status * @param[in] * @return ira status */ uint8_t PAN3028_get_irq(void) { uint8_t tmpreg; tmpreg = PAN3028_read_spec_page_reg(PAGE0_SEL,0x6C); return tmpreg; } /** * @brief software reset * @param[in] * @return result */ uint32_t PAN3028_rst(void) { uint8_t tmpreg = 0; tmpreg = PAN3028_read_reg(REG_SYS_CTL); tmpreg |= 0x80; PAN3028_write_reg(REG_SYS_CTL,tmpreg); tmpreg = PAN3028_read_reg(REG_SYS_CTL); tmpreg &= 0x7F; PAN3028_write_reg(REG_SYS_CTL,tmpreg); return OK; } /** * @brief clear packet count register * @param[in] * @return none */ void PAN3028_clr_pkt_cnt(void) { uint8_t tmpreg; tmpreg = PAN3028_read_reg(REG_SYS_CTL); tmpreg = (tmpreg & 0xbf) | 0x40 ; PAN3028_write_reg(REG_SYS_CTL,tmpreg); tmpreg = PAN3028_read_reg(REG_SYS_CTL); tmpreg = (tmpreg & 0xbf); PAN3028_write_reg(REG_SYS_CTL,tmpreg); } /** * @brief enable AGC function * @param[in] * AGC_OFF/AGC_ON * @return result */ uint32_t PAN3028_agc_enable(uint32_t state) { uint8_t reg_val = 0x02; if(state == AGC_OFF) { reg_val = 0x03; } else { reg_val = 0x02; } if(PAN3028_write_spec_page_reg(PAGE2_SEL,0x06, reg_val) != OK) { return FAIL; } return OK; } /** * @brief configure AGC function * @param[in] * @return result */ uint32_t PAN3028_agc_config(void) { return PAN3028_write_read_continue_regs(PAGE2_SEL,0x07,reg_agc_value,98); } /** * @brief do basic configuration to initialize * @param[in] * @return result */ uint32_t PAN3028_init(void) { if(PAN3028_write_spec_page_reg(PAGE0_SEL,0x06,0x01) != OK) { return FAIL; } if(PAN3028_write_spec_page_reg(PAGE0_SEL,0x40,0x50) != OK) { return FAIL; } if(PAN3028_write_spec_page_reg(PAGE0_SEL,0x3e,0x2c) != OK) { return FAIL; } if(PAN3028_write_spec_page_reg(PAGE0_SEL,0x3c,0xff) != OK) { return FAIL; } if(PAN3028_write_spec_page_reg(PAGE0_SEL,0x4c,0xbf) != OK) { return FAIL; } if(PAN3028_write_spec_page_reg(PAGE0_SEL,0x4d,0x0e) != OK) { return FAIL; } if(PAN3028_write_spec_page_reg(PAGE1_SEL,0x0e,0x44) != OK) { return FAIL; } if(PAN3028_write_spec_page_reg(PAGE1_SEL,0x0f,0x0A) != OK) { return FAIL; } if(PAN3028_write_spec_page_reg(PAGE1_SEL,0x1e,0x00) != OK) { return FAIL; } if(PAN3028_write_spec_page_reg(PAGE1_SEL,0x11,0xA1) != OK) { return FAIL; } if(PAN3028_write_spec_page_reg(PAGE1_SEL,0x15,0x38) != OK) { return FAIL; } if(PAN3028_write_spec_page_reg(PAGE1_SEL,0x2f,0x0c) != OK) { return FAIL; } if(PAN3028_write_spec_page_reg(PAGE3_SEL,0x06,0x26) != OK) { return FAIL; } if(PAN3028_write_spec_page_reg(PAGE3_SEL,0x10,0x80) != OK) { return FAIL; } if(PAN3028_write_spec_page_reg(PAGE3_SEL,0x11,0x0d) != OK) { return FAIL; } if(PAN3028_write_spec_page_reg(PAGE3_SEL,0x12,0x16) != OK) { return FAIL; } if(PAN3028_write_spec_page_reg(PAGE3_SEL,0x18,0xff) != OK) { return FAIL; } return OK; } /** * @brief change PAN3028 mode from deep sleep to standby3(STB3) * @param[in] * @return result */ uint32_t PAN3028_deepsleep_wakeup(void) { rf_port.delayus(10); if(PAN3028_write_reg(REG_OP_MODE,PAN3028_MODE_DEEP_SLEEP) != OK) { return FAIL; } rf_port.delayus(10); if(PAN3028_write_reg(REG_OP_MODE,PAN3028_MODE_SLEEP) != OK) { return FAIL; } rf_port.delayms(1); if(PAN3028_write_reg(0x03, 0x1b) != OK) { return FAIL; } if(PAN3028_write_reg(0x04, 0x76) != OK) { return FAIL; } if(PAN3028_write_spec_page_reg(PAGE3_SEL,0x26, 0x40) != OK) { return FAIL; } rf_port.tcxo_init(); rf_port.delayms(1); if(PAN3028_write_reg(REG_OP_MODE,PAN3028_MODE_STB1) != OK) { return FAIL; } rf_port.delayus(10); if(PAN3028_write_reg(REG_OP_MODE,PAN3028_MODE_STB2) != OK) { return FAIL; } rf_port.delayms(1); if(PAN3028_write_reg(REG_OP_MODE,PAN3028_MODE_STB3) != OK) { return FAIL; } else { rf_port.delayus(10); return OK; } } /** * @brief change PAN3028 mode from sleep to standby3(STB3) * @param[in] * @return result */ uint32_t PAN3028_sleep_wakeup(void) { rf_port.delayus(10); if(PAN3028_write_reg(REG_OP_MODE,PAN3028_MODE_SLEEP) != OK) { return FAIL; } rf_port.delayms(1); if(PAN3028_write_reg(0x03, 0x1b) != OK) { return FAIL; } if(PAN3028_write_reg(0x04, 0x76) != OK) { return FAIL; } if(PAN3028_write_spec_page_reg(PAGE3_SEL,0x26, 0x40) != OK) { return FAIL; } rf_port.tcxo_init(); rf_port.delayms(1); if(PAN3028_write_reg(REG_OP_MODE,PAN3028_MODE_STB1) != OK) { return FAIL; } rf_port.delayus(10); if(PAN3028_write_reg(REG_OP_MODE,PAN3028_MODE_STB2) != OK) { return FAIL; } rf_port.delayms(1); if(PAN3028_write_reg(REG_OP_MODE,PAN3028_MODE_STB3) != OK) { return FAIL; } else { rf_port.delayus(10); return OK; } } /** * @brief change PAN3028 mode from standby3(STB3) to deep sleep, PAN3028 should set DCDC_OFF before enter deepsleep * @param[in] * @return result */ uint32_t PAN3028_deepsleep(void) { rf_port.delayus(10); if(PAN3028_write_reg(REG_OP_MODE,PAN3028_MODE_STB3) != OK) { return FAIL; } rf_port.delayus(10); if(PAN3028_write_reg(REG_OP_MODE,PAN3028_MODE_STB2) != OK) { return FAIL; } rf_port.delayus(10); if(PAN3028_write_reg(REG_OP_MODE,PAN3028_MODE_STB1) != OK) { return FAIL; } rf_port.delayus(10); rf_port.tcxo_close(); if(PAN3028_write_reg(0x04, 0x06) != OK) { return FAIL; } rf_port.delayus(10); if(PAN3028_write_reg(REG_OP_MODE,PAN3028_MODE_SLEEP) != OK) { return FAIL; } rf_port.delayus(10); if(PAN3028_write_reg(REG_OP_MODE,PAN3028_MODE_DEEP_SLEEP) != OK) { return FAIL; } else { return OK; } } /** * @brief change PAN3028 mode from standby3(STB3) to sleep, PAN3028 should set DCDC_OFF before enter sleep * @param[in] * @return result */ uint32_t PAN3028_sleep(void) { rf_port.delayus(10); if(PAN3028_write_reg(REG_OP_MODE,PAN3028_MODE_STB3) != OK) { return FAIL; } rf_port.delayus(10); if(PAN3028_write_reg(REG_OP_MODE,PAN3028_MODE_STB2) != OK) { return FAIL; } rf_port.delayus(10); if(PAN3028_write_reg(REG_OP_MODE,PAN3028_MODE_STB1) != OK) { return FAIL; } rf_port.delayus(10); rf_port.tcxo_close(); if(PAN3028_write_reg(0x04, 0x16) != OK) { return FAIL; } rf_port.delayus(10); if(PAN3028_write_reg(REG_OP_MODE,PAN3028_MODE_SLEEP) != OK) { return FAIL; } else { rf_port.delayus(10); return OK; } } /** * @brief set LO frequency * @param[in] LO frequency * LO_400M / LO_800M * @return result */ uint32_t PAN3028_set_lo_freq(uint32_t lo) { uint32_t reg_val = 0; reg_val = PAN3028_read_spec_page_reg(PAGE0_SEL,0x45); reg_val &= ~(0x03); if(lo == LO_400M) { reg_val |= 0x02; } else { reg_val |= 0x01; } if(PAN3028_write_spec_page_reg(PAGE0_SEL,0x45, reg_val) != OK) { return FAIL; } return OK; } /** * @brief set frequence * @param[in] RF frequency(in Hz) to set * @return result */ uint32_t PAN3028_set_freq(uint32_t freq) { uint8_t reg_read; uint8_t reg_freq; float tmp_var = 0.0; int integer_part = 0; float fractional_part = 0.0; int fb,fc; uint8_t lowband_sel = 0; if ( (freq >= freq_336000000) && (freq <= freq_470000000)) { if(PAN3028_write_spec_page_reg(PAGE0_SEL,0x4a,0x92) != OK) { return FAIL; } lowband_sel = 1; tmp_var = freq * 4 * 1.0 / 16000000; PAN3028_set_lo_freq(LO_400M); } else if ( (freq > freq_470000000) && (freq <= freq_510000000)) { if(PAN3028_write_spec_page_reg(PAGE0_SEL,0x4a,0xb2) != OK) { return FAIL; } lowband_sel = 1; tmp_var = freq * 4 * 1.0 / 16000000; PAN3028_set_lo_freq(LO_400M); } else if((freq >= freq_800000000) && (freq <= freq_920000000)) { if(PAN3028_write_spec_page_reg(PAGE0_SEL,0x4a,0x92) != OK) { return FAIL; } lowband_sel = 0; tmp_var = freq * 2 * 1.0 / 16000000; PAN3028_set_lo_freq(LO_800M); } else { return FAIL; } integer_part = (int)tmp_var; fb = integer_part - 20; fractional_part = tmp_var - integer_part; fc = (int)(fractional_part * 1600 / (2 * (1 + lowband_sel))); if(fc < 0xff) { fb = fb - 1; fc = fc + 400; } if(PAN3028_write_spec_page_reg(PAGE3_SEL, 0x15, (fb & 0x7F)) != OK) { return FAIL; } if(PAN3028_write_spec_page_reg(PAGE3_SEL, 0x16,(fc & 0xff)) != OK) { return FAIL; } if(PAN3028_write_spec_page_reg(PAGE3_SEL, 0x17,((fc >> 8) & 0x0f)) != OK) { return FAIL; } reg_read = PAN3028_read_spec_page_reg(PAGE3_SEL, 0x18); reg_read &= ~((1 << 2) | (1 << 1)); reg_read |= (1 << 3) | (lowband_sel << 2) | (lowband_sel << 1); if(PAN3028_write_spec_page_reg(PAGE3_SEL, 0x18, reg_read) != OK) { return FAIL; } reg_freq = freq & 0xff; if(PAN3028_write_spec_page_reg(PAGE3_SEL, 0x09, reg_freq) != OK) { return FAIL; } reg_freq = (freq >> 8) & 0xff; if(PAN3028_write_spec_page_reg(PAGE3_SEL, 0x0a, reg_freq) != OK) { return FAIL; } reg_freq = (freq >> 16) & 0xff; if(PAN3028_write_spec_page_reg(PAGE3_SEL, 0x0b, reg_freq) != OK) { return FAIL; } reg_freq = (freq >> 24) & 0xff; if(PAN3028_write_spec_page_reg(PAGE3_SEL, 0x0c, reg_freq) != OK) { return FAIL; } return OK; } /** * @brief read frequency(in Hz) * @param[in] * @return frequency(in Hz) */ uint32_t PAN3028_read_freq(void) { uint8_t reg1, reg2, reg3 , reg4; uint32_t freq = 0x00; reg1 = PAN3028_read_spec_page_reg(PAGE3_SEL, 0x09); reg2 = PAN3028_read_spec_page_reg(PAGE3_SEL, 0x0a); reg3 = PAN3028_read_spec_page_reg(PAGE3_SEL, 0x0b); reg4 = PAN3028_read_spec_page_reg(PAGE3_SEL, 0x0c); freq = (reg4 << 24) | (reg3 << 16) | (reg2 << 8) | reg1; return freq; } /** * @brief calculate tx time * @param[in] * @return tx time(ms) */ uint32_t PAN3028_calculate_tx_time(void) { int bw_val; float tx_done_time; uint8_t pl = PAN3028_read_spec_page_reg(PAGE1_SEL,REG_PAYLOAD_LEN); uint8_t sf = PAN3028_get_sf(); uint8_t crc = PAN3028_get_crc(); uint8_t code_rate = PAN3028_get_code_rate(); uint8_t bw = PAN3028_get_bw(); float a,b,c,d=0.00; if(bw == 6) { bw_val = 62500; } if(bw == 7) { bw_val = 125000; } if(bw == 8) { bw_val = 250000; } if(bw == 9) { bw_val = 500000; } a = (float)(8 * pl - 4 * sf + 28 + 16 *crc) / (float)(4 * sf); b = ceil(a); c = code_rate + 4; d = ((float)((2<<(sf-1))) / bw_val); tx_done_time =(12.25 +8 + b*c)*d*1000 ; return tx_done_time + 5; } /** * @brief set bandwidth * @param[in] value relate to bandwidth * BW_62_5K / BW_125K / BW_250K / BW_500K * @return result */ uint32_t PAN3028_set_bw(uint32_t bw_val) { uint8_t temp_val_1; uint8_t temp_val_2; temp_val_1 = PAN3028_read_spec_page_reg(PAGE3_SEL, 0x0d); temp_val_2 = ((temp_val_1 & 0x0F) | (bw_val << 4)) ; if(PAN3028_write_spec_page_reg(PAGE3_SEL, 0x0d, temp_val_2) != OK) { return FAIL; } else { return OK; } } /** * @brief read bandwidth * @param[in] * @return bandwidth */ uint8_t PAN3028_get_bw(void) { uint8_t tmpreg; tmpreg = PAN3028_read_spec_page_reg(PAGE3_SEL, 0x0d); return (tmpreg & 0xff) >> 4; } /** * @brief set spread factor * @param[in] spread factor to set * SF_7 / SF_8 / SF_9 / SF_10 / SF_11 / SF_12 * @return result */ uint32_t PAN3028_set_sf(uint32_t sf_val) { uint8_t temp_val_1; uint8_t temp_val_2; if(sf_val < 7 || sf_val > 12) { return FAIL; } else { temp_val_1 = PAN3028_read_spec_page_reg(PAGE3_SEL, 0x0e); temp_val_2 = ((temp_val_1 & 0x0F) | (sf_val << 4)) ; if(PAN3028_write_spec_page_reg(PAGE3_SEL, 0x0e, temp_val_2) != OK) { return FAIL; } else { return OK; } } } /** * @brief read Spreading Factor * @param[in] * @return Spreading Factor */ uint8_t PAN3028_get_sf(void) { uint8_t tmpreg; tmpreg = PAN3028_read_spec_page_reg(PAGE3_SEL, 0x0e); return (tmpreg & 0xff) >> 4; } /** * @brief set payload CRC * @param[in] CRC to set * CRC_ON / CRC_OFF * @return result */ uint32_t PAN3028_set_crc(uint32_t crc_val) { uint8_t temp_val_1; uint8_t temp_val_2; temp_val_1 = PAN3028_read_spec_page_reg(PAGE3_SEL, 0x0e); temp_val_2 = ((temp_val_1 & 0xF7) | (crc_val << 3)) ; if(PAN3028_write_spec_page_reg(PAGE3_SEL, 0x0e, temp_val_2) != OK) { return FAIL; } else { return OK; } } /** * @brief read payload CRC * @param[in] * @return CRC status */ uint8_t PAN3028_get_crc(void) { uint8_t tmpreg; tmpreg = PAN3028_read_spec_page_reg(PAGE3_SEL, 0x0e); return (tmpreg & 0x08) >> 3; } /** * @brief set code rate * @param[in] code rate to set * CODE_RATE_45 / CODE_RATE_46 / CODE_RATE_47 / CODE_RATE_48 * @return result */ uint32_t PAN3028_set_code_rate(uint8_t code_rate) { uint8_t tmpreg = 0; tmpreg = PAN3028_read_spec_page_reg(PAGE3_SEL, 0x0d); tmpreg &= ~(0x7 << 1); tmpreg |= (code_rate << 1); if(PAN3028_write_spec_page_reg(PAGE3_SEL, 0x0d, tmpreg) != OK) { return FAIL; } else { return OK; } } /** * @brief get code rate * @param[in] * @return code rate */ uint8_t PAN3028_get_code_rate(void) { uint8_t code_rate = 0; uint8_t tmpreg = 0; tmpreg = PAN3028_read_spec_page_reg(PAGE3_SEL, 0x0d); code_rate = ((tmpreg & 0x0e) >> 1); return code_rate; } /** * @brief set rf mode * @param[in] * PAN3028_MODE_DEEP_SLEEP / PAN3028_MODE_SLEEP * PAN3028_MODE_STB1 / PAN3028_MODE_STB2 * PAN3028_MODE_STB3 / PAN3028_MODE_TX / PAN3028_MODE_RX * @return result */ uint32_t PAN3028_set_mode(uint8_t mode) { if(PAN3028_write_reg(REG_OP_MODE,mode) != OK) { return FAIL; } else { return OK; } } /** * @brief get rf mode * @param[in] * @return mode * PAN3028_MODE_DEEP_SLEEP / PAN3028_MODE_SLEEP * PAN3028_MODE_STB1 / PAN3028_MODE_STB2 * PAN3028_MODE_STB3 / PAN3028_MODE_TX / PAN3028_MODE_RX */ uint8_t PAN3028_get_mode(void) { return PAN3028_read_reg(REG_OP_MODE); } /** * @brief set rf Tx mode * @param[in] * PAN3028_TX_SINGLE/PAN3028_TX_CONTINOUS * @return result */ uint32_t PAN3028_set_tx_mode(uint8_t mode) { uint8_t tmp; tmp = PAN3028_read_spec_page_reg(PAGE3_SEL, 0x06); tmp = tmp & (~(1 << 2)); tmp = tmp | (mode << 2); if(PAN3028_write_spec_page_reg(PAGE3_SEL,0x06,tmp) != OK) { return FAIL; } else { return OK; } } /** * @brief set rf Rx mode * @param[in] * PAN3028_RX_SINGLE/PAN3028_RX_SINGLE_TIMEOUT/PAN3028_RX_CONTINOUS * @return result */ uint32_t PAN3028_set_rx_mode(uint8_t mode) { uint8_t tmp; tmp = PAN3028_read_spec_page_reg(PAGE3_SEL, 0x06); tmp = tmp & (~(3 << 0)); tmp = tmp | (mode << 0); if(PAN3028_write_spec_page_reg(PAGE3_SEL, 0x06, tmp) != OK) { return FAIL; } else { return OK; } } /** * @brief set timeout for Rx. It is useful in PAN3028_RX_SINGLE_TIMEOUT mode * @param[in] rx single timeout time(in ms) * @return result */ uint32_t PAN3028_set_timeout(uint32_t timeout) { uint8_t timeout_lsb = 0; uint8_t timeout_msb = 0; if(timeout > 0xffff) { timeout = 0xffff; } timeout_lsb = timeout & 0xff; timeout_msb = (timeout >> 8) & 0xff; if(PAN3028_write_spec_page_reg(PAGE3_SEL, 0x07, timeout_lsb) != OK) { return FAIL; } if(PAN3028_write_spec_page_reg(PAGE3_SEL, 0x08, timeout_msb) != OK) { return FAIL; } else { return OK; } } /** * @brief get snr value * @param[in] * @return snr */ float PAN3028_get_snr(void) { float snr_val=0.0; uint8_t sig_pow_l, sig_pow_m, sig_pow_h; uint8_t noise_pow_l, noise_pow_m, noise_pow_h; uint32_t sig_pow_val; uint32_t noise_pow_val; uint32_t sf_val; sig_pow_l = PAN3028_read_spec_page_reg(PAGE1_SEL,0x74); sig_pow_m = PAN3028_read_spec_page_reg(PAGE1_SEL,0x75); sig_pow_h = PAN3028_read_spec_page_reg(PAGE1_SEL,0x76); sig_pow_val = ((sig_pow_h << 16) | (sig_pow_m << 8) | sig_pow_l ); noise_pow_l = PAN3028_read_spec_page_reg(PAGE2_SEL,0x71); noise_pow_m = PAN3028_read_spec_page_reg(PAGE2_SEL,0x72); noise_pow_h = PAN3028_read_spec_page_reg(PAGE2_SEL,0x73); noise_pow_val = ((noise_pow_h << 16) | (noise_pow_m << 8) | noise_pow_l ); sf_val = (PAN3028_read_spec_page_reg(PAGE1_SEL,0x7c) & 0xf0) >> 4; if(noise_pow_val == 0) { noise_pow_val = 1; } snr_val = (float)(10 * log10((sig_pow_val / pow(2,sf_val)) / noise_pow_val)); return snr_val; } /** * @brief get cascade snr value * @param[in] * @return snr */ float PAN3028_get_snr_cascade(void) { float snr_val1=0.0; uint8_t sig_pow_l, sig_pow_m, sig_pow_h; uint8_t noise_pow_l, noise_pow_m, noise_pow_h; uint32_t sig_pow_val; uint32_t noise_pow_val; sig_pow_l = PAN3028_read_spec_page_reg(PAGE1_SEL, 0x74); sig_pow_m = PAN3028_read_spec_page_reg(PAGE1_SEL, 0x75); sig_pow_h = PAN3028_read_spec_page_reg(PAGE1_SEL, 0x76); sig_pow_val = ( (sig_pow_h << 16) | (sig_pow_m << 8) | sig_pow_l ); noise_pow_l = PAN3028_read_spec_page_reg(PAGE2_SEL,0x71); noise_pow_m = PAN3028_read_spec_page_reg(PAGE2_SEL,0x72); noise_pow_h = PAN3028_read_spec_page_reg(PAGE2_SEL,0x73); noise_pow_val = ((noise_pow_h << 16) | (noise_pow_m << 8) | noise_pow_l ); if(noise_pow_val == 0) { noise_pow_val = 1; } snr_val1 = (float)(10 * log10((sig_pow_val)/noise_pow_val)); return snr_val1; } /** * @brief get rssi value * @param[in] * @return rssi */ float PAN3028_get_rssi(void) { float rssi_val; int rssi_mix_val; int bw_pow_val; float snr; int bw_val = PAN3028_get_bw(); switch(bw_val) { case 6 : bw_pow_val = 9; break; case 7 : bw_pow_val = 6; break; case 8: bw_pow_val = 3; break; case 9: bw_pow_val = 0; break; } snr = PAN3028_get_snr(); PAN3028_get_snr_cascade(); if(snr < 6) { rssi_val = snr - 113 - bw_pow_val; }else{ rssi_mix_val = PAN3028_read_spec_page_reg(PAGE1_SEL,0x7e); rssi_val = rssi_mix_val - 256; } return rssi_val; } /** * @brief set tx_power * @param[in] Reference datasheet for tx_power parameter description * @return result */ uint32_t PAN3028_set_tx_power(uint8_t tx_power) { uint8_t pa_1st_pwr, pa_2nd_pwr, reg_val; pa_1st_pwr = (tx_power >> 4) & 0x07; pa_2nd_pwr = tx_power & 0x0f; if(pa_1st_pwr < 0x7) { pa_2nd_pwr = 0x0; } reg_val = (pa_2nd_pwr << 4) | pa_1st_pwr; return PAN3028_write_spec_page_reg(PAGE1_SEL, 0x63, reg_val); } /** * @brief get tx_power * @param[in] * @return tx_power */ uint32_t PAN3028_get_tx_power(void) { uint8_t pa_1st_pwr, pa_2nd_pwr, reg_val; reg_val = PAN3028_read_spec_page_reg(PAGE1_SEL, 0x63); pa_1st_pwr = reg_val & 0x07; pa_2nd_pwr = (reg_val >> 4) & 0x0f; return ((pa_1st_pwr << 4) | pa_2nd_pwr); } /** * @brief set preamble * @param[in] preamble * @return result */ uint32_t PAN3028_set_preamble(uint16_t reg) { uint8_t tmp_value; tmp_value = reg & 0xff; if(PAN3028_write_spec_page_reg(PAGE3_SEL,0x13, tmp_value) != OK) { return FAIL; } tmp_value = (reg >> 8) & 0xff; if(PAN3028_write_spec_page_reg(PAGE3_SEL,0x14, tmp_value) != OK) { return FAIL; } return OK; } /** * @brief set RF GPIO as input * @param[in] pin number of GPIO to be enable * @return result */ uint32_t PAN3028_set_gpio_input(uint8_t gpio_pin) { uint8_t tmpreg = 0; if(gpio_pin < 8) { tmpreg = PAN3028_read_spec_page_reg(PAGE0_SEL, 0x63); tmpreg |= (1 << gpio_pin); if(PAN3028_write_spec_page_reg(PAGE0_SEL, 0x63, tmpreg) != OK) { return FAIL; } else { return OK; } } else { tmpreg = PAN3028_read_spec_page_reg(PAGE0_SEL, 0x64); tmpreg |= (1 << (gpio_pin - 8)); if(PAN3028_write_spec_page_reg(PAGE0_SEL, 0x64, tmpreg) != OK) { return FAIL; } else { return OK; } } } /** * @brief set RF GPIO as output * @param[in] pin number of GPIO to be enable * @return result */ uint32_t PAN3028_set_gpio_output(uint8_t gpio_pin) { uint8_t tmpreg = 0; if(gpio_pin < 8) { tmpreg = PAN3028_read_spec_page_reg(PAGE0_SEL, 0x65); tmpreg |= (1 << gpio_pin); if(PAN3028_write_spec_page_reg(PAGE0_SEL, 0x65, tmpreg) != OK) { return FAIL; } else { return OK; } } else { tmpreg = PAN3028_read_spec_page_reg(PAGE0_SEL, 0x66); tmpreg |= (1 << (gpio_pin - 8)); if(PAN3028_write_spec_page_reg(PAGE0_SEL, 0x66, tmpreg) != OK) { return FAIL; } else { return OK; } } } /** * @brief set GPIO output state, SET or RESET * @param[in] pin number of GPIO to be opearted * 0 - reset, * 1 - set * @return result */ uint32_t PAN3028_set_gpio_state(uint8_t gpio_pin, uint8_t state) { uint8_t tmpreg = 0; if(gpio_pin < 8) { tmpreg = PAN3028_read_spec_page_reg(PAGE0_SEL, 0x67); if(state == 0) { tmpreg &= ~(1 << gpio_pin); } else { tmpreg |= (1 << gpio_pin); } if(PAN3028_write_spec_page_reg(PAGE0_SEL, 0x67, tmpreg) != OK) { return FAIL; } else { return OK; } } else { tmpreg = PAN3028_read_spec_page_reg(PAGE0_SEL, 0x68); if(state == 0) { tmpreg &= ~(1 << (gpio_pin - 8)); } else { tmpreg |= (1 << (gpio_pin - 8)); } if(PAN3028_write_spec_page_reg(PAGE0_SEL, 0x68, tmpreg) != OK) { return FAIL; } else { return OK; } } } /** * @brief CAD function enable * @param[in] * @return result */ uint32_t PAN3028_cad_en(void) { PAN3028_set_gpio_output(11); if(PAN3028_write_spec_page_reg(PAGE1_SEL, 0x0f, 0x10) != OK) { return FAIL; } return OK; } /** * @brief set rf syncword * @param[in] syncword * @return result */ uint32_t PAN3028_set_syncword(uint32_t sync) { if(PAN3028_write_spec_page_reg(PAGE3_SEL, 0x0f, sync) != OK) { return FAIL; } else { return OK; } } /** * @brief read rf syncword * @param[in] * @return syncword */ uint8_t PAN3028_get_syncword(void) { uint8_t tmpreg; tmpreg = PAN3028_read_spec_page_reg(PAGE3_SEL, 0x0f); return tmpreg; } /** * @brief send one packet * @param[in] buffer contain data to send * @param[in] the length of data to send * @return result */ uint32_t PAN3028_send_packet(uint8_t *buff, uint32_t len) { if(PAN3028_write_spec_page_reg(PAGE1_SEL,REG_PAYLOAD_LEN,len) != OK) { return FAIL; } if(PAN3028_write_reg(REG_OP_MODE,PAN3028_MODE_TX) != OK) { return FAIL; } else { PAN3028_write_fifo(REG_FIFO_ACC_ADDR,buff,len); return OK; } } /** * @brief receive a packet in non-block method, it will return 0 when no data got * @param[in] buffer provide for data to receive * @return length, it will return 0 when no data got */ uint8_t PAN3028_recv_packet(uint8_t *buff) { uint32_t len = 0; len = PAN3028_read_spec_page_reg(PAGE1_SEL, 0x7D); PAN3028_read_fifo(REG_FIFO_ACC_ADDR,buff,len); /* clear rx done irq */ PAN3028_clr_irq(); return len; } /** * @brief set early interruption * @param[in] PLHD IRQ to set * PLHD_IRQ_ON / PLHD_IRQ_OFF * @return result */ uint32_t PAN3028_set_early_irq(uint32_t earlyirq_val) { uint8_t temp_val_1; uint8_t temp_val_2; temp_val_1 = PAN3028_read_spec_page_reg(PAGE1_SEL, 0x2d); temp_val_2 = ((temp_val_1 & 0x7f) | (earlyirq_val << 7)) ; if(PAN3028_write_spec_page_reg(PAGE1_SEL, 0x2d, temp_val_2) != OK) { return FAIL; } else { return OK; } } /** * @brief read plhd irq status * @param[in] * @return plhd irq status */ uint8_t PAN3028_get_early_irq(void) { uint8_t tmpreg; tmpreg = PAN3028_read_spec_page_reg(PAGE1_SEL, 0x2d); return tmpreg; } /** * @brief set plhd * @param[in] PLHD start addr,Range:0..7f * PLHD len * PLHD_LEN8 / PLHD_LEN16 * @return result */ uint32_t PAN3028_set_plhd(uint8_t addr,uint8_t len) { uint8_t temp_val_2; temp_val_2 = ((addr & 0x7f) | (len << 7)) ; if(PAN3028_write_spec_page_reg(PAGE1_SEL, 0x2e, temp_val_2) != OK) { return FAIL; } else { return OK; } } /** * @brief read plhd status * @param[in] * @return plhd status */ uint8_t PAN3028_get_plhd(void) { uint8_t tmpreg; tmpreg = PAN3028_read_spec_page_reg(PAGE1_SEL, 0x2e); return ((tmpreg & 0x80) >> 7); } /** * @brief set plhd mask * @param[in] plhd mask to set * PLHD_ON / PLHD_OFF * @return result */ uint32_t PAN3028_set_plhd_mask(uint32_t plhd_val) { uint8_t temp_val_1; uint8_t temp_val_2; temp_val_1 = PAN3028_read_spec_page_reg(PAGE0_SEL, 0x58); temp_val_2 = ((temp_val_1 & 0xef) | (plhd_val << 4)) ; if(PAN3028_write_spec_page_reg(PAGE0_SEL, 0x58, temp_val_2) != OK) { return FAIL; } else { return OK; } } /** * @brief read plhd mask * @param[in] * @return plhd mask */ uint8_t PAN3028_get_plhd_mask(void) { uint8_t tmpreg; tmpreg = PAN3028_read_spec_page_reg(PAGE0_SEL, 0x58); return tmpreg; } /** * @brief receive 8 bytes plhd data * @param[in] buffer provide for data to receive * @return result */ uint8_t PAN3028_recv_plhd8(uint8_t *buff) { uint32_t i,len = 8; for(i = 0; i < len; i++) { buff[i] = PAN3028_read_spec_page_reg(PAGE2_SEL, 0x76 + i); } PAN3028_clr_irq(); return len; } /** * @brief receive 16 bytes plhd data * @param[in] buffer provide for data to receive * @return result */ uint8_t PAN3028_recv_plhd16(uint8_t *buff) { uint32_t i,len = 16; for(i = 0; i < len; i++) { if(i<10) { buff[i] = PAN3028_read_spec_page_reg(PAGE2_SEL, 0x76 + i); }else{ buff[i] = PAN3028_read_spec_page_reg(PAGE0_SEL, 0x76 + i - 10); } } PAN3028_clr_irq(); return len; } /** * @brief receive a packet in non-block method, it will return 0 when no data got * @param[in] buffer provide for data to receive * PLHD_LEN8 / PLHD_LEN16 * @return result */ uint32_t PAN3028_plhd_receive(uint8_t *buf,uint8_t len) { if(len == PLHD_LEN8) { return PAN3028_recv_plhd8(buf); }else if (len == PLHD_LEN16) { return PAN3028_recv_plhd16(buf); } return FAIL; } /** * @brief set dcdc mode, The default configuration is DCDC_OFF, PAN3028 should set DCDC_OFF before enter sleep/deepsleep * @param[in] dcdc switch * DCDC_ON / DCDC_OFF * @return result */ uint32_t PAN3028_set_dcdc_mode(uint32_t dcdc_val) { uint8_t temp_val_1; uint8_t temp_val_2; temp_val_1 = PAN3028_read_spec_page_reg(PAGE3_SEL, 0x1e); temp_val_2 = ((temp_val_1 & 0xfe) | (dcdc_val << 0)) ; if(PAN3028_write_spec_page_reg(PAGE3_SEL, 0x1e, temp_val_2) != OK) { return FAIL; } else { return OK; } } /** * @brief set LDR mode * @param[in] LDR switch * LDR_ON / LDR_OFF * @return result */ uint32_t PAN3028_set_ldr(uint32_t mode) { uint8_t temp_val_1; uint8_t temp_val_2; temp_val_1 = PAN3028_read_spec_page_reg(PAGE3_SEL, 0x12); temp_val_2 = ((temp_val_1 & 0xF7) | (mode << 3)) ; if(PAN3028_write_spec_page_reg(PAGE3_SEL, 0x12, temp_val_2) != OK) { return FAIL; } else { return OK; } } /** * @brief set preamble by Spreading Factor,It is useful in all_sf_search mode * @param[in] Spreading Factor * @return result */ uint32_t PAN3028_set_all_sf_preamble(uint32_t sf) { switch(sf) { case 7: return (PAN3028_write_spec_page_reg(PAGE3_SEL, 0x13, 128) == OK); case 8: return (PAN3028_write_spec_page_reg(PAGE3_SEL, 0x13, 80) == OK); case 9: return (PAN3028_write_spec_page_reg(PAGE3_SEL, 0x13, 48) == OK); case 10: return (PAN3028_write_spec_page_reg(PAGE3_SEL, 0x13, 24) == OK); case 11: return (PAN3028_write_spec_page_reg(PAGE3_SEL, 0x13, 16) == OK); case 12: return (PAN3028_write_spec_page_reg(PAGE3_SEL, 0x13, 12) == OK); default: return FAIL; } } /** * @brief open all sf auto-search mode * @param[in] * @return result */ uint32_t PAN3028_set_all_sf_search(void) { uint8_t tmp_val; tmp_val = (PAN3028_read_spec_page_reg(PAGE3_SEL,0x12) | 0x01); if(PAN3028_write_spec_page_reg(PAGE3_SEL,0x12,tmp_val) != OK) { return FAIL; } if(PAN3028_write_spec_page_reg(PAGE1_SEL,0x2d,0x3f) != OK) { return FAIL; } if(PAN3028_write_spec_page_reg(PAGE0_SEL,0x06,0x01) != OK) { return FAIL; } if(PAN3028_write_spec_page_reg(PAGE0_SEL,0x4a,0xae) != OK) { return FAIL; } if(PAN3028_write_spec_page_reg(PAGE0_SEL,0x55,0x02) != OK) { return FAIL; } if(PAN3028_write_spec_page_reg(PAGE1_SEL,0x0f,0x0a) != OK) { return FAIL; } return OK; } /** * @brief close all sf auto-search mode * @param[in] * @return result */ uint32_t PAN3028_set_all_sf_search_off(void) { uint8_t tmp_val; tmp_val = (PAN3028_read_spec_page_reg(PAGE3_SEL,0x12) & 0xFE); if(PAN3028_write_spec_page_reg(PAGE3_SEL,0x12,tmp_val) != OK) { return FAIL; } else { return OK; } } /** * @brief RF IRQ server routine, it should be call at ISR of IRQ pin * @param[in] * @return result */ void PAN3028_irq_handler(void) { double snr,rssi,plhd_len; uint16_t size = 0; uint8_t irq = PAN3028_get_irq(); if(irq & REG_IRQ_RX_PLHD_DONE) { plhd_len = PAN3028_get_plhd(); size = PAN3028_plhd_receive(plhd_buf,plhd_len); rf_rx_plhddone_event( plhd_buf, size ); }else if(irq & REG_IRQ_RX_DONE) { snr = PAN3028_get_snr(); rssi = PAN3028_get_rssi(); size = PAN3028_recv_packet(RadioRxPayload); rf_rx_done_event( RadioRxPayload, size, rssi, snr ); } else if(irq & REG_IRQ_CRC_ERR) { rf_rx_err_event(); PAN3028_clr_irq(); } else if(irq & REG_IRQ_RX_TIMEOUT) { rf_rx_timeout_event(); PAN3028_clr_irq(); } else if(irq & REG_IRQ_TX_DONE) { rf_tx_done_event(); PAN3028_clr_irq(); } } /** * @brief set carrier tx power * @param[in] * @return result */ uint32_t PAN3028_set_carrier_tx_power(uint8_t tx_power) { uint8_t pa_1st_pwr, pa_2nd_pwr, reg_val; pa_1st_pwr = (tx_power >> 4) & 0x07; pa_2nd_pwr = tx_power & 0x0f; if(pa_1st_pwr < 0x7) { pa_2nd_pwr = 0x0; } reg_val = (pa_2nd_pwr << 4) | pa_1st_pwr; return PAN3028_write_spec_page_reg(PAGE1_SEL, 0x65, reg_val); } /** * @brief get carrier tx power * @param[in] * @return carrier tx power */ uint32_t PAN3028_get_carrier_tx_power(void) { uint8_t pa_1st_pwr, pa_2nd_pwr, reg_val; reg_val = PAN3028_read_spec_page_reg(PAGE1_SEL, 0x65); pa_1st_pwr = reg_val & 0x07; pa_2nd_pwr = (reg_val >> 4) & 0x0f; return ((pa_1st_pwr << 4) | pa_2nd_pwr); } /** * @brief set wave test mode * @param[in] none * @return result */ uint32_t PAN3028_set_carrier_wave_test_mode(void) { if(PAN3028_write_reg(0x02, 0x01) != OK) { return FAIL; } rf_port.delayms(8); if(PAN3028_write_reg(0x04, 0x16) != OK) { return FAIL; } rf_port.delayms(8); if(PAN3028_write_reg(0x04, 0x56) != OK) { return FAIL; } rf_port.delayms(8); if(PAN3028_write_reg(0x04, 0x76) != OK) { return FAIL; } rf_port.delayms(8); rf_port.antenna_init(); rf_port.tcxo_init(); rf_port.delayms(8); if(PAN3028_write_reg(0x04, 0xF6) != OK) { return FAIL; } rf_port.delayms(8); if(PAN3028_write_reg(0x02, 0x02) != OK) { return FAIL; } rf_port.delayms(8); if(PAN3028_write_spec_page_reg(PAGE3_SEL, 0x24, 0x60) != OK) { return FAIL; } rf_port.delayms(8); if(PAN3028_write_reg(0x02, 0x03) != OK) { return FAIL; } rf_port.delayms(8); if(PAN3028_write_spec_page_reg(PAGE3_SEL, 0x24, 0xE0) != OK) { return FAIL; } rf_port.delayms(8); if(PAN3028_write_reg(0x02, 0x04) != OK) { return FAIL; } rf_port.delayms(8); if(PAN3028_write_spec_page_reg(PAGE3_SEL, 0x24, 0xF0) != OK) { return FAIL; } rf_port.delayms(8); if(PAN3028_write_spec_page_reg(PAGE0_SEL, 0x4a, 0x8e) != OK) { return FAIL; } rf_port.delayms(8); if(PAN3028_write_spec_page_reg(PAGE3_SEL, 0x18, 0x0f) != OK) { return FAIL; } rf_port.delayms(8); if(PAN3028_write_spec_page_reg(PAGE3_SEL, 0x15, 0x58) != OK) { return FAIL; } rf_port.delayms(8); if(PAN3028_write_spec_page_reg(PAGE3_SEL, 0x16, 0x64) != OK) { return FAIL; } rf_port.delayms(8); if(PAN3028_write_spec_page_reg(PAGE3_SEL, 0x17, 0x00) != OK) { return FAIL; } rf_port.delayms(8); if(PAN3028_write_spec_page_reg(PAGE1_SEL, 0x66, 0x7f) != OK) { return FAIL; } rf_port.delayms(8); if(PAN3028_write_spec_page_reg(PAGE1_SEL, 0x65, 0xf7) != OK) { return FAIL; } rf_port.delayms(8); if(PAN3028_write_spec_page_reg(PAGE0_SEL, 0x17, 0x08) != OK) { return FAIL; } rf_port.delayms(8); if(PAN3028_write_spec_page_reg(PAGE0_SEL, 0x18, 0x28) != OK) { return FAIL; } rf_port.delayms(8); return OK; }