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
}
Всё :)