san.net.ru

AIS-приёмник из доступных комплектующих, своими руками (подробно)

Проект заинтересовал множество лиц, а у меня катострофически не хватает времени заниматься разработкой. Поэтому мной принято решение опубликовать свои наработки в свободном доступе. Прошу людей использующих информацию с этого сайта давать ссылку на первоисточник.

Технические подробности

Работа с модулем E30-170T20D на трансивере от Silicon Labs 4463 rev B1 (далее SI4463), который спроектирован на работу в диапазоне от 148-173 МГц (подходит для работы с AIS работающим на частоте ~162 МГц).

Как я изначально и предполагал, чип SI4463 подключен по рекомендованной производителем схеме, а вот управляет им микроконтроллер 8L151G (предполагаю, что это аналог STM8L). Очень радует что есть контакты «VSGR» и судя по разводке возможно перепрошить STM8 через SWIM под свои нужды. Обращаю внимание, что питанием кварцевого резонатора (26МГц) управляет вывода PB2 микросхемы STM8L. Схема коммутации микросхем на модуле ниже:

Настройка чипа SI4463 Rev 1B

Конфигурирование чипа производится с помощью фирменой утилиты от Silicon Labs WDS3.

Я не смог настроить SI4463 на аппаратное определение закодированной NRZI преамбулы AIS кадра. Чип настроен в режим RAW, а NRZI декодирование производится программно. В планах было выводить множество полезной информации о работе чипа, уровне сигнала и т. д., но руки до реализации так и не дошли (модули работают стабильно и я не вижу причины усложнять код).

Программирование STM8L

Для создания прошивки для STM8L я использовал IAR.

Исходный код:

#include <iostm8l151g6.h>

#include <D:\IAR-Projects\RawAis\radio_config_Si4463_V2.h>
const unsigned char config_array[] = RADIO_CONFIGURATION_DATA_ARRAY;

void nmea_push_char(char c);
unsigned char nmea_push_packet(unsigned 
char packet_size);

unsigned char buf[16];

void char2usart(unsigned char tx) {
  while (!USART1_SR_bit.TXE) {
    asm("nop");
  }
  USART1_DR = tx;
}

// triade to code
unsigned char t2c(unsigned char tx) {
  tx = tx & 0x0F;
  if (tx>9) tx = tx + 55;
  else tx = tx + 48;
  
  return tx;
}

void send2usart( unsigned char *buf, unsigned char len ){
  unsigned char b;
  
  for (unsigned char i=0; i<len; i++){
    b = buf[i] >> 4;
    char2usart( t2c(b) );
    
    b = buf[i];
    char2usart( t2c(b) );
    
    char2usart(0x20);
  }
  /*
  char2usart(0x0A);
  char2usart(0x0D);
  */
}



enum PH_STATE {
  PH_STATE_OFF = 0,
  PH_STATE_RESET,               // reset/restart packet handler
  PH_STATE_WAIT_FOR_SYNC,       // wait for preamble (010101..) 
and start flag (0x7e)
  PH_STATE_PREFETCH,            // 
receive first 8 bits of packet
  PH_STATE_RECEIVE_PACKET       // receive packet payload
};

// packet handler errors
enum PH_ERROR {
  PH_ERROR_NONE = 0,
  PH_ERROR_STUFFBIT,    // invalid stuff-bit
  PH_ERROR_NOEND,       // no end flag after more than 1020 bits, 
message too long
  PH_ERROR_CRC          // CRC error
};

enum PH_SYNC_STATE {
  PH_SYNC_RESET = 0,    // reset/restart sync detection
  PH_SYNC_0,            // last bit was a 
0
  PH_SYNC_1,            // last bit was a 
1
  PH_SYNC_FLAG          // detecting start flag
};
#define PH_PREAMBLE_LENGTH  8   // minimum number of 
alternating bits we need for a valid preamble
#define PH_SYNC_TIMEOUT 16      // number of 
bits we wait for a preamble to start before changing channel

volatile unsigned char ph_state = PH_STATE_RESET;
volatile unsigned char ph_last_error = PH_ERROR_NONE;
volatile unsigned char ph_radio_channel = 
0;

unsigned short int rx_bitstream; 
       // shift register with incoming data
unsigned short int rx_bit_count; 
       // bit counter for various purposes
unsigned short int rx_crc; 
             // word for AIS payload CRC calculation
unsigned char rx_one_count; 
            // counter of 1's to identify stuff bits
