commit 7c46bd12f65e1ab0c794cefc7866c196bee4ea92 Author: Philipp Date: Mon Sep 26 23:43:14 2016 +0200 Utility: Adding tool to control Attenuator (ZX76-31R5-SP+) - Uses sysfs GPIO to control the attenuator chip - Attenuation setable in 0.5 dB steps, maximum is 31.5 dB - Attenuators are referenced by their ID (1-4) diff --git a/util/Makefile b/util/Makefile new file mode 100644 index 0000000..9f6b31e --- /dev/null +++ b/util/Makefile @@ -0,0 +1,13 @@ + +all: main.o gpio.o + gcc -Wall ./*.o -losmocore -ltalloc -o attctrl -O3 + +main.o: main.c + gcc -Wall -c main.c + +gpio.o: gpio.c + gcc -Wall -c gpio.c + +clean: + rm -f ./*.o + rm -f ./attctrl diff --git a/util/gpio.c b/util/gpio.c new file mode 100644 index 0000000..15d2566 --- /dev/null +++ b/util/gpio.c @@ -0,0 +1,143 @@ +/* GPIO handler routines */ + +/* (C) 2016 by sysmocom s.f.m.c. GmbH + * All Rights Reserved + * + * Author: Philipp Maier + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include "gpio.h" + +#define SYSFS_EXPORT "/sys/class/gpio/export" +#define SYSFS_UNEXPORT "/sys/class/gpio/unexport" +#define SYSFS_DIRECTION "/sys/class/gpio/gpio%d/direction" +#define SYSFS_VALUE "/sys/class/gpio/gpio%d/value" + +/* Open sysfs endpoints and initalize GPIOs */ +struct gpio_descr *gpio_open(void *ctx, unsigned int *gpios, + unsigned int gpios_len) +{ + int i; + char buf[255]; + FILE *fd_export = NULL; + FILE *fd_direction[MAX_GPIOS]; + FILE *fd_value[MAX_GPIOS]; + struct gpio_descr *gd; + + OSMO_ASSERT(gpios); + OSMO_ASSERT(gpios_len <= MAX_GPIOS); + + /* Export requested GPIOs to userspace */ + fd_export = fopen(SYSFS_EXPORT, "w"); + if (!fd_export) { + printf("Error: Could not open: %s\n", SYSFS_EXPORT); + return NULL; + } else { + for (i = 0; i < gpios_len; i++) { + sprintf(buf, "%d", gpios[i]); + rewind(fd_export); + fputs(buf, fd_export); + } + fclose(fd_export); + fd_export = NULL; + } + + /* Open GPIO file descriptors */ + for (i = 0; i < gpios_len; i++) { + sprintf(buf, SYSFS_DIRECTION, gpios[i]); + fd_direction[i] = fopen(buf, "w"); + if (!fd_direction[i]) { + printf("Error: Could not open: %s\n", buf); + return NULL; + } + + sprintf(buf, SYSFS_VALUE, gpios[i]); + fd_value[i] = fopen(buf, "w"); + if (!fd_value[i]) { + printf("Error: Could not open: %s\n", buf); + return NULL; + } + } + + /* Configure GPIOs as outputs */ + for (i = 0; i < gpios_len; i++) { + rewind(fd_direction[i]); + fputs("out", fd_direction[i]); + fflush(fd_direction[i]); + } + + /* Generate descriptor struct */ + gd = talloc_zero(ctx, struct gpio_descr); + memcpy(gd->gpios, gpios, sizeof(gd->gpios)); + gd->gpios_len = gpios_len; + memcpy(gd->fd_direction, fd_direction, sizeof(gd->fd_direction)); + memcpy(gd->fd_value, fd_value, sizeof(gd->fd_value)); + + return gd; +} + +/* Close sysfs endpoints and unintialize GPIOs */ +void gpio_close(struct gpio_descr *gd) +{ + int i; + char buf[255]; + FILE *fd_unexport = NULL; + + OSMO_ASSERT(gd); + + /* Close GPIOs file descriptors */ + for (i = 0; i < gd->gpios_len; i++) { + if (gd->fd_direction[i]) + fclose(gd->fd_direction[i]); + if (gd->fd_value[i]) + fclose(gd->fd_value[i]); + } + + /* Remove requested GPIOs from userspace */ + fd_unexport = fopen(SYSFS_UNEXPORT, "w"); + if (fd_unexport) { + for (i = 0; i < gd->gpios_len; i++) { + sprintf(buf, "%d", gd->gpios[i]); + rewind(fd_unexport); + fputs(buf, fd_unexport); + } + fclose(fd_unexport); + } + + /* Free descriptor struct */ + talloc_free(gd); +} + +/* Set GPIO pin status */ +void gpio_set(struct gpio_descr *gd, unsigned int gpio, unsigned int state) +{ + OSMO_ASSERT(gd); + OSMO_ASSERT(gpio < gd->gpios_len); + + rewind(gd->fd_value[gpio]); + if (state) + fputs("1", gd->fd_value[gpio]); + else + fputs("0", gd->fd_value[gpio]); + + fflush(gd->fd_value[gpio]); +} diff --git a/util/gpio.h b/util/gpio.h new file mode 100644 index 0000000..6b10f38 --- /dev/null +++ b/util/gpio.h @@ -0,0 +1,43 @@ +/* GPIO handler routines */ + +/* (C) 2016 by sysmocom s.f.m.c. GmbH + * All Rights Reserved + * + * Author: Philipp Maier + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#pragma once + +#define MAX_GPIOS 16 + +/* Struct holding the GPIO file descriptors and + meta information about the GPIO itsself */ +struct gpio_descr { + unsigned int gpios[MAX_GPIOS]; + unsigned int gpios_len; + FILE *fd_direction[MAX_GPIOS]; + FILE *fd_value[MAX_GPIOS]; +}; + +/* Open sysfs endpoints and initalize GPIOs */ +struct gpio_descr *gpio_open(void *ctx, unsigned int *gpios, + unsigned int gpios_len); + +/* Close sysfs endpoints and unintialize GPIOs */ +void gpio_close(struct gpio_descr *gd); + +/* Set GPIO pin status */ +void gpio_set(struct gpio_descr *gd, unsigned int gpio, unsigned int state); diff --git a/util/main.c b/util/main.c new file mode 100644 index 0000000..f90ae4b --- /dev/null +++ b/util/main.c @@ -0,0 +1,175 @@ +/* Tool to control ZX76-31R5-SP-S+ digital attenuators on LTHW Main unit */ + +/* (C) 2016 by sysmocom s.f.m.c. GmbH + * All Rights Reserved + * + * Author: Philipp Maier + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "gpio.h" + +/* GPIO pin assignment */ +#define GPIO_LE_1 13 +#define GPIO_LE_2 7 +#define GPIO_LE_3 5 +#define GPIO_LE_4 11 +#define GPIO_DATA 15 +#define GPIO_CLK 17 + +/* Internal GPIO alias */ +#define LE_1 0 +#define LE_2 1 +#define LE_3 2 +#define LE_4 3 +#define DATA 4 +#define CLK 5 + +/* ZX76-31R5-SP-S+ properties */ +#define DB_MAX 31.5 + +/* Generate a clock or latch enable pulse */ +static inline void gpio_pulse(struct gpio_descr *gd, unsigned int gpio) +{ + gpio_set(gd, gpio, 1); + gpio_set(gd, gpio, 0); +} + +/* Write control word to Attenuator */ +void att_write(struct gpio_descr *gd, uint8_t cw, unsigned int le) +{ + int i; + unsigned int bit; + + /* Write control word (val) to Attenuator */ + for (i = 5; i >= 0; i--) { + bit = ((cw >> i) & 1); + gpio_set(gd, DATA, bit); + gpio_pulse(gd, CLK); + } + + /* Generate latch enable pulse */ + gpio_pulse(gd, le); +} + +/* Initalize attenuators */ +struct gpio_descr *att_init(void) +{ + unsigned int gpios[6]; + struct gpio_descr *gd; + + /* GPIO pin mapping */ + gpios[LE_1] = GPIO_LE_1; + gpios[LE_2] = GPIO_LE_2; + gpios[LE_3] = GPIO_LE_3; + gpios[LE_4] = GPIO_LE_4; + gpios[DATA] = GPIO_DATA; + gpios[CLK] = GPIO_CLK; + + /* Open GPIO */ + gd = gpio_open(NULL, gpios, 6); + if (!gd) { + printf("Error: Unable to open GPIO\n"); + exit(1); + } + + /* Initalize GPIOs to low level */ + gpio_set(gd, LE_1, 0); + gpio_set(gd, LE_2, 0); + gpio_set(gd, LE_3, 0); + gpio_set(gd, LE_4, 0); + gpio_set(gd, DATA, 0); + gpio_set(gd, CLK, 0); + + return gd; +} + +/* Set attenuator value */ +void att_set(struct gpio_descr *gd, double val, unsigned int le) +{ + uint8_t cw; + + if (val > DB_MAX || val < 0) { + printf("Error: Invalid attenuation value!\n"); + return; + } + + printf("Setting attenuator #%d to value %.1lf...\n", le + 1, val); + cw = (uint8_t) (val * 2); + att_write(gd, cw, le); +} + +/* Print online help */ +void print_help(char *progname) +{ + printf("Usage: %s -i ID -a ATT\n", progname); + printf("Attenuation (ATT) in 0.5db steps, range 0-31.5\n"); + printf("\n"); + exit(0); +} + +int main(int argc, char *argv[]) +{ + struct gpio_descr *gd; + int option; + double getopt_att = -1; + int getopt_id = -1; + + /* Parse options */ + while ((option = getopt(argc, argv, "hi:a:")) != -1) { + switch (option) { + case 'h': + print_help(basename(argv[0])); + break; + case 'a': + getopt_att = atof(optarg); + break; + case 'i': + getopt_id = atoi(optarg) - 1; + break; + } + } + + /* Check options */ + if (getopt_att < 0) { + printf("Error: No attenuation value specified!\n"); + exit(1); + } + + if (getopt_id < 0) { + printf("Error: No attenuator id specified!\n"); + exit(1); + } + + if (getopt_id > 3) { + printf("Error: Invalid attenuator id specified!\n"); + exit(1); + } + + /* Set attenuation */ + gd = att_init(); + att_set(gd, getopt_att, getopt_id); + gpio_close(gd); + + return 0; +}