pico_tnc/pico_tnc/send.c

439 wiersze
13 KiB
C

/**
* Copyright (c) 2021 JN1DFF
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* send.c - send packet
*/
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "pico/stdlib.h"
#include "hardware/dma.h"
#include "hardware/pwm.h"
#include "hardware/irq.h"
#include "hardware/sync.h"
#include "hardware/pio.h"
#include "hardware/structs/uart.h"
#include "pico/util/queue.h"
#include "pio_dac.pio.h"
#include "tnc.h"
#include "send.h"
#include "ax25.h"
//#include "wave_table.h"
#include "wave_table.h"
static const int pwm_pins[] = {
10, // port 0
8, // port 1
6, // port 2
};
static const int ptt_pins[] = {
11, // port 0
9, // port 1
7, // port 2
};
#define LED_PIN PICO_DEFAULT_LED_PIN
#define ISR_PIN 15
#define CAL_TIMEOUT (60 * 100) // 60 sec
static void __isr dma_handler(void)
{
int int_status = dma_hw->ints0;
//printf("irq: %08x\n", dma_hw->ints0);
for (int i = 0; i < PORT_N; i++) {
tnc_t *tp = &tnc[i];
if (int_status & tp->data_chan_mask) {
// generate modem signal
uint32_t *addr;
if (queue_try_remove(&tp->dac_queue, &addr)) {
dma_channel_set_read_addr(tp->ctrl_chan, addr, true);
#if 0
printf("dma_hander: block = %p\n", &block[0]);
for (int i = 0; i < CONTROL_N + 1; i++) {
printf("block[%d] = %p\n", i, block[i]);
}
#endif
} else {
gpio_put(tp->ptt_pin, 0); // PTT off
//pwm_set_chan_level(tp->pwm_slice, PWM_CHAN_A, 0); // set pwm level 0
tp->busy = false;
//printf("(%u) dma_handler: queue is empty, port = %d, data_chan = %d, ints = %08x\n", tnc_time(), tp->port, tp->data_chan, int_status);
}
//dma_hw->ints0 = tp->data_chan_mask;
}
} // for
dma_hw->ints0 = int_status;
}
static void send_start(tnc_t *tp)
{
if (!tp->busy) {
gpio_put(tp->ptt_pin, 1); // PTT on
tp->busy = true;
//printf("restart dma, ctrl = %08x, port = %d\n", dma_hw->ch[tp->data_chan].ctrl_trig, tp->port);
dma_channel_set_read_addr(tp->data_chan, NULL, true); // trigger NULL interrupt
}
}
bool send_packet(tnc_t *tp, uint8_t *data, int len)
{
int length = len + 2; // fcs 2 byte
uint8_t byte;
if (send_queue_free(tp) < length + 2) return false; // queue has no room
// packet length
byte = length;
queue_try_add(&tp->send_queue, &byte);
byte = length >> 8;
queue_try_add(&tp->send_queue, &byte);
// send packet to queue
for (int i = 0; i < len; i++) {
queue_try_add(&tp->send_queue, &data[i]);
}
int fcs = ax25_fcs(0, data, len);
// fcs
byte = fcs;
queue_try_add(&tp->send_queue, &byte);
byte = fcs >> 8;
queue_try_add(&tp->send_queue, &byte);
return true;
}
int send_byte(tnc_t *tp, uint8_t data, bool bit_stuff)
{
int idx = 0;
// generate modem signal
if (!queue_is_full(&tp->dac_queue)) {
//uint8_t data = rand();
int byte = data | 0x100; // sentinel
int bit = byte & 1;
while (byte > 1) { // check sentinel
if (tp->do_nrzi) {
if (!bit) tp->level ^= 1; // NRZI, invert if original bit == 0
} else {
tp->level = bit;
}
// make Bell202 CPAFSK audio samples
tp->dma_blocks[tp->next][idx++] = phase_tab[tp->level][tp->phase]; // 1: mark, 0: space
if (!tp->level) { // need adjust phase if space (2200Hz)
if (--tp->phase < 0) tp->phase = PHASE_CYCLE - 1;
}
// bit stuffing
if (bit_stuff) {
if (bit) {
if (++tp->cnt_one >= BIT_STUFF_BITS) {
// insert "0" bit
bit = 0;
continue; // while
}
} else {
tp->cnt_one = 0;
}
}
byte >>= 1;
bit = byte & 1;
}
// insert DMA end mark
tp->dma_blocks[tp->next][idx] = NULL;
// send fsk data to dac queue
uint32_t const **block = &tp->dma_blocks[tp->next][0];
if (queue_try_add(&tp->dac_queue, &block)) {
#if 0
if (!tp->busy) {
tp->busy = true;
dma_channel_set_read_addr(tp->data_chan, NULL, true);
//printf("restart dma, ctrl = %08x, port = %d\n", dma_hw->ch[tp->data_chan].ctrl_trig, tp->port);
}
#endif
if (++tp->next >= DAC_BLOCK_LEN) tp->next = 0;
//printf("main: queue add success\n");
} else {
printf("main: queue add fail\n");
}
} else {
send_start(tp);
}
return idx;
}
void send_init(void)
{
// set system clock, PWM uses system clock
//set_sys_clock_khz(SYS_CLK_KHZ, true);
// pio_dac initialize
PIO pio = pio0;
// load pio_dac program
uint offset = pio_add_program(pio, &pio_dac_program);
// initialize tnc[]
for (int i = 0; i < PORT_N; i++) {
tnc_t *tp = &tnc[i];
// port No.
tp->port = i;
// queue
queue_init(&tp->dac_queue, sizeof(uint32_t *), DAC_QUEUE_LEN);
// PTT pins
tp->ptt_pin = ptt_pins[i];
gpio_init(tp->ptt_pin);
gpio_set_dir(tp->ptt_pin, true); // output
gpio_put(tp->ptt_pin, 0);
#if 0
// PWM pins
tp->pwm_pin = pwm_pins[i];
// PWM configuration
gpio_set_function(tp->pwm_pin, GPIO_FUNC_PWM);
// PWM slice
tp->pwm_slice = pwm_gpio_to_slice_num(tp->pwm_pin);
// PWM configuration
pwm_config pc = pwm_get_default_config();
pwm_config_set_clkdiv_int(&pc, 1); // 1.0
pwm_config_set_wrap(&pc, PWM_CYCLE - 1);
pwm_init(tp->pwm_slice, &pc, true); // start PWM
#endif
// PIO
// find free state machine
uint sm = pio_claim_unused_sm(pio, true);
pio_dac_program_init(pio, sm, offset, pwm_pins[i], PIO_DAC_FS);
// DMA
tp->ctrl_chan = dma_claim_unused_channel(true);
tp->data_chan = dma_claim_unused_channel(true);
tp->data_chan_mask = 1 << tp->data_chan;
//printf("port %d: pwm_pin = %d, ptt_pin = %d, ctrl_chan = %d, data_chan = %d\n", tp->port, tp->pwm_pin, tp->ptt_pin, tp->ctrl_chan, tp->data_chan);
// DMA control channel
dma_channel_config dc = dma_channel_get_default_config(tp->ctrl_chan);
channel_config_set_transfer_data_size(&dc, DMA_SIZE_32);
channel_config_set_read_increment(&dc, true);
channel_config_set_write_increment(&dc, false);
dma_channel_configure(
tp->ctrl_chan,
&dc,
&dma_hw->ch[tp->data_chan].al3_read_addr_trig, // Initial write address
NULL, // Initial read address
1, // Halt after each control block
false // Don't start yet
);
// DMA data channel
dc = dma_channel_get_default_config(tp->data_chan);
channel_config_set_transfer_data_size(&dc, DMA_SIZE_32); // pio_dac data size, 32bit
channel_config_set_dreq(&dc, pio_get_dreq(pio, sm, true)); // pio sm, TX
channel_config_set_chain_to(&dc, tp->ctrl_chan);
channel_config_set_irq_quiet(&dc, true);
// set high priority bit
//dc.ctrl |= DMA_CH0_CTRL_TRIG_HIGH_PRIORITY_BITS;
dma_channel_configure(
tp->data_chan,
&dc,
&pio0_hw->txf[sm], // The initial write address
NULL, // Initial read address and transfer count are unimportant;
BIT_CYCLE, // audio data of 1/1200 s
false // Don't start yet.
);
// configure IRQ
dma_channel_set_irq0_enabled(tp->data_chan, true);
}
// configure IRQ
irq_set_exclusive_handler(DMA_IRQ_0, dma_handler);
//irq_add_shared_handler(DMA_IRQ_0, dma_handler, PICO_SHARED_IRQ_HANDLER_DEFAULT_ORDER_PRIORITY);
irq_set_enabled(DMA_IRQ_0, true);
// ISR time measurement
gpio_init(ISR_PIN);
gpio_set_dir(ISR_PIN, true);
#define SMPS_PIN 23
// SMPS set PWM mode
gpio_init(SMPS_PIN);
gpio_set_dir(SMPS_PIN, true);
gpio_put(SMPS_PIN, 1);
}
int send_queue_free(tnc_t *tp)
{
return SEND_QUEUE_LEN - queue_get_level(&tp->send_queue);
}
void send(void)
{
uint8_t data;
tnc_t *tp = &tnc[0];
while (tp < &tnc[PORT_N]) {
switch (tp->send_state) {
case SP_IDLE:
//printf("(%d) send: SP_IDEL\n", tnc_time());
if (!queue_is_empty(&tp->send_queue)) {
tp->send_state = SP_WAIT_CLR_CH;
continue;
}
break;
case SP_WAIT_CLR_CH:
//printf("(%d) send: SP_WAIT_CLR_CH\n", tnc_time());
if (tp->kiss_fullduplex || !tp->cdt) {
tp->send_state = SP_P_PERSISTENCE;
continue;
}
break;
case SP_P_PERSISTENCE:
data = rand();
//printf("(%d) send: SP_P_PERSISTENCE, rnd = %d\n", tnc_time(), data);
if (data <= tp->kiss_p) {
tp->send_state = SP_PTT_ON;
continue;
}
tp->send_time = tnc_time();
tp->send_state = SP_WAIT_SLOTTIME;
break;
case SP_WAIT_SLOTTIME:
//printf("(%d) send: SP_WAIT_SLOTTIME\n", tnc_time());
if (tnc_time() - tp->send_time >= tp->kiss_slottime) {
tp->send_state = SP_WAIT_CLR_CH;
continue;
}
break;
case SP_PTT_ON:
//printf("(%d) send: SP_PTT_ON\n", tnc_time());
//gpio_put(tp->ptt_pin, 1);
tp->send_len = (tp->kiss_txdelay * 3) / 2 + 1; // TXDELAY * 10 [ms] into number of flags
tp->send_state = SP_SEND_FLAGS;
/* FALLTHROUGH */
case SP_SEND_FLAGS:
//printf("(%d) send: SP_SEND_FLAGS\n", tnc_time());
while (tp->send_len > 0 && send_byte(tp, AX25_FLAG, false)) { // false: bit stuffing off
--tp->send_len;
}
if (tp->send_len > 0) break;
tp->cnt_one = 0; // bit stuffing counter clear
tp->send_state = SP_DATA_START;
/* FALLTHROUGH */
case SP_DATA_START:
if (!queue_try_remove(&tp->send_queue, &data)) {
tp->send_state = SP_IDLE;
break;
}
// read packet length low byte
tp->send_len = data;
if (!queue_try_remove(&tp->send_queue, &data)) {
printf("send: send_queue underrun, len\n");
tp->send_state = SP_IDLE;
break;
}
// read packet length high byte
tp->send_len += data << 8;
//printf("(%d) send: SP_DATA_START, len = %d\n", tnc_time(), tp->send_len);
if (!queue_try_remove(&tp->send_queue, &data)) {
printf("send: send_queue underrun, data(1)\n");
tp->send_state = SP_IDLE;
break;
}
tp->send_data = data;
--tp->send_len;
tp->send_state = SP_DATA;
/* FALLTHROUGH */
case SP_DATA:
//printf("(%d) send: SP_DATA\n", tnc_time());
if (!send_byte(tp, tp->send_data, true)) break;
if (tp->send_len <= 0) {
tp->send_len = 1;
tp->send_state = SP_SEND_FLAGS;
send_start(tp);
continue;
}
if (!queue_try_remove(&tp->send_queue, &data)) {
printf("send: send_queue underrun, data(2)\n");
tp->send_state = SP_IDLE;
break;
}
--tp->send_len;
tp->send_data = data;
continue;
case SP_ERROR:
//printf("(%d) send: SP_ERROR\n", tnc_time());
while (queue_try_remove(&tp->send_queue, &data)) {
}
tp->send_state = SP_IDLE;
break;
case SP_CALIBRATE:
if (tnc_time() - tp->cal_time >= CAL_TIMEOUT) {
tp->send_state = SP_CALIBRATE_OFF;
} else {
send_byte(tp, tp->cal_data, false);
}
break;
}
tp++;
} // while
}