unsigned char rx_data_byte; 
            // byte to receive actual package data
unsigned char rx_prev_bit_NRZI; 
        // previous bit for NRZI decoding
unsigned char rx_sync_state; 
           // state of preamble and start flag detection
unsigned char rx_sync_count; 
           // length of valid bits in current sync sequence
unsigned char rx_this_bit_NRZI; 
        // current bit for NRZI decoding
unsigned char rx_bit; 
                  // current decoded bit




#define FIFO_BUFFER_SIZE 256 
                   // size of FIFO in bytes (must be 2^x)
#define FIFO_PACKETS 8 
                         // max 
number of individual packets in FIFO (must be 2^x, should be approx. FIFO_BUFFER_SIZE/avg message size)
#define FIFO_PTR_TYPE unsigned char 
            // 8 bit for FIFO smaller than 256 bytes
#define FIFO_BUFFER_MASK (FIFO_BUFFER_SIZE - 
1) // mask for easy warping of buffer
#define FIFO_PACKET_MASK (FIFO_PACKETS - 
1)  // mask for easy warping of packet table

unsigned char fifo_buffer[FIFO_BUFFER_SIZE];  // buffer to hold packet data
FIFO_PTR_TYPE fifo_packets[FIFO_PACKETS]; // table with start offsets of received packets

FIFO_PTR_TYPE fifo_bytes_in;      // counter 
for bytes written into current packet
FIFO_PTR_TYPE fifo_bytes_out;     // counter for 
bytes read from current packet
volatile unsigned char fifo_packet_in; 
   // table index of incoming packet
unsigned char fifo_packet_out;      // table index of outgoing packet

void fifo_reset(void) {
  // reset FIFO
  fifo_bytes_in = 0;
  fifo_bytes_out = 0;
  fifo_packet_in = 0;
  fifo_packet_out = 0;
  fifo_packets[0] = 0;  // ensure valid entry for first packet
}

void fifo_new_packet(void) {
  // reset offset to (re)start packet
  fifo_bytes_in = 0;
}

void fifo_write_byte(unsigned char data) {
  // add byte to the incoming packet
  FIFO_PTR_TYPE position = (fifo_packets[fifo_packet_in] + fifo_bytes_in) & FIFO_BUFFER_MASK; // calculate position in buffer
  fifo_buffer[position] = data; 
                                                                // store byte at position
  fifo_bytes_in++; 
                               // increase byte counter
}

void fifo_commit_packet(void) {
  // complete incoming packet by advancing to next slot in FIFO
  FIFO_PTR_TYPE new_position = (fifo_packets[fifo_packet_in] + fifo_bytes_in) & FIFO_BUFFER_MASK; 
      // calculate position in buffer for next packet
  fifo_packet_in = (fifo_packet_in + 
1) & FIFO_PACKET_MASK;
  fifo_packets[fifo_packet_in] = new_position; 
                                                         // store new position in packet table
  fifo_bytes_in = 0; 
                                                                                   // reset offset to be ready to store data
}

unsigned short int fifo_get_packet(void) {
  // if available, initiate reading from packet from FIFO
  if (fifo_packet_in == fifo_packet_out)  // if no packets are in FIFO
  return 0;         // 
return 0

  fifo_bytes_out = 0;       // reset read offset within current packet

  // calculate and size of available packet
  FIFO_PTR_TYPE next_packet = (fifo_packet_out + 1) & FIFO_PACKET_MASK;
  return (FIFO_BUFFER_SIZE - fifo_packets[fifo_packet_out] + fifo_packets[next_packet]) & FIFO_BUFFER_MASK;
}

unsigned char fifo_read_byte(void) {
  // retrieve byte from current packet
  FIFO_PTR_TYPE position = (fifo_packets[fifo_packet_out] + fifo_bytes_out) & FIFO_BUFFER_MASK; // calculate current read position
  fifo_bytes_out++; 
                              // increase current read offset
  return fifo_buffer[position]; 
                                        // return byte from calculated position
}

void fifo_remove_packet(void) {
  // remove packet from FIFO, advance to next slot
  if(fifo_packet_in != fifo_packet_out)             // but only do so, 
if there's actually a packet available
  fifo_packet_out = (fifo_packet_out + 
1) & FIFO_PACKET_MASK;
}


#define NMEA_MAX_AIS_PAYLOAD 42 
                                // number of AIS bytes per NMEA sentence, to keep total NMEA sentence always below 82 characters
#define NMEA_AIS_BITS (NMEA_MAX_AIS_PAYLOAD * 8)
#define NMEA_AIS_BITS_ENCODED ((NMEA_AIS_BITS + 5) / 6)
//const char nmea_lead[] = "!AIVDM,"; 
                            // static 
start of NMEA sentence
const char nmea_lead[] = "$AIVDM,"; 
                            // static start of NMEA sentence
char nmea_buffer[8+NMEA_AIS_BITS_ENCODED+5+1];                  // 
buffer for dynamic part of NMEA sentence
 
                                                               // fragment and channel info, AIS payload, stuff-bit and crc, 0-termination
unsigned char nmea_buffer_index; 
                               // current buffer position

#define NMEA_LEAD_CRC 'A' ^ 'I' ^ 'V' ^ 'D' ^ 'M' ^ ',' // CRC for static 
start of sentence
unsigned char nmea_crc; 
                                // calculated CRC

unsigned char nmea_message_id = 0;                      // 
sequential message id for multi-sentence message

const char nmea_hex[] = {
  '0', '1', '2', '3',   // lookup 
table for hex conversion of CRC
  '4', '5', '6', '7',
  '8', '9', 'A', 'B',
  'C', 'D', 'E', 'F'
};
// process AIS next packet in FIFO and transmit as NMEA sentence(s) through UART
void nmea_process_packet(void) {
  unsigned short int packet_size = 
fifo_get_packet();
  unsigned short int i;

  if (packet_size == 0 || packet_size < 4) 
     // check for empty packet
    return; 
                                    // no (valid) packet available in FIFO, nothing to send

  unsigned char radio_channel = fifo_read_byte() + 'A'; // retrieve radio channel (0=A, 1=B)

  // calculate number of fragments, NMEA allows 82 characters per sentence
  //      -> max 62 6-bit characters payload
  //      -> max 46 AIS bytes (368 bits) per sentence
  packet_size -= 3;     // Ignore channel information and AIS CRC
  unsigned char curr_fragment = 1;
  unsigned char total_fragments = 1;
  unsigned short int packet_bits = 
packet_size * 8;
  while (packet_bits > NMEA_AIS_BITS) {
    packet_bits -= NMEA_AIS_BITS;
    total_fragments++;
  }

  // avoid sending garbage if fragment count does not make sense
  if (total_fragments > 9) {
    return;
  }

  // maintain message id if this is a multi-sentence message
  if (total_fragments > 1) {
    nmea_message_id++;
    if (nmea_message_id > 9)
      nmea_message_id = 1; 
   // keep message id < 10
  }

  // create fragments
  while (packet_size > 0) {
    // reset buffer and CRC
    nmea_buffer_index = 0;
    nmea_crc = NMEA_LEAD_CRC;

    // write fragment information, I assume total fragments always < 10
    nmea_push_char(total_fragments + '0');
    nmea_push_char(',');
    nmea_push_char(curr_fragment + '0');
    nmea_push_char(',');
    curr_fragment++;

    // write message id if there are multiple fragments
    if (total_fragments > 1)
    nmea_push_char(nmea_message_id + '0');
    nmea_push_char(',');

    // write channel information
    nmea_push_char(radio_channel);
    nmea_push_char(',');

    // encode and write next NMEA_MAX_AIS_PAYLOAD bytes from AIS packet
    unsigned char fragment_size = packet_size;
    if (fragment_size > NMEA_MAX_AIS_PAYLOAD)
    {
      fragment_size = NMEA_MAX_AIS_PAYLOAD;
    }
    unsigned char stuff_bits = nmea_push_packet(fragment_size);
    packet_size -= fragment_size;

    // write stuff bit
    nmea_push_char(',');
    nmea_push_char(stuff_bits + '0');

    // write CRC
    unsigned char final_crc = nmea_crc;   // copy CRC as push_char will modify it
    nmea_push_char('*');
    nmea_push_char(nmea_hex[final_crc >> 4]);
    nmea_push_char(nmea_hex[final_crc & 0x0f]);

    // terminate message with 0
    nmea_push_char(0);

    // send NMEA sentence over UART
    //Serial.print(nmea_lead);
    for(i=0; i<sizeof(nmea_lead)-1; i++) {
      char2usart(nmea_lead[i]);
    }
    //Serial.println(nmea_buffer);
    i=0;
    while (nmea_buffer[i] !=0 ) {
      char2usart(nmea_buffer[i]);
      i++;
    }
    char2usart(0x0D);
    char2usart(0x0A);
  }
}

// adds char to buffer and updates CRC
void nmea_push_char(char c) {
  nmea_crc ^= c;
  nmea_buffer[nmea_buffer_index++] 
= c;
}

// encodes and adds AIS packet to buffer, returns # of stuff bits
unsigned char nmea_push_packet(unsigned 
char packet_size) {
  unsigned char raw_byte;
  unsigned char raw_bit;

  unsigned char nmea_byte;
  unsigned char nmea_bit;

  nmea_byte = 0;
  nmea_bit = 6;

  while (packet_size != 0) {
    raw_byte = fifo_read_byte();
    raw_bit = 8;

    while (raw_bit > 0) {
      nmea_byte <<= 1;
      if (raw_byte & 0x80)
      nmea_byte |= 1;
      nmea_bit--;

      if (nmea_bit == 0) {
        if (nmea_byte > 39)
          nmea_byte += 8;
        nmea_byte += 48;
        nmea_push_char(nmea_byte);
        nmea_byte = 0;
        nmea_bit = 6;
      }

      raw_byte <<= 1;
      raw_bit--;
    }

    packet_size--;
  }

  // stuff unfinished NMEA character
  unsigned char stuff_bits = 0;
  if (nmea_bit != 6)
  {
    // stuff with 0 bits as needed
    while (nmea_bit != 0) {
      nmea_byte <<= 1;
      nmea_bit--;
      stuff_bits++;
    }

    // .. and convert and store last byte
    if (nmea_byte > 39)
      nmea_byte += 8;
    nmea_byte += 48;
    nmea_push_char(nmea_byte);
  }

  return stuff_bits;
}



void delay_us( unsigned int us ) {
  unsigned int t = (unsigned int)(3.2*us);        // while(t>0){t--;} 4-5 tacts 16/5=3.2
  
  while (t>0) {
    t--;
  }
}

void delay_ms( unsigned int ms ) {
  while (ms>0) {
    delay_us(1000);
    ms--;
  }
}

void delay( unsigned int s ) {
  while (s>0) {
    delay_ms(1000);
    s--;
  }  
}







unsigned char spi_shift_byte( unsigned char tx ) {
  while (!SPI1_SR_bit.TXE) {
    asm("nop");
  }
  SPI1_DR = tx;
  while (!SPI1_SR_bit.RXNE) {
    asm("nop");
  }
  return SPI1_DR;
}

void spi_start( void ){
  PB_ODR_bit.ODR4 = 0;  // SI4463 nSEL low level for SPI_CR2_bit.SSM = 1
  SPI1_CR1_bit.SPE = 1; // Enable SPI
  delay_us(20); 
        // 20µs
}

void spi_end( void ){
  while (SPI1_SR_bit.BSY) {
    asm("nop");
  }
  PB_ODR_bit.ODR4 = 1;  // SI4463 nSEL high level for SPI_CR2_bit.SSM = 1
  SPI1_CR1_bit.SPE = 0; // Disable SPI
  delay_us(80); 
        // 80µs
}
/*
void waitCTSspi ( void ){
  unsigned char rx=0;
  
  while (rx!=0xFF) {
    spi_start();
    rx = spi_shift_byte(0x44);
    rx = spi_shift_byte(0xFF);
    spi_end();
  }
}
*/
// wait CTC (HIGH) on GPIO1(SI4463) = PC1(STM8L)
void waitCTS( void ) {
  while ( !PC_IDR_bit.IDR1 ){
    asm("nop");
  }
}

void spi_send_buf(unsigned char* buf, unsigned char len) {
  unsigned char i;
  
  spi_start();
  for (i=0; i<len; i++){
    spi_shift_byte(buf[i]);
  }
  spi_end();
}

void parse_config_si4463(const unsigned 
char *cmd){
  unsigned char size, i;
  
  while( *cmd != 0 ) {
    size = *cmd;
    cmd++; 
    for(i=0; i<size; i++) {
      buf[i]=*cmd;
      cmd++;
    }
    waitCTS();
    spi_send_buf(buf, size);
  }
}

unsigned char si4463_get_response( unsigned 
char *buf, unsigned char len, unsigned char maxReq ) {
  unsigned char i;
  
  while (maxReq > 0) {
    i=0;
    spi_start();
    if (i==0) {
      spi_shift_byte(0x44);
      if ( spi_shift_byte(0xFF) != 0xFF ) {
        maxReq--;
      } else {
        for (i=0; i<len; i++) {
          buf[i] = spi_shift_byte(0xFF);
        }
        maxReq=0;
      }
    }
    spi_end();
  }
  return i;
}

void main( void ) {
  CLK_CKDIVR = 0;       // 16 MHz HSI clock
  
  // Used pin setup
  PA_DDR_bit.DDR2 = 1;  // output MODULE TXD
  PA_CR1_bit.C12 = 1;   // Push-pull
  PA_CR2_bit.C22 = 1;   // Output speed up to 10 MHz
  
  PA_DDR_bit.DDR3 = 0;  // input MODULE RXD
  PA_CR1_bit.C13 = 1;   // Input with pull-up
  PA_CR2_bit.C23 = 0;   // External interrupt disabled
  
  PB_DDR_bit.DDR2 = 1;  // output SI4463 OSCILATOR
  PB_CR1_bit.C12 = 1;   // Push-pull
  PB_CR2_bit.C22 = 1;   // Output speed up to 10 MHz
  
  PB_DDR_bit.DDR3 = 1;  // output SI4463 SDN
  PB_CR1_bit.C13 = 1;   // Push-pull
  PB_CR2_bit.C23 = 1;   // Output speed up to 10 MHz
  
  PB_DDR_bit.DDR4 = 1;  // output SI4463 nSEL
  PB_CR1_bit.C14 = 1;   // Push-pull
  PB_CR2_bit.C24 = 1;   // Output speed up to 10 MHz
  
  PB_DDR_bit.DDR5 = 1;  // output SI4463 SCLK
  PB_CR1_bit.C15 = 1;   // Push-pull
  PB_CR2_bit.C25 = 1;   // Output speed up to 10 MHz
  
  PB_DDR_bit.DDR6 = 1;  // output SI4463 SDI
  PB_CR1_bit.C16 = 1;   // Push-pull
  PB_CR2_bit.C26 = 1;   // Output speed up to 10 MHz
  
  PB_DDR_bit.DDR7 = 0;  // input SI4463 SDO
  PB_CR1_bit.C17 = 1;   // Input with pull-up
  PB_CR2_bit.C27 = 0;   // External interrupt disabled
  
  PC_DDR_bit.DDR0 = 0;  // input SI4463 nIRQ
  PC_CR1_bit.C10 = 1;   // Input with pull-up
  PC_CR2_bit.C20 = 0;   // External interrupt disabled
  
  PC_DDR_bit.DDR1 = 0;  // input SI4463 GPIO1
  PC_CR1_bit.C11 = 1;   // Input with pull-up
  PC_CR2_bit.C21 = 0;   // External interrupt disabled
  
  PC_DDR_bit.DDR4 = 0;  // input SI4463 GPIO0
  PC_CR1_bit.C14 = 1;   // Input with pull-up
  PC_CR2_bit.C24 = 0;   // External interrupt disabled
  
  PC_DDR_bit.DDR5 = 1;  // output MODULE M0
  PC_CR1_bit.C15 = 1;   // Push-pull
  PC_CR2_bit.C25 = 1;   // Output speed up to 10 MHz
  
  PC_DDR_bit.DDR6 = 1;  // output MODULE M1
  PC_CR1_bit.C16 = 1;   // Push-pull
  PC_CR2_bit.C26 = 1;   // Output speed up to 10 MHz
  
  PD_DDR_bit.DDR0 = 1;  // output MODULE AUX
  PD_CR1_bit.C10 = 1;   // Push-pull
  PD_CR2_bit.C20 = 1;   // Output speed up to 10 MHz
  
  // Not used pin output
  PA_DDR_bit.DDR0 = 1; PA_CR1_bit.C10 = 1; //PA_ODR_bit.ODR0 = 1;
  PA_DDR_bit.DDR1 = 1; PA_CR1_bit.C11 = 1; //PA_ODR_bit.ODR1 = 1;
  PA_DDR_bit.DDR4 = 1; PA_CR1_bit.C14 = 1; //PA_ODR_bit.ODR4 = 1;
  PA_DDR_bit.DDR5 = 1; PA_CR1_bit.C15 = 1; //PA_ODR_bit.ODR5 = 1;
  PB_DDR_bit.DDR0 = 1; PB_CR1_bit.C10 = 1; //PB_ODR_bit.ODR0 = 1;
  PB_DDR_bit.DDR1 = 1; PB_CR1_bit.C11 = 1; //PB_ODR_bit.ODR1 = 1;
  PC_DDR_bit.DDR2 = 1; PC_CR1_bit.C12 = 1; //PC_ODR_bit.ODR2 = 1;
  PC_DDR_bit.DDR3 = 1; PC_CR1_bit.C13 = 1; //PC_ODR_bit.ODR3 = 1;
  PD_DDR_bit.DDR1 = 1; PD_CR1_bit.C11 = 1; //PD_ODR_bit.ODR1 = 1;
  PD_DDR_bit.DDR2 = 1; PD_CR1_bit.C12 = 1; //PD_ODR_bit.ODR2 = 1;
  PD_DDR_bit.DDR3 = 1; PD_CR1_bit.C13 = 1; //PD_ODR_bit.ODR3 = 1;

  
  // USART Configure 
  CLK_PCKENR1_bit.PCKEN15 = 1;  // STM8L enable tacting USART
  // Remap USART pin
  SYSCFG_RMPCR1_bit.USART1TR_REMAP = 1; // USART1_TX on PA2 and USART1_RX on PA3
  SYSCFG_RMPCR1_bit.USART1CK_REMAP = 1; // USART1_CK mapped on PA0
  // USART Speed 19200bps for 16MHz clock on chip
  USART1_BRR2 = 0x01;
  USART1_BRR1 = 0x34;
  // USART 8-N-1
  USART1_CR1 = 0;
  USART1_CR3 = 0;
  // USART pin
  USART1_CR2_bit.REN = 0; // RX disable
  USART1_CR2_bit.TEN = 1; // TX enable
  
  
  // SPI Configure
  PB_ODR_bit.ODR4 = 1;          // SI4463 nSEL default high level
  CLK_PCKENR1_bit.PCKEN14 = 1;  // STM8L enable tacting SPI
  
  SPI1_ICR = 0x00; // Disable 
interups, disable DMA
  SPI1_CR1 = 0x04; // LSBFIRST=0, 
SPE=0, BR=000b, MSTR=1, CPOL=0, CPHA=0
                   // MSB is transmitted first, SPI disabled, Baud 
rate control fSYSCLK/2, Master configuration, SCK to 0 when idle, The first clock transition is the first data capture edge
  SPI1_CR2 = 0x03; // BDM=0, BDOE=0, 
CRCEN=0, CRCNEXT=0, RXONLY=0, SSM=1, SSI=1
                   // 2-line unidirectional data mode selected, Full 
duplex, Software slave management enabled,  Master mode
  
  // SI4463 reset
  PB_ODR_bit.ODR2 = 1;  // Enable 26MHz Crystal Oscillator for SI4463 (XIN pin)
  PB_ODR_bit.ODR3 = 1;  // Shutdown SI4463 (SDN pin high)
  delay_us(10); 
        // 10us <- AN633 p25  
  PB_ODR_bit.ODR3 = 0;  // PowerUp SI4463 (SDN pin low)
  //delay_ms(6);          // 6ms <- AN633 p25
  waitCTS();
  
  // parse including config and send to SI4463
  parse_config_si4463(config_array);
  
  // Read ITs, clear pending ones
  buf[0]=0x20;  // GET_INT_STATUS
  buf[1]=0;
  buf[2]=0;
  buf[3]=0;
  waitCTS();
  spi_send_buf(buf, 4);
  si4463_get_response(buf, 8, 3);

  /* SI4463 READY */
  /*
  buf[0]=0x01; spi_send_buf(buf, 1); // PART_INFO
  if (si4463_get_response(buf, 8, 3) != 0) {
    send2usart(buf, 8);
  }
  char2usart(0x0D);
  char2usart(0x0A);
  */
   // Start RX
  buf[0]=0x32;  // START_RX
  buf[1]=3;     // Channel
  buf[2]=0;     // Start RX immediately.
  buf[3]=0x1F;  // RX_LEN HW
  buf[4]=0xFF;  // RX_LEN LW
  /*
  States:
  NOCHANGE  0   Remain in RX state if RXTIMEOUT occurs.   
  SLEEP   1   SLEEP or STANDBY state, according to the mode of operation of the 32K R-C Osc selected by GLOBAL_CLK_CFG:CLK_32K_SEL. 
  
  SPI_ACTIVE  2   SPI ACTIVE state.   
  READY   3   READY state.  
  READY2  4   Another enumeration for READY state.  
  TX_TUNE   5   TX_TUNE state.  
  RX_TUNE   6   RX_TUNE state.  
  TX          7   TX state.   
  RX          8   RX state (briefly exit and re-enter RX state to re-arm for acquisition of another 
packet).
  */
  buf[5]=0;     // RXTIMEOUT_STATE
  buf[6]=0;     // RXVALID_STATE
  buf[7]=0;     // RXINVALID_STATE
  spi_send_buf(buf, 8);
  waitCTS();
  
  
  PC_CR2_bit.C20 = 1;    // nIRQ External interrupt enable
  EXTI_CR1_bit.P0IS = 1; // 01b 
  CPU_CFG_GCR_bit.AL = 1;
  asm("RIM");
  //asm("HALT");
  while (1){
    asm("WFI");
  }
}

#pragma vector=EXTI0_vector
__interrupt void packet_received(void) {
    
  rx_this_bit_NRZI = PC_IDR_bit.IDR4 
? 1 : 0;
  rx_bit = !(rx_prev_bit_NRZI ^ rx_this_bit_NRZI);  // NRZI decoding: 
change = 0-bit, no change = 1-bit, i.e. 00,11=>1, 01,10=>0, i.e. NOT(A XOR B)
  rx_prev_bit_NRZI = rx_this_bit_NRZI; 
             // store encoded bit for next round of decoding

  // add decoded bit to bit-stream (receiving LSB first)
  rx_bitstream >>= 1;
  if (rx_bit) rx_bitstream |= 0x8000;
  
  // packet handler state machine
  switch (ph_state) {

    // STATE: OFF
    case PH_STATE_OFF: 
                   // state: off, do nothing
      break;

    // STATE: RESET
    case PH_STATE_RESET: 
                 // state: reset, prepare state machine for next 
packet
      rx_bitstream &= 0x8000; 
            // reset bit-stream (but don't throw away incoming bit)
      rx_bit_count = 0; 
                  // reset bit counter
      fifo_new_packet(); 
                 // reset fifo packet
      fifo_write_byte(ph_radio_channel);  // indicate channel for this packet
      ph_state = PH_STATE_WAIT_FOR_SYNC; 
 // next state: wait for training sequence
      rx_sync_state = PH_SYNC_RESET;
      break;

    // STATE: WAIT FOR PREAMBLE AND START FLAG
    case PH_STATE_WAIT_FOR_SYNC:        // state: waiting for preamble and start flag
      rx_bit_count++; 
                  // count processed bits since reset
      // START OF SYNC STATE MACHINE
      switch (rx_sync_state) {
          // SYNC STATE: RESET
          case PH_SYNC_RESET: 
                          // 
sub-state: (re)start sync process
              if (rx_bit_count > PH_SYNC_TIMEOUT) {     // if we exceeded sync time out
                  ph_state = PH_STATE_RESET;            // reset state machine, 
will trigger channel hop
              }
              else { 
                                   // else
                  rx_sync_count = 0;                    // start new preamble
                  rx_sync_state = rx_bit ? PH_SYNC_1 : PH_SYNC_0;
              }
              break;

          // SYNC STATE: 0-BIT
          case PH_SYNC_0: 
                                      // sub-state: last bit was a 0
              if (rx_bit) { 
                                    // if we get a 1
                  rx_sync_count++; 
                             // valid preamble bit
                  rx_sync_state = PH_SYNC_1;                    // next state
              } else { 
                                         // if we get another 0
                if (rx_sync_count > PH_PREAMBLE_LENGTH){ 
       // if we have a sufficient preamble length
                      rx_sync_count = 7; 
                       // treat this as part 
of start flag, we already have 1 out of 8 bits (0.......)
                      rx_sync_state = PH_SYNC_FLAG;             // next state flag detection
                  }
                  else 
                                         // if not
                    rx_sync_state = 
PH_SYNC_RESET;              // 
invalid preamble bit, restart preamble detection
              }
              break;

          // SYNC STATE: 1-BIT
          case PH_SYNC_1: 
                                      // sub-state: last bit was a 1
              if (!rx_bit) { 
                                   // if we get a 0
                  rx_sync_count++; 
                             // valid preamble bit
                  rx_sync_state = PH_SYNC_0;      // next state
              } else { 
           // if we get another 1
                  if (rx_sync_count > PH_PREAMBLE_LENGTH){ 
     // if we have a sufficient preamble length
                      rx_sync_count = 5; 
                       // treat this as part 
of start flag, we already have 3 out of 8 bits (011.....)
                      rx_sync_state = PH_SYNC_FLAG;   // next state flag detection
                  }
                  else 
           // if not
                      rx_sync_state = PH_SYNC_RESET;    // treat this as invalid preamble 
bit
              }
              break;

          // SYNC STATE: START FLAG
          case PH_SYNC_FLAG: 
       // sub-state: start flag detection
              rx_sync_count--; 
       // count down bits
              if (rx_sync_count != 0) {     // if this is not 
the last bit of start flag
                  if (!rx_bit)        // we expect a 1, 0 is an 
error
                      rx_sync_state = PH_SYNC_RESET;  // restart preamble detection
              } else { 
         // if this is the last bit of start flag
                  if (!rx_bit) {      // we 
expect a 0
                      rx_bit_count = 0;     // reset bit counter
                      ph_state = PH_STATE_PREFETCH; // next state: start receiving packet
                  } else 
       // 1 is an error
                      rx_sync_state = PH_SYNC_RESET;  // restart preamble detection
              }
              break;
      }
      // END OF SYNC STATE MACHINE - preamble and start flag detected
      break;

    // STATE: PREFETCH FIRST PACKET BYTE
    case PH_STATE_PREFETCH: 
          // state: pre-fill receive buffer with 8 bits
        rx_bit_count++; 
            // increase bit counter
        if (rx_bit_count == 8) {                // after 8 bits arrived
          rx_bit_count = 0; 
          // reset bit counter
          rx_one_count = 0; 
          // reset counter for stuff bits
          rx_data_byte = 0; 
          // reset buffer for data byte
          rx_crc = 0xffff; 
           // init CRC calculation
          ph_state = PH_STATE_RECEIVE_PACKET;   // next state: receive and process packet
        }
        break;          // do nothing for the first 8 bits to fill buffer

    // STATE: RECEIVE PACKET
    case PH_STATE_RECEIVE_PACKET:   // state: 
receiving packet data
      rx_bit = rx_bitstream & 0x80;   // extract data bit for processing

      if (rx_one_count == 5) {      // if we expect a stuff-bit..
        if (rx_bit) {       // if stuff bit is not zero the packet is invalid
          ph_last_error = PH_ERROR_STUFFBIT;  // report invalid stuff-bit error
          ph_state = PH_STATE_RESET;    // reset state machine
        } else
          rx_one_count = 0; 
    // else ignore bit and reset stuff-bit counter
        break;
      }

      rx_data_byte = rx_data_byte >> 1 | rx_bit;// shift bit into current data 
byte

      if (rx_bit) { 
      // if current bit is a 1
        rx_one_count++;       // count 1's to identify stuff bit
        rx_bit = 1; 
      // avoid shifting for CRC
      } else
        rx_one_count = 0; 
    // or reset stuff-bit counter

      if (rx_bit ^ (rx_crc & 0x0001))   // CCITT CRC calculation (according to Dr. Dobbs)
        rx_crc = (rx_crc >> 1) ^ 0x8408;
      else
        rx_crc >>= 1;
      if ((rx_bit_count & 0x07)==0x07) 
{  // every 8th bit.. (counter started at 0)
        fifo_write_byte(rx_data_byte);    // add buffered byte to FIFO
        rx_data_byte = 0; 
    // reset buffer
      }
      rx_bit_count++;       // count valid, de-stuffed data bits
      if ((rx_bitstream & 0xff00) == 0x7e00) {  // if we found the end flag 0x7e we're done
        //Serial.println(rx_crc,HEX);
        if (rx_crc != 0xf0b8)     // if CRC verification failed
          ph_last_error = PH_ERROR_CRC;   // report CRC error
        else {
          fifo_commit_packet();     // else commit packet in FIFO
        }
        ph_state = PH_STATE_RESET; 
   // reset state machine
      }
      else if (rx_bit_count > 1020) {   // if packet is too 
long, it's probably invalid
        ph_last_error = PH_ERROR_NOEND;   // report error
        ph_state = PH_STATE_RESET; 
   // reset state machine
      }
      break;
  }
  // END OF PACKET HANDLER STATE MACHINE
  
  if (fifo_get_packet()) {
    nmea_process_packet();
    fifo_remove_packet();
  }
  
  EXTI_SR1_bit.P0F = 1; //escape from interrupt
}

Всё :)