WIP: add librfn
parent
84524934a2
commit
9b21fe50db
|
@ -0,0 +1,7 @@
|
|||
*.d
|
||||
*.o
|
||||
|
||||
*.map
|
||||
*.elf
|
||||
*.bin
|
||||
*.srec
|
|
@ -2,3 +2,6 @@
|
|||
path = libopencm3
|
||||
url = https://github.com/libopencm3/libopencm3.git
|
||||
branch = master
|
||||
[submodule "librfn"]
|
||||
path = librfn
|
||||
url = https://github.com/daniel-thompson/librfn.git
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 80f85d0325ced8ab56887afa567c2449ab5f2320
|
|
@ -0,0 +1,17 @@
|
|||
-include $(OBJS:.o=.d)
|
||||
|
||||
LIBRFN_DIR = ../../librfn
|
||||
|
||||
ifeq ($(V),1)
|
||||
$(info Using $(LIBRFN_DIR) path to librfn library)
|
||||
endif
|
||||
|
||||
OBJS += \
|
||||
ringbuf.o
|
||||
|
||||
vpath %.c $(LIBRFN_DIR)/librfn
|
||||
vpath %.c $(LIBRFN_DIR)/librfn/libopencm3
|
||||
|
||||
CPPFLAGS += -DNDEBUG
|
||||
CPPFLAGS += -DCONFIG_CONSOLE_FROM_ISR=1
|
||||
CPPFLAGS += -I$(LIBRFN_DIR)/include
|
|
@ -38,4 +38,5 @@ OOCD_TARGET ?= stm32f1x
|
|||
|
||||
all: elf bin srec
|
||||
|
||||
include ../../mk/librfn.mk
|
||||
include ../rules.mk
|
||||
|
|
|
@ -0,0 +1,152 @@
|
|||
/* Code to control digital step attenuators of the sysmocom RFDN board
|
||||
* (C) 2017 by Harald Welte <laforge@gnumonks.org>
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <libopencm3/stm32/gpio.h>
|
||||
#include <libopencm3/stm32/rcc.h>
|
||||
|
||||
#include "attenuator.h"
|
||||
#include "utils.h"
|
||||
|
||||
static const struct attenuator_cfg *g_att_cfg;
|
||||
static struct attenuator_state **g_att_state;
|
||||
|
||||
static void delay()
|
||||
{
|
||||
volatile uint32_t i;
|
||||
for (i = 0; i < 10000; i++) { }
|
||||
}
|
||||
|
||||
/* create a positive pulse on a given GPIO line */
|
||||
static void gpio_pulse(uint32_t bank, uint32_t nr)
|
||||
{
|
||||
gpio_set(bank, nr);
|
||||
/* FIXME: Some delay, 30ns for LE or CLK */
|
||||
delay();
|
||||
gpio_clear(bank, nr);
|
||||
}
|
||||
|
||||
/* set the data line to high or low */
|
||||
static void gpio_set_data(int high)
|
||||
{
|
||||
if (high)
|
||||
gpio_set(g_att_cfg->gpio_data.bank, g_att_cfg->gpio_data.gpio_nr);
|
||||
else
|
||||
gpio_clear(g_att_cfg->gpio_data.bank, g_att_cfg->gpio_data.gpio_nr);
|
||||
}
|
||||
|
||||
/* pulse the clock line with one positive pulse */
|
||||
static void gpio_pulse_clk(void)
|
||||
{
|
||||
gpio_pulse(g_att_cfg->gpio_clock.bank, g_att_cfg->gpio_clock.gpio_nr);
|
||||
}
|
||||
|
||||
/* set a given attenuator to a given value
|
||||
* \param channel The RF channel (0..7)
|
||||
* \param stage Attenuator stage (0..1)
|
||||
* \param value in quarter-dB (0..124)
|
||||
*/
|
||||
int attenuator_set(uint8_t channel, uint8_t stage, uint8_t val_qdb)
|
||||
{
|
||||
uint8_t val = val_qdb/4; /* whole dB for this attenuator */
|
||||
const struct attenuator_def *ad;
|
||||
int i;
|
||||
|
||||
if (!g_att_cfg)
|
||||
return -ENODEV;
|
||||
|
||||
if (channel >= g_att_cfg->num_channels)
|
||||
return -ENODEV;
|
||||
|
||||
if (stage >= g_att_cfg->channels[channel].num_stages)
|
||||
return -ENODEV;
|
||||
|
||||
ad = &g_att_cfg->channels[channel].stage[stage];
|
||||
|
||||
if (val > 0x1f)
|
||||
return -ERANGE;
|
||||
|
||||
/* The shift register should be loaded while LE is held low to
|
||||
* prevent the attenuator value from changing as data is
|
||||
* entered */
|
||||
for (i = 5; i >= 0; i--) {
|
||||
unsigned int bit = ((val >> i) & 1);
|
||||
gpio_set_data(bit);
|
||||
gpio_pulse_clk();
|
||||
}
|
||||
|
||||
/* The LE input should then be toggled HIGH and brought LOW
|
||||
* again, latching the new data. Minimum LE pulse width is 30ns */
|
||||
gpio_pulse(ad->le.bank, ad->le.gpio_nr);
|
||||
|
||||
/* actual value we have set may not be exactly what was requested */
|
||||
g_att_state[channel][stage].value_qdB.current = val*4;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* get current attenuator value in quarter-dB */
|
||||
int attenuator_get(uint8_t channel, uint8_t stage, enum attenuator_value av)
|
||||
{
|
||||
if (!g_att_cfg)
|
||||
return -ENODEV;
|
||||
|
||||
if (channel >= g_att_cfg->num_channels)
|
||||
return -ENODEV;
|
||||
|
||||
if (stage >= g_att_cfg->channels[channel].num_stages)
|
||||
return -ENODEV;
|
||||
|
||||
switch (av) {
|
||||
case ATT_VAL_CURRENT:
|
||||
return g_att_state[channel][stage].value_qdB.current;
|
||||
case ATT_VAL_STARTUP:
|
||||
return g_att_state[channel][stage].value_qdB.startup;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
/* initialize the attenuator driver */
|
||||
void attenuator_init(const struct attenuator_cfg *cfg,
|
||||
struct attenuator_state **state)
|
||||
{
|
||||
uint32_t banks[6] = { GPIOA, GPIOB, GPIOC, GPIOD, GPIOE, GPIOF };
|
||||
const uint32_t periph[6] = { RCC_GPIOA, RCC_GPIOB, RCC_GPIOC,
|
||||
RCC_GPIOD, RCC_GPIOE, RCC_GPIOF };
|
||||
uint32_t pins[6] = {0, 0, 0, 0, 0, 0};
|
||||
unsigned int i, j, k;
|
||||
|
||||
g_att_cfg = cfg;
|
||||
g_att_state = state;
|
||||
|
||||
/* collect the bit mask of all LE pins for each bank */
|
||||
for (i = 0; i < g_att_cfg->num_channels; i++) {
|
||||
const struct attenuator_channel_def *ch = &g_att_cfg->channels[i];
|
||||
for (j = 0; j < ch->num_stages; j++) {
|
||||
const struct attenuator_def *at = &ch->stage[j];
|
||||
for (k = 0; k < ARRAY_SIZE(banks); k++) {
|
||||
if (at->le.bank != banks[k])
|
||||
continue;
|
||||
pins[k] |= at->le.gpio_nr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* add the shared DATA / CLOCK pins */
|
||||
for (k = 0; k < ARRAY_SIZE(banks); k++) {
|
||||
if (g_att_cfg->gpio_clock.bank == banks[k])
|
||||
pins[k] |= g_att_cfg->gpio_clock.gpio_nr;
|
||||
if (g_att_cfg->gpio_data.bank == banks[k])
|
||||
pins[k] |= g_att_cfg->gpio_data.gpio_nr;
|
||||
}
|
||||
|
||||
/* configure the GPIO of each bank based on he collected pin-bitmasks */
|
||||
for (k = 0; k < ARRAY_SIZE(banks); k++) {
|
||||
if (!pins[k])
|
||||
continue;
|
||||
rcc_periph_clock_enable(periph[k]);
|
||||
gpio_set_mode(banks[k], GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, pins[k]);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
#pragma once
|
||||
#include <stdint.h>
|
||||
|
||||
struct attenuator_gpio {
|
||||
uint32_t bank;
|
||||
uint32_t gpio_nr;
|
||||
};
|
||||
|
||||
|
||||
/* definition o the Latch Enable line for one given Attenuator */
|
||||
struct attenuator_def {
|
||||
struct attenuator_gpio le;
|
||||
};
|
||||
|
||||
/* Our board has two attenuators in series for each RF channel */
|
||||
struct attenuator_channel_def {
|
||||
const char *name;
|
||||
unsigned int num_stages;
|
||||
const struct attenuator_def *stage;
|
||||
};
|
||||
|
||||
struct attenuator_cfg {
|
||||
unsigned int num_channels;
|
||||
const struct attenuator_channel_def *channels;
|
||||
struct attenuator_gpio gpio_clock;
|
||||
struct attenuator_gpio gpio_data;
|
||||
};
|
||||
|
||||
enum attenuator_value {
|
||||
ATT_VAL_CURRENT,
|
||||
ATT_VAL_STARTUP
|
||||
};
|
||||
|
||||
/* dynamic [runtime] state of one attenuator chip */
|
||||
struct attenuator_state {
|
||||
struct {
|
||||
uint8_t current;
|
||||
uint8_t startup;
|
||||
} value_qdB;
|
||||
};
|
||||
|
||||
int attenuator_set(uint8_t channel, uint8_t stage, uint8_t val_qdb);
|
||||
int attenuator_get(uint8_t channel, uint8_t stage, enum attenuator_value av);
|
||||
void attenuator_init(const struct attenuator_cfg *cfg,
|
||||
struct attenuator_state **state);
|
|
@ -0,0 +1,47 @@
|
|||
|
||||
#include <libopencm3/stm32/gpio.h>
|
||||
|
||||
#include "attenuator.h"
|
||||
#include "utils.h"
|
||||
|
||||
#define NUM_CHAN 4
|
||||
#define NUM_STAGE 2
|
||||
|
||||
static const struct attenuator_def attenuator_def_at1[NUM_STAGE] = {
|
||||
{ { GPIOB, GPIO3 } }, { { GPIOA, GPIO15 } },
|
||||
};
|
||||
static const struct attenuator_def attenuator_def_at2[NUM_STAGE] = {
|
||||
{ { GPIOB, GPIO5 } }, { { GPIOB, GPIO4 } },
|
||||
};
|
||||
static const struct attenuator_def attenuator_def_at3[NUM_STAGE] = {
|
||||
{ { GPIOD, GPIO0 } }, { { GPIOD, GPIO1 } },
|
||||
};
|
||||
static const struct attenuator_def attenuator_def_at4[NUM_STAGE] = {
|
||||
{ { GPIOA, GPIO0 } }, { { GPIOA, GPIO1 } },
|
||||
};
|
||||
static struct attenuator_state attenuator_st_at1[NUM_STAGE];
|
||||
static struct attenuator_state attenuator_st_at2[NUM_STAGE];
|
||||
static struct attenuator_state attenuator_st_at3[NUM_STAGE];
|
||||
static struct attenuator_state attenuator_st_at4[NUM_STAGE];
|
||||
|
||||
/* LE definition of the Attenuator Channels, taken from schematics */
|
||||
static const struct attenuator_channel_def channels[NUM_CHAN] = {
|
||||
[0] = { "AT1", ARRAY_SIZE(attenuator_def_at1), attenuator_def_at1 },
|
||||
[1] = { "AT2", ARRAY_SIZE(attenuator_def_at2), attenuator_def_at2 },
|
||||
[2] = { "AT3", ARRAY_SIZE(attenuator_def_at3), attenuator_def_at3 },
|
||||
[3] = { "AT4", ARRAY_SIZE(attenuator_def_at4), attenuator_def_at4 },
|
||||
};
|
||||
|
||||
const struct attenuator_cfg board_att_cfg = {
|
||||
.num_channels = ARRAY_SIZE(channels),
|
||||
.channels = channels,
|
||||
.gpio_clock = { GPIOA, GPIO11 },
|
||||
.gpio_data = { GPIOA, GPIO12 },
|
||||
};
|
||||
|
||||
struct attenuator_state *board_att_st[NUM_CHAN] = {
|
||||
[0] = attenuator_st_at1,
|
||||
[1] = attenuator_st_at2,
|
||||
[2] = attenuator_st_at3,
|
||||
[3] = attenuator_st_at4,
|
||||
};
|
|
@ -0,0 +1,5 @@
|
|||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
extern void msleep(uint32_t delay);
|
|
@ -0,0 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
/*! Determine number of elements in an array of static size */
|
||||
#ifndef ARRAY_SIZE
|
||||
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
|
||||
#endif
|
|
@ -204,8 +204,8 @@ print-%:
|
|||
$(Q)$(LD) $(TGT_LDFLAGS) $(LDFLAGS) $(OBJS) $(LDLIBS) -o $(*).elf
|
||||
|
||||
%.o: %.c
|
||||
@#printf " CC $(*).c\n"
|
||||
$(Q)$(CC) $(TGT_CFLAGS) $(CFLAGS) $(TGT_CPPFLAGS) $(CPPFLAGS) -o $(*).o -c $(*).c
|
||||
#@printf " CC $(*).c\n"
|
||||
$(Q)$(CC) $(TGT_CFLAGS) $(CFLAGS) $(TGT_CPPFLAGS) $(CPPFLAGS) -o $@ -c $<
|
||||
|
||||
%.o: %.cxx
|
||||
@#printf " CXX $(*).cxx\n"
|
||||
|
|
Loading…
Reference in New Issue