/* write one page to flash */ int nrf_write_page(devp dev, int page, void *data){ unsigned char *b = (unsigned char *)data; unsigned char cmd[2], ret; int block; if(page < 0 || page > 63){ /* invalid page */ return -1; } /* send the flash-write command */ cmd[0] = 0x02; cmd[1] = (unsigned char)page; if(nrf_cmd(dev, cmd, sizeof(cmd), &ret, sizeof(ret)) || ret){ return -1; } /* send a page of memory, one block at a time */ for(block = 0; block < 8; block++){ if(nrf_cmd(dev, &b[block * 64], 64, &ret, sizeof(ret)) || ret){ return -2; } } return 0; }
/* compare/verify that a block on the device is identical to the one in data * returns result of memcpy() been device and data */ int nrf_compare_block(devp dev, int block, void *data){ unsigned char cmd[2], devblock[64]; /* request the block */ cmd[0] = 0x03; cmd[1] = (unsigned char)block; if(nrf_cmd(dev, cmd, 2, devblock, 64)){ return -1; } return memcmp(data, devblock, 64); }
// High-Level: void nrf_rcv_pkt_start(void){ nrf_write_reg(R_CONFIG, R_CONFIG_PRIM_RX| // Receive mode R_CONFIG_PWR_UP| // Power on R_CONFIG_EN_CRC // CRC on, single byte ); nrf_cmd(C_FLUSH_RX); nrf_write_reg(R_STATUS,0); CE_HIGH(); };
const char *nrf_version_str(devp dev){ static unsigned char verbin[2]; static char verstr[4]; static unsigned char vercmd = 0x01; if(nrf_cmd(dev, &vercmd, 1, verbin, sizeof(verbin))){ return "?.?"; } else { verstr[0] = verbin[0] + '0'; verstr[1] = '.'; verstr[2] = verbin[0] + '0'; verstr[3] = '\0'; return verstr; } }
void nrf_rcv_pkt_end(void) { CE_LOW(); nrf_cmd(C_FLUSH_RX); nrf_write_reg(R_STATUS,R_STATUS_RX_DR); };
void main_bridge(void) { GLOBAL(daytrig)=10; GLOBAL(lcdbacklight)=10; GLOBAL(privacy) = 3; char input[64]; char led1=0; char led2=0; usbCDCInit(); delayms(500); nrf_init(); nrf_config_set(&config); nrf_rcv_pkt_start(R_CONFIG_EN_CRC); while(1){ int l, i, status; CDC_OutBufAvailChar (&l); if(l>0){ gpioSetValue (RB_LED0, led1);led1=1-led1; CDC_RdOutBuf (input, &l); for(i=0; i<l; i++){ uint8_t cmd = serialmsg_put(input[i]); if( cmd != SERIAL_NONE ){ switch( cmd ){ case '1': // can we loose packets here? nrf_rcv_pkt_end(); status=snd_pkt_no_crc(serialmsg_len, serialmsg_message); //status=nrf_snd_pkt_crc(serialmsg_len, serialmsg_message); nrf_rcv_pkt_start(R_CONFIG_EN_CRC); break; case '3': memcpy(config.txmac, serialmsg_message, 5); nrf_write_long(C_W_REGISTER|R_TX_ADDR,5,config.txmac); break; case '4': memcpy(config.mac0, serialmsg_message, 5); nrf_write_long(C_W_REGISTER|R_RX_ADDR_P0,5,config.mac0); nrf_write_reg(R_EN_RXADDR,1); break; case '5': config.channel=serialmsg_message[0]; nrf_set_channel(config.channel); nrf_cmd(C_FLUSH_RX); break; case '6': config.maclen[0]=serialmsg_message[0]; nrf_write_reg(R_RX_PW_P0,config.maclen[0]); break; case '7': puts("\\7"); char s[sizeof(uint32_t)+1]; *((uint32_t*)s) =GetUUID32(); s[sizeof(uint32_t)]=0; puts(s); puts("\\0"); break; case '8': /* set mac width */ nrf_write_reg(R_SETUP_AW,serialmsg_message[0]); break; case '9': // Dis/Enable CRC nrf_write_reg(R_CONFIG, R_CONFIG_PRIM_RX|R_CONFIG_PWR_UP| ((serialmsg_message[0]&1)?R_CONFIG_EN_CRC :0)| ((serialmsg_message[0]&2)?R_CONFIG_CRCO :0) ); /* maybe add enhanced shockburst stuff here */ nrf_cmd(C_FLUSH_RX); nrf_write_reg(R_STATUS,0); break; }; puts("\\2\\0"); } } } int len; uint8_t buf[32]; len=nrf_rcv_pkt_poll(sizeof(buf),buf); if( len > 0 ){ gpioSetValue (RB_LED2, led2);led2=1-led2; puts("\\1"); dump_encoded(len, buf); puts("\\0"); } } }
void APP_Run(void) { static const uint8_t chRf[] = {2, 26,80}; static const uint8_t chLe[] = {37,38,39}; uint8_t i, L, ch = 0; uint8_t buf[32]; nrf_cmd(0x20, 0x12); //on, no crc, int on RX/TX done nrf_cmd(0x21, 0x00); //no auto-acknowledge nrf_cmd(0x22, 0x00); //no RX nrf_cmd(0x23, 0x02); //5-byte address nrf_cmd(0x24, 0x00); //no auto-retransmit nrf_cmd(0x26, 0x06); //1MBps at 0dBm nrf_cmd(0x27, 0x3E); //clear various flags nrf_cmd(0x3C, 0x00); //no dynamic payloads nrf_cmd(0x3D, 0x00); //no features nrf_cmd(0x31, 32); //always RX 32 bytes nrf_cmd(0x22, 0x01); //RX on pipe 0 buf[0] = 0x30; //set addresses buf[1] = swapbits(0x8E); buf[2] = swapbits(0x89); buf[3] = swapbits(0xBE); buf[4] = swapbits(0xD6); #if 0 nrf_manybytes(buf, 5); #else RF1_WriteRegisterData(RF1_TX_ADDR, &buf[1], 4); #endif buf[0] = 0x2A; #if 0 nrf_manybytes(buf, 5); #else RF1_WriteRegisterData(RF1_RX_ADDR_P0, &buf[1], 4); #endif while(1) { L = 0; buf[L++] = 0x40; //PDU type, given address is random buf[L++] = 11; //17 bytes of payload buf[L++] = MY_MAC_0; buf[L++] = MY_MAC_1; buf[L++] = MY_MAC_2; buf[L++] = MY_MAC_3; buf[L++] = MY_MAC_4; buf[L++] = MY_MAC_5; buf[L++] = 2; //flags (LE-only, limited discovery mode) buf[L++] = 0x01; buf[L++] = 0x05; buf[L++] = 7; buf[L++] = 0x08; buf[L++] = 'n'; buf[L++] = 'R'; buf[L++] = 'F'; buf[L++] = ' '; buf[L++] = 'L'; buf[L++] = 'E'; buf[L++] = 0x55; //CRC start value: 0x555555 buf[L++] = 0x55; buf[L++] = 0x55; if(++ch == sizeof(chRf)) { /* channel hopping */ ch = 0; } //nrf_cmd(0x25, chRf[ch]); (void)RF1_SetChannel(chRf[ch]); //nrf_cmd(0x27, 0x6E); //clear flags RF1_WriteRegister(RF1_STATUS, RF1_STATUS_RX_DR|RF1_STATUS_TX_DS|RF1_STATUS_RX_P_NO_RX_FIFO_EMPTY); /* clear flags */ btLePacketEncode(buf, L, chLe[ch]); //nrf_simplebyte(0xE2); //Clear RX Fifo RF1_Write(RF1_FLUSH_RX); /* flush old data */ //nrf_simplebyte(0xE1); //Clear TX Fifo //RF1_Write(RF1_FLUSH_TX); /* flush old data */ /* done in RF1_TxPayload() */ RF1_TxPayload(buf, L); #if 0 cbi(PORTB, PIN_nCS); spi_byte(0xA0); for(i = 0 ; i < L ; i++) { spi_byte(buf[i]); } sbi(PORTB, PIN_nCS); nrf_cmd(0x20, 0x12); //tx on sbi(PORTB, PIN_CE); //do tx delay_ms(10); cbi(PORTB, PIN_CE); (in preparation of switching to RX quickly); #endif LED1_Neg(); WAIT1_Waitms(10); } /* for */ }
void main (void) { static const uint8_t chRf[] = {2, 26,80}; static const uint8_t chLe[] = {37,38,39}; uint8_t i, L, ch = 0; // initialize buffer with set tx address command // address 8E 89 BE D6 bit-reversed // static rather than local saves 18 bytes static uint8_t buf[32]; //= {0x30, 0x6B, 0x7D, 0x91, 0x71}; SPI_init(); nrf_cmd(0x20, 0x12); //on, no crc, int on RX/TX done _delay_ms(2); // Tpd2stby //nrf_cmd(0x22, 0x00); //no RX nrf_cmd(0x21, 0x00); //no auto-acknowledge //nrf_cmd(0x22, 0x01); //RX on pipe 0 nrf_cmd(0x23, 0x02); //4-byte address nrf_cmd(0x24, 0x00); //no auto-retransmit //nrf_cmd(0x26, 0x06); //1MBps at 0dBm nrf_cmd(0x26, 0x00); //1MBps at -18dBm //nrf_cmd(0x27, 0x3E); //clear various flags //nrf_cmd(0x31, 32); //always RX 32 bytes buf[0] = 0x30; //set tx address buf[1] = swapbits(0x8E); buf[2] = swapbits(0x89); buf[3] = swapbits(0xBE); buf[4] = swapbits(0xD6); nrf_manybytes(buf, 5); //buf[0] = 0x2A; // rx address P0 //nrf_manybytes(buf, 5); while(1){ if(++ch == sizeof(chRf)){ ch = 0; sbi(NRF_PORT, DEBUG_PIN); _delay_ms(109); // wait between advertisements cbi(NRF_PORT, DEBUG_PIN); } L = 0; buf[L++] = 0x02; //PDU type, given address is random buf[L++] = 11; //11 + 6 = 17 bytes of payload buf[L++] = MY_MAC_0; buf[L++] = MY_MAC_1; buf[L++] = MY_MAC_2; buf[L++] = MY_MAC_3; buf[L++] = MY_MAC_4; buf[L++] = MY_MAC_5; buf[L++] = 2; //flags (LE-only, limited discovery mode) buf[L++] = 0x01; buf[L++] = 0x05; buf[L++] = 7; buf[L++] = 0x08; buf[L++] = 'n'; buf[L++] = 'R'; buf[L++] = 'F'; buf[L++] = ' '; buf[L++] = 'L'; buf[L++] = 'E'; buf[L++] = 0x55; //CRC start value: 0x555555 buf[L++] = 0x55; buf[L++] = 0x55; btLePacketEncode(buf, L, chLe[ch]); nrf_cmd(0x25, chRf[ch]); nrf_cmd(0x27, 0x6E); //clear flags //nrf_simplebyte(0xE2); //Clear RX Fifo //nrf_simplebyte(0xE1); //Clear TX Fifo cbi(NRF_PORT, PIN_nCS); spi_byte(0xA0); for(i = 0 ; i < L ; i++) spi_byte(buf[i]); sbi(NRF_PORT, PIN_nCS); //nrf_cmd(0x20, 0x12); //tx on // no CE pulse required when CE tied high sbi(NRF_PORT, PIN_CE); //do tx _delay_us(15); cbi(NRF_PORT, PIN_CE); // only delay for debugging - i.e. check IRQ trigger //_delay_ms(1); // let tx finish } }
/* write fn to device */ int nrf_program_nrf24lu(devp dev, const char *fn){ unsigned char cmd[2], data[64], *flash_copy, dirty_bv[64]; FILE *fp = NULL; int ecode, block, page, rc; unsigned int first_addr, last_addr; IHexRecord record; printf("Programming nRF24LU\n"); /* read the entire ROM into memory * * Why? Because Nordic doesn't have decent software. Their flash write * command erases the page first, instead of letting me decide if the page * should be erased first. * * Byte-level writing is offered by using a read-modify-write operation. * It would be smarter to operate on a per-page basis, but Intel HEX files * are not guaranteed to have sequential addressing. Since Nordic screwed * up and wild IHX files are adhoc, let's work with the whole 32k flash * memory at once. */ if((flash_copy = malloc(FLASH_SIZE)) == NULL){ ecode = -4; goto err; } printf("[*] Reading device.\n"); for(block = 0; block < 0x200; block++){ /* set address MSB */ if((block % 0x100) == 0){ cmd[0] = 0x06; cmd[1] = (unsigned char)(block / 256); if(nrf_cmd(dev, cmd, 2, data, 1)){ ecode = -2; goto err; } } /* request the block */ cmd[0] = 0x03; cmd[1] = (unsigned char)block; if(nrf_cmd(dev, cmd, 2, &flash_copy[block2addr(block)], 64)){ ecode = -2; goto err; } } /* overwrite in-memory with IHX contents */ if((fp = fopen(fn, "r")) == NULL){ ecode = -1; goto err; } memset(dirty_bv, 0, sizeof(dirty_bv)); while((rc = Read_IHexRecord(&record, fp)) == IHEX_OK && record.type != IHEX_TYPE_01){ if(record.type != IHEX_TYPE_00){ /* we cannot process segment or linear ihex files */ printf("[!] IHX file contains segment or linear addressing.\n"); ecode = -5; goto err; } /* first and last byte that is touched by this record */ first_addr = record.address; last_addr = record.address + record.dataLen - 1; if(!addr_valid(first_addr) || !addr_valid(last_addr)){ printf("[!] IHX record touches invalid or protected bytes: " "0x%04X - 0x%04X\n", first_addr, last_addr); ecode = -6; goto err; } /* only write if the ihx record changes memory */ if(memcmp(&flash_copy[first_addr], record.data, record.dataLen)){ /* copy record into memory */ memcpy(&flash_copy[first_addr], record.data, record.dataLen); /* mark dirty blocks */ for(block = addr2block(first_addr); block <= addr2block(last_addr); block++){ /* check the block since we might have some *really* long * record */ if(!addr_valid(block2addr(block))){ printf("[!] IHX record touches invalid or protected bytes:" " 0x%04X\n", block2addr(block)); ecode = -6; goto err; } bitset(dirty_bv, block); } } } if(rc != IHEX_OK && rc != IHEX_ERROR_EOF){ /* we had an error that isn't EOF */ ecode = -4; goto err; } /* write to flash */ printf("[*] Writing device"); fflush(stdout); for(page = 0; page < 64; page++){ /* Each page is 8 blocks, which conviently maps to our dirty bit vector * on byte boundries. */ if(dirty_bv[page]){ printf("."); fflush(stdout); if(nrf_write_page(dev, page, &flash_copy[page2addr(page)])){ printf("\n[!] Write failed.\n"); ecode = -7; goto err; } } } printf("\n"); /* verify */ printf("[*] Verifying device"); fflush(stdout); for(block = 0; block < 512; block++){ if(bitisset(dirty_bv, block)){ if(nrf_compare_block(dev, block, &flash_copy[block2addr(block)])){ printf("\n[!] Block %d failed.\n", block); ecode = -8; goto err; } else { printf("."); fflush(stdout); } } } printf("\n"); ecode = 0; err: if(flash_copy){ free(flash_copy); } if(fp){ fclose(fp); } return ecode; }
/* dump all of device flash to fn */ int nrf_dump(devp dev, const char *fn){ unsigned char cmd[2], data[64]; FILE *fp = NULL; int ecode, block, sub_block; IHexRecord record; if((fp = fopen(fn, "w+")) == NULL){ ecode = -1; goto err; } /* dump */ for(block = 0; block < 0x200; block++){ /* set address MSB */ if((block % 0x100) == 0){ cmd[0] = 0x06; cmd[1] = (unsigned char)(block / 256); if(nrf_cmd(dev, cmd, 2, data, 1)){ ecode = -2; goto err; } } /* request the block */ cmd[0] = 0x03; cmd[1] = (unsigned char)block; if(nrf_cmd(dev, cmd, 2, data, 64)){ ecode = -2; goto err; } /* write two records for this block * records are only written if the block contains something not 0xFF */ for(sub_block = 0; sub_block < 2; sub_block++){ record.type = IHEX_TYPE_00; memcpy(record.data, &data[sub_block * 32], 32); record.dataLen = 32; record.address = block * 64 + sub_block * 32; if(memnotchr(record.data, 0xFF, 32) && Write_IHexRecord(&record, fp)){ ecode = -3; goto err; } } } /* write EoF record */ record.type = IHEX_TYPE_01; record.address = 0; record.dataLen = 0; if(Write_IHexRecord(&record, fp)){ ecode = -3; goto err; } ecode = 0; err: if(fp){ fclose(fp); } return ecode; }
// High-Level: int nrf_rcv_pkt_time_encr(int maxtime, int maxsize, uint8_t * pkt, uint32_t const key[4]){ uint8_t len; uint8_t status=0; uint16_t cmpcrc; nrf_write_reg(R_CONFIG, R_CONFIG_PRIM_RX| // Receive mode R_CONFIG_PWR_UP| // Power on R_CONFIG_EN_CRC // CRC on, single byte ); nrf_cmd(C_FLUSH_RX); nrf_write_reg(R_STATUS,0); CE_HIGH(); for(int i=0;i<maxsize;i++) pkt[i] = 0x00; // Sanity: clear packet buffer #define LOOPY 10 for (;maxtime >= LOOPY;maxtime-=LOOPY){ delayms(LOOPY); status =nrf_cmd_status(C_NOP); if( (status & R_STATUS_RX_DR) == R_STATUS_RX_DR){ if( (status & R_STATUS_RX_P_NO) == R_STATUS_RX_FIFO_EMPTY){ nrf_cmd(C_FLUSH_RX); delayms(1); nrf_write_reg(R_STATUS,0); continue; }else{ // Get/Check packet... nrf_read_long(C_R_RX_PL_WID,1,&len); if(len>32 || len==0){ continue; return -2; // no packet error }; if(len>maxsize){ continue; return -1; // packet too large }; nrf_read_pkt(len,pkt); if(key != NULL) xxtea_decode_words((uint32_t*)pkt,len/4,key); cmpcrc=crc16(pkt,len-2); if(cmpcrc != (pkt[len-2] <<8 | pkt[len-1])) { continue; return -3; // CRC failed }; break; }; }; }; CE_LOW(); CS_HIGH(); if(maxtime<LOOPY) return 0; // timeout return len; };