123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566 |
- /*
- * radio.c
- *
- * Created on: Oct 11, 2019
- * Author: curiousmuch
- */
- #include <stdio.h>
- #include <stdint.h>
- #include <math.h>
- #include "freertos/FreeRTOS.h"
- #include "freertos/task.h"
- #include "freertos/semphr.h"
- #include "sdkconfig.h"
- #include "esp_log.h"
- #include "driver/timer.h"
- #include "radio.h"
- #include "cc1200.h"
- #include "cc1200_protocol.h"
- #include "afsk_modulator.h"
- #include "afsk_demodulator.h"
- #include "aprs_decoder.h"
- #include "aprs_encoder.h"
- /* Debugging Tag */
- #define RADIO_TAG "radio"
- /* Data Structures */
- typedef enum {
- RADIO_TIMER_NO_CONFIG = 0,
- RADIO_TIMER_TX,
- RADIO_TIMER_RX,
- } timer_radio_semaphore_t;
- /* Local Global Variables */
- static radio_param_t radio_param = { .type = NOT_CONFIGURED,
- .status = RADIO_IDLE,
- .rx_cb = NULL,
- .tx_cb = NULL };
- static SemaphoreHandle_t xRadioTXSemaphore;
- static SemaphoreHandle_t xRadioRXSemaphore;
- static SemaphoreHandle_t xRadioMutex;
- static timer_radio_semaphore_t radio_semaphore;
- #define DEBUG_0 16
- #define DEBUG_1 4
- #define DEBUG_2 32
- #define DEBUG_3 33
- /* Private Functions */
- void IRAM_ATTR radio_timer_isr(void *param)
- {
- GPIO.out_w1ts = (1 << DEBUG_0);
- int timer_idx = (int) param; // cast to int for timer index
- TIMERG0.int_clr_timers.t0 = 1; // clear interrupt
- TIMERG0.hw_timer[0].config.alarm_en = TIMER_ALARM_EN; // re-enable timer alarm
- // provide unblocking semaphore
- static BaseType_t xHigherPriorityTaskWoken = pdFALSE;
- switch(radio_semaphore) {
- case RADIO_TIMER_NO_CONFIG:
- //ESP_LOGE(RADIO_TAG, "no configuration");
- break;
- case RADIO_TIMER_RX:
- xSemaphoreGiveFromISR(xRadioRXSemaphore, &xHigherPriorityTaskWoken);
- break;
- case RADIO_TIMER_TX:
- xSemaphoreGiveFromISR(xRadioTXSemaphore, &xHigherPriorityTaskWoken);
- break;
- default:
- //ESP_LOGE(RADIO_TAG, "invalid configuration");
- break;
- }
- // wake higher priority task if woken
- if (xHigherPriorityTaskWoken == pdTRUE)
- {
- portYIELD_FROM_ISR();
- }
- GPIO.out_w1tc = (1 << DEBUG_0);
- }
- #define RADIO_TIMER_GROUP TIMER_GROUP_0
- #define RADIO_TIMER TIMER_0
- void timer_radio_init(uint32_t sample_rate, void *timer_isr)
- {
- // setup timer settings
- timer_config_t timer_config;
- timer_config.divider = 2;
- timer_config.counter_dir = TIMER_COUNT_UP;
- timer_config.counter_en = TIMER_PAUSE;
- timer_config.alarm_en = TIMER_ALARM_EN;
- timer_config.intr_type = TIMER_INTR_LEVEL;
- timer_config.auto_reload = TIMER_AUTORELOAD_EN;
- // calculate value for timer alarm
- // TODO: confirm timer alarm value calculation
- uint32_t timer_alarm;
- timer_alarm = (uint32_t)(40000000 / sample_rate);
- // load timer settings
- timer_init(RADIO_TIMER_GROUP, RADIO_TIMER, &timer_config);
- timer_set_counter_value(RADIO_TIMER_GROUP, RADIO_TIMER, 0x00000000ULL);
- timer_set_alarm_value(RADIO_TIMER_GROUP, RADIO_TIMER, 3030);
- timer_isr_register(RADIO_TIMER_GROUP, RADIO_TIMER, timer_isr,
- (void *) RADIO_TIMER, ESP_INTR_FLAG_IRAM, NULL);
- // setup timer ISR semaphore type
- radio_semaphore = RADIO_TIMER_NO_CONFIG;
- }
- void timer_radio_start(timer_radio_semaphore_t type)
- {
- // setup timer ISR semaphore type
- radio_semaphore = type;
- // start timer
- timer_enable_intr(RADIO_TIMER_GROUP, RADIO_TIMER);
- timer_set_counter_value(RADIO_TIMER_GROUP, RADIO_TIMER, 0x00000000ULL);
- timer_start(RADIO_TIMER_GROUP, RADIO_TIMER);
- }
- void timer_radio_stop(void)
- {
- // setup timer ISR semaphore type
- radio_semaphore = RADIO_TIMER_NO_CONFIG;
- // stop timer
- timer_disable_intr(RADIO_TIMER_GROUP, RADIO_TIMER);
- timer_pause(RADIO_TIMER_GROUP, RADIO_TIMER);
- }
- /* HAL Layer */
- void radio_rx_task(void *para);
- void radio_idle(void)
- {
- // disable all radio tasks
- //vTaskSuspend(radio_rx_task);
- // disable all radio timers
- timer_radio_stop();
- cc1200_radio_idle();
- radio_param.status = RADIO_IDLE;
- }
- void radio_tx(void)
- {
- if (radio_param.status != RADIO_IDLE)
- radio_idle();
- cc1200_radio_tx();
- radio_param.status = RADIO_TX;
- }
- void radio_rx(void)
- {
- if (radio_param.status != RADIO_IDLE)
- radio_idle();
- cc1200_radio_rx();
- radio_param.status = RADIO_RX;
- }
- radio_status_t radio_get_status(void)
- {
- return radio_param.status;
- }
- uint8_t radio_get_cs(void)
- {
- return ax25_decoder_get_cs();
- }
- void radio_packet_tx(uint8_t *data, int32_t len, void *settings)
- {
- // configure radio for TX
- switch (radio_param.type) {
- case AX25: {
- ESP_LOGI(RADIO_TAG, "transmitting AX25 packet");
- // load ax25 encoder state machine
- ax25_encoder_encode(data, len);
- // configure radio to send CW
- xSemaphoreTake( xRadioMutex, portMAX_DELAY ); // lock radio
- radio_tx();
- xSemaphoreGive( xRadioMutex ); // unlock radio
- // start sampling timer
- timer_radio_start(RADIO_TIMER_TX);
- break;
- }
- default: {
- ESP_LOGE(RADIO_TAG, "invalid configuration");
- break;
- }
- }
- }
- void radio_set_frequency(uint32_t freq)
- {
- xSemaphoreTake( xRadioMutex, portMAX_DELAY ); // lock radio
- cc1200_radio_frequency(freq);
- xSemaphoreGive( xRadioMutex );
- }
- void radio_tx_task(void *para)
- {
- // variables for transmission
- uint32_t sample_count = 0;
- uint8_t nrzi_bit = 0;
- int8_t amplitude = 0;
- // metrics
- uint32_t tx_count = 0;
- while(1)
- {
- // block until sampling timer is running
- if (xSemaphoreTake(xRadioTXSemaphore, portMAX_DELAY) == pdTRUE)
- {
- if (ax25_encoder_get_status() == READY)
- {
- // reset sample count used per RF symbol
- sample_count = 0;
- // setup first amplitude for tone
- nrzi_bit = ax25_encoder_get_bit();
- amplitude = afsk_get_amplitude(nrzi_bit);
- }
- else
- {
- // process error
- }
- }
- // run until packet is finished
- while(ax25_encoder_get_status() == READY)
- {
- // block until semaphore is given or there has been a timing error
- if (xSemaphoreTake(xRadioTXSemaphore, portMAX_DELAY) == pdTRUE)
- {
- // update carrier for AFSK
- xSemaphoreTake( xRadioMutex, portMAX_DELAY ); // lock radio
- cc1200_radio_write_CFM(amplitude);
- xSemaphoreGive( xRadioMutex ); // unlock radio
- // increment symbol count
- sample_count++;
- if (sample_count >= 11)
- {
- GPIO.out_w1ts = (1 << DEBUG_1);
- nrzi_bit = ax25_encoder_get_bit();
- sample_count = 0;
- GPIO.out_w1tc = (1 << DEBUG_1);
- }
- // get amplitude for next nrzi bit
- amplitude = afsk_get_amplitude(nrzi_bit);
- }
- else
- {
- ESP_LOGE(RADIO_TAG, "timing error");
- ESP_LOGI(RADIO_TAG, "canceling transmission");
- break;
- }
- }
- // stop sampling timer
- timer_radio_stop();
- // confirm if TX failed or not
- // ax25_encoder will not reach DONE if their is a timing failure
- switch(ax25_encoder_get_status()) {
- case STOP:
- ESP_LOGI(RADIO_TAG, "transmission complete: [%d]", tx_count++);
- break;
- case READY:
- ESP_LOGE(RADIO_TAG, "transmission failed");
- break;
- case ERROR:
- ESP_LOGE(RADIO_TAG, "encoder error");
- break;
- default:
- ESP_LOGE(RADIO_TAG, "unknown encoder error");
- }
- // active callback function
- if (radio_param.tx_cb != NULL)
- {
- radio_param.tx_cb();
- }
- }
- }
- void radio_rx_task(void *para)
- {
- // analog variables
- int8_t cfm_value=0;
- // demod variables
- uint8_t raw_bit=0, prev_raw_bit=0;
- // pll variables and settings
- int32_t d_pll=0, dpll_error=0, err_div=0;
- // TODO: The following group should be made user setable fed via the settings
- const int32_t SAMPLE_FREQUENCY = 13200;
- const int32_t BAUD_RATE = 1200;
- const int32_t SAMPLES_BIT = SAMPLE_FREQUENCY / BAUD_RATE;
- const int32_t D_PLL_INC = (SAMPLES_BIT * 1);
- const int32_t D_PLL_MAX = (D_PLL_INC * SAMPLES_BIT * 1);
- const int32_t D_PLL_MARGIN = 1;
- // decoder variables
- uint32_t success=0;
- // task
- while(1)
- {
- // block until semphore is given or there has been a timing error
- if (xSemaphoreTake(xRadioRXSemaphore, portMAX_DELAY) == pdTRUE)
- {
- // read register analog value from radio
- xSemaphoreTake( xRadioMutex, portMAX_DELAY ); // lock radio
- cfm_value = cc1200_radio_read_CFM();
- xSemaphoreGive( xRadioMutex ); // unlock radio
- // afsk demod
- raw_bit = afsk_demod_add_sample(cfm_value);
- // clock synchronizer for sampling
- if (raw_bit != prev_raw_bit)
- {
- dpll_error = d_pll - (D_PLL_MAX / 2);
- if (dpll_error > D_PLL_MARGIN)
- {
- d_pll -= ax25_decoder_get_cs() ? D_PLL_MARGIN:
- (err_div + dpll_error); //(dpll_error + err_div / 2) / err_div;
- }
- else if (dpll_error < -D_PLL_MARGIN)
- {
- d_pll += ax25_decoder_get_cs() ? D_PLL_MARGIN :
- -(err_div + dpll_error); // (dpll_error + err_div / 2) / err_div;
- }
- }
- // increment clock
- d_pll += D_PLL_INC;
- // slice
- if (d_pll >= D_PLL_MAX)
- {
- switch (ax25_decoder_feed_bit(raw_bit))
- {
- case NORMAL:
- break;
- case FRAME_DECODED:
- ESP_LOGI("radio", "AX25 transmission received: [%d]", success++);
- // send via KISS TNC to over BLE SPP
- //ESP_LOG_BUFFER_HEXDUMP("APRS RX", aprs_buf, 100, ESP_LOG_INFO);
- //kiss_transmit(KISS_DATAFRAME, v.frame_buffer, v.frame_len);
- if (radio_param.rx_cb != NULL)
- radio_param.rx_cb(ax25_decoder_get_frame(), ax25_decoder_get_frame_len());
- break;
- case ERROR_FCS_MISMATCH:
- ESP_LOGV("radio", "AX25 fcs error");
- break;
- default:
- //printf("Weird Error\n");
- break;
- }
- d_pll -= D_PLL_MAX;
- }
- prev_raw_bit = raw_bit;
- }
- else
- {
- ESP_LOGE(RADIO_TAG, "rx timing error");
- }
- }
- }
- // Functionality will depend on protocol
- // AX.25 - Non-Block and will activate callback function
- void radio_packet_rx(void *settings)
- {
- switch(radio_param.type)
- {
- case AX25: {
- // reset decoder
- ax25_decoder_reset(); // this is required because sometimes it locks up if stopped
- // by TX Task
- // setup radio
- xSemaphoreTake( xRadioMutex, portMAX_DELAY ); // lock radio
- radio_rx();
- xSemaphoreGive( xRadioMutex ); // unlock radio
- // setup sampling timer
- timer_radio_start(RADIO_TIMER_RX);
- break;
- }
- default: {
- ESP_LOGE(RADIO_TAG, "invalid configuration");
- break;
- }
- }
- }
- void radio_reinit(radio_config_t type, void *settings)
- {
- // lock radio
- xSemaphoreTake(xRadioMutex, portMAX_DELAY);
- // disable radio
- radio_idle();
- // suspend radio tasks
- if (radio_param.type == AX25)
- {
- vTaskSuspend(radio_param.tx_task);
- vTaskSuspend(radio_param.rx_task);
- }
- switch(type)
- {
- case AX25:
- ESP_LOGI(RADIO_TAG, "APRS Mode");
- radio_param.type = AX25;
- // cast setting struct
- ax25_param_t *p = (ax25_param_t *)settings;
- // setup LUT for AFSK demodulator
- afsk_mod_init(p->sample_rate, p->symbol0_freq, p->symbol1_freq);
- // setup frequency detectors for AFSK demodulator
- afsk_demod_init(p->sample_rate, p->symbol0_freq, p->symbol1_freq);
- // setup encoder for ax25
- ax25_encoder_init(p->tx_delay, p->tx_tail);
- // setup ax25 decoder
- ax25_decoder_init(p->rx_buf, p->rx_buf_len);
- radio_param.rx_cb = p->rx_cb;
- radio_param.tx_cb = p->tx_cb;
- // load AX25_settings without reseting / reloading SPI
- cc1200_radio_config(AX25_SETTINGS, sizeof(AX25_SETTINGS)/sizeof(cc1200_reg_settings_t));
- // put chip to idle
- radio_idle();
- // resume radio tasks
- break;
- default:
- ESP_LOGE(RADIO_TAG, "invalid configuration");
- radio_param.type = NOT_CONFIGURED;
- break;
- }
- // resume tasks
- // suspend radio tasks
- if (radio_param.type == AX25)
- {
- vTaskResume(radio_param.tx_task);
- vTaskResume(radio_param.rx_task);
- }
- xSemaphoreGive(xRadioMutex);
- }
- void radio_init(radio_config_t type, void *settings)
- {
- // create mutux so SPI transactions will be multi-thread safe
- // TODO: fix so mutex is only created one to avoid memory leak
- xRadioMutex = xSemaphoreCreateMutex();
- // lock radio
- xSemaphoreTake( xRadioMutex, portMAX_DELAY );
- switch(type)
- {
- case AX25: {
- ESP_LOGI("Radio", "APRS Mode");
- radio_param.type = AX25;
- // cast setting struct
- ax25_param_t *p = (ax25_param_t *)settings;
- // setup LUT for AFSK demodulator
- afsk_mod_init(p->sample_rate, p->symbol0_freq, p->symbol1_freq);
- // setup frequency detectors for AFSK demodulator
- afsk_demod_init(p->sample_rate, p->symbol0_freq, p->symbol1_freq);
- // setup encoder for ax25
- ax25_encoder_init(p->tx_delay, p->tx_tail);
- // setup ax25 decoder
- ax25_decoder_init(p->rx_buf, p->rx_buf_len);
- radio_param.rx_cb = p->rx_cb;
- radio_param.tx_cb = p->tx_cb;
- // setup timer for TX / RX
- timer_radio_init(p->sample_rate, radio_timer_isr);
- // configure CC1200
- cc1200_radio_init(AX25_SETTINGS, sizeof(AX25_SETTINGS)/sizeof(cc1200_reg_settings_t));
- // put chip to idle
- radio_idle();
- // task communication
- xRadioRXSemaphore = xSemaphoreCreateBinary();
- xRadioTXSemaphore = xSemaphoreCreateBinary();
- // create task for sampling
- // TODO: Reduce stack requirements?
- xTaskCreatePinnedToCore(radio_rx_task, "radio rx", 1024*4, 0, 1, &radio_param.tx_task, p->cpu_core);
- xTaskCreatePinnedToCore(radio_tx_task, "radio tx", 1024*4, 0, 2, &radio_param.rx_task, p->cpu_core);
- //vTaskSuspend(radio_rx_task);
- break;
- }
- default: {
- ESP_LOGE(RADIO_TAG, "invalid configuration");
- radio_param.type = NOT_CONFIGURED;
- //TODO: Add assert to stop functionality
- break;
- }
- }
- // unlock radio
- xSemaphoreGive( xRadioMutex );
- }
|