New TS-7400 based rotor backend

From Øystein Hårberg, LA7LKA, a backend for a rotor based on the TS-7400
embedded ARM board running Linux from http://www.embeddedarm.com/

Signed-off-by: Nate Bargmann <n0nb@n0nb.us>
Hamlib-1.2.14
Øystein Hårberg 2011-07-09 14:37:34 +02:00 zatwierdzone przez Nate Bargmann
rodzic 9ebd14decd
commit e53bfe96b8
14 zmienionych plików z 1543 dodań i 2 usunięć

Wyświetl plik

@ -18,7 +18,7 @@ DIST_SUBDIRS = macros include lib $(subdirs) src c++ bindings tests doc \
icom kenwood aor yaesu dummy pcr alinco uniden tentec kachina jrc \
rpcrig winradio easycomm fodtrack rpcrot drake rotorez \
flexradio sartek lowe rft rs tapr kit skanti prm80 wj racal tuner \
gs232a heathkit spid ars m2 amsat scripts
gs232a heathkit spid ars m2 amsat scripts ts7400
rpm: Makefile dist
$(RPMBUILD) -ta $(PACKAGE)-$(VERSION).tar.gz

Wyświetl plik

@ -259,7 +259,7 @@ AC_SUBST(RIGMATRIX)
BACKEND_LIST="icom kenwood aor yaesu dummy pcr alinco uniden tentec kachina jrc drake lowe rft rs kit skanti prm80 tapr flexradio wj racal tuner"
ROT_BACKEND_LIST="dummy easycomm fodtrack gs232a heathkit kit rotorez sartek spid ars m2 amsat"
ROT_BACKEND_LIST="dummy easycomm fodtrack gs232a heathkit kit rotorez sartek spid ars m2 amsat ts7400"
BINDINGS=""
BINDING_ALL=""
BINDING_CHECK=""
@ -539,6 +539,7 @@ rotorez/Makefile
flexradio/Makefile
m2/Makefile
amsat/Makefile
ts7400/Makefile
scripts/Makefile
hamlib.pc
hamlib.spec]

Wyświetl plik

@ -254,6 +254,17 @@
#define ROT_MODEL_IF100 ROT_MAKE_MODEL(ROT_AMSAT, 1)
/*! \def ROT_MODEL_TS7400
* \brief A macro that returns the model number of the TS7400 backend.
*
* The TS-7400 backend supports and embedded ARM board using the TS-7400
* Linux board. More information is at http://www.embeddedarm.com
*/
#define ROT_TS7400 13
#define ROT_BACKEND_TS7400 "ts7400"
#define ROT_MODEL_TS7400 ROT_MAKE_MODEL(ROT_TS7400, 1)
/*! \typedef typedef int rot_model_t
\brief Convenience type definition for rotator model.
*/
@ -280,6 +291,7 @@ typedef int rot_model_t;
{ ROT_M2, ROT_BACKEND_M2 }, \
{ ROT_ARS, ROT_BACKEND_ARS }, \
{ ROT_AMSAT, ROT_BACKEND_AMSAT }, \
{ ROT_TS7400, ROT_BACKEND_TS7400 }, \
{ 0, NULL }, /* end */ \
}

11
ts7400/Makefile.am 100644
Wyświetl plik

@ -0,0 +1,11 @@
EXTRA_DIST = include/ep93xx_adc.h include/io.h include/peekpoke.h \
include/readADC.h include/io.c include/peekpoke.c include/readADC.c \
include/test7400ADC.c
pkglib_LTLIBRARIES = hamlib-ts7400.la
hamlib_ts7400_la_SOURCES = ts7400.c
hamlib_ts7400_la_LDFLAGS = -no-undefined -module -avoid-version
hamlib_ts7400_la_LIBADD = $(top_builddir)/src/libhamlib.la \
@MATH_LIBS@
noinst_HEADERS = ts7400.h

Wyświetl plik

@ -0,0 +1,78 @@
#define ADC_PAGE 0x80900000
#define ADCRESULT_OFFSET 0x0008
#define SDR_MASK 0x80000000
#define DATA_OFFSET 0x0008
#define DATA_MASK 0xFFFF
#define ADCSWITCH_OFFSET 0x0018
#define ADC_CH0 0x0608
#define ADC_CH1 0x0680
#define ADC_CH2 0x0640
#define ADC_CH3 0x0620
#define ADC_CH4 0x0610
#define ADCSWLOCK_OFFSET 0x0020
#define UNLOCK_VAL 0xAA
#define SYSCON_PAGE 0x80930000
#define ADCCLKDIV_OFFSET 0x0090
#define SYSCON_UNLOCK 0x00C0
#define TSEN_MASK 0x80000000
#define DEVICECFG_OFFSET 0x0080
#define ADCPD_MASK 0x04
#define ADCEN_MASK 0x20000
/* prototypes */
void init_ADC(unsigned long adc_page, unsigned long syscon_page);
int read_channel(unsigned long adc_page, unsigned short channel);
static char is_ADC_busy(unsigned long adc_page);
void init_ADC(unsigned long adc_page, unsigned long syscon_page)
{
unsigned long val;
/* set TSEN bit */
val = PEEK32(syscon_page + ADCCLKDIV_OFFSET);
//unlock the software lock
POKE32(syscon_page + SYSCON_UNLOCK, UNLOCK_VAL);
POKE32(syscon_page + ADCCLKDIV_OFFSET, TSEN_MASK | val);
/* set ADCEN bit */
val = PEEK32(syscon_page + DEVICECFG_OFFSET);
POKE32(syscon_page + SYSCON_UNLOCK, UNLOCK_VAL); //unlock the soft lock
POKE32(syscon_page + DEVICECFG_OFFSET, val | ADCEN_MASK);
/* clear ADCPD bit */
val = PEEK32(syscon_page + DEVICECFG_OFFSET);
POKE32(adc_page + SYSCON_UNLOCK, UNLOCK_VAL); //unlock the soft lock
POKE32(syscon_page + DEVICECFG_OFFSET, val & ~ADCPD_MASK);
}
int read_channel(unsigned long adc_page, unsigned short channel)
{
unsigned long val;
POKE32(adc_page + ADCSWLOCK_OFFSET, UNLOCK_VAL); //unlock the soft lock
//write ADCSwitch reg to select channel
POKE32(adc_page + ADCSWITCH_OFFSET, channel);
while(is_ADC_busy(adc_page)); //poll ADCResult
//read result from data regisyyter
val = PEEK32(adc_page + DATA_OFFSET) ;
val = val & DATA_MASK;
return val;
}
static char is_ADC_busy(unsigned long adc_page)
{
unsigned long val;
val = PEEK32(adc_page + ADCRESULT_OFFSET);
if((val & SDR_MASK) == SDR_MASK)
return TRUE;
return FALSE;
}

Wyświetl plik

@ -0,0 +1,49 @@
// filename button.c
// connect a button to DIO pin 1 and ground
// blinks green and red led on the ts-7200 when button is pressed
//
// compile arm-linux-gcc -o button button.c
//
#include<unistd.h>
#include<sys/types.h>
#include<sys/mman.h>
#include<stdio.h>
#include<fcntl.h>
#include<string.h>
int main(int argc, char **argv)
{
volatile unsigned int *PEDR, *PEDDR, *PBDR, *PBDDR, *GPIOBDB;
int i;
unsigned char state;
unsigned char *start;
int fd = open("/dev/mem", O_RDWR|O_SYNC);
start = mmap(0, getpagesize(), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0x80840000);
PBDR = (unsigned int *)(start + 0x04); // port b
PBDDR = (unsigned int *)(start + 0x14); // port b direction register
PEDR = (unsigned int *)(start + 0x20); // port e data
PEDDR = (unsigned int *)(start + 0x24); // port e direction register
GPIOBDB = (unsigned int *)(start + 0xC4); // debounce on port b
*PBDDR = 0xf0; // upper nibble output, lower nibble input
*PEDDR = 0xff; // all output (just 2 bits)
*GPIOBDB = 0x01; // enable debounce on bit 0
state = *PBDR; // read initial state
while (state & 0x01) { // wait until button goes low
state = *PBDR; // remember bit 0 is pulled up with 4.7k ohm
}
// blink 5 times, sleep 1 second so it's visible
for (i = 0; i < 5; i++) {
*PEDR = 0xff;
sleep(1);
*PEDR = 0x00;
sleep(1);
}
close(fd);
return 0;
}

Wyświetl plik

Wyświetl plik

@ -0,0 +1,112 @@
#include<unistd.h>
#include<sys/types.h>
#include<sys/mman.h>
#include<stdio.h>
#include<fcntl.h>
unsigned int parseBinary(char *str) {
unsigned int val = 0;
if (*str == 'b') {
str++;
while (*str) {
if (*str == '0') {
val <<= 1;
} else if (*str == '1') {
val = (val << 1) + 1;
} else {
goto binaryError;
}
}
}
return val;
binaryError:
fprintf(stderr,"Unrecognized numeric value: %s\n",str);
exit(0);
}
unsigned int parseNumber(char *str) {
unsigned int addr = 0;
if (!sscanf(str, "0x%x", &addr)) {
if (!sscanf(str, "%u", &addr)) {
addr = parseBinary(str);
}
}
return addr;
}
/*
Features that the old peekXX/pokeXX did not have:
1. Support for 8/16/32 bit READ/WRITE in one function
2. Support for decimal and binary values
3. The value return is returned (to become the status code)
*/
int main(int argc, char **argv) {
off_t addr, page;
int fd,bits,dowrite=0,doread=1;
unsigned char *start;
unsigned char *chardat, charval;
unsigned short *shortdat, shortval;
unsigned int *intdat, intval;
if (argc < 3 || argc > 5) {
fprintf(stderr,"Usage: peekpoke BIT_WIDTH ADDRESS <VALUE <x>>\n");
fprintf(stderr,"<x> can be anything; supresses read-back on write\n");
return 0;
}
sscanf(argv[1], "%d", &bits);
if (bits != 8 && bits != 16 && bits != 32) {
fprintf(stderr,"Error: BIT_WIDTH must be 8, 16, or 32\n");
return 0;
}
addr = parseNumber(argv[2]);
if (argc > 3 ) { // peekpoke BITS ADDRESS VALUE x
intval = parseNumber(argv[3]);
if (argc > 4) doread = 0;
dowrite = 1;
}
fd = open("/dev/mem", O_RDWR|O_SYNC);
if (fd == -1) {
perror("open(/dev/mem):");
return 0;
}
page = addr & 0xfffff000;
start = mmap(0, getpagesize(), PROT_READ|PROT_WRITE, MAP_SHARED, fd, page);
if (start == MAP_FAILED) {
perror("mmap:");
return 0;
}
if (bits == 8) {
charval = (unsigned char)intval;
chardat = start + (addr & 0xfff);
if (dowrite) {
*chardat = charval;
}
if (doread) {
intval = (unsigned int)*chardat;
}
} else if (bits == 16) {
shortval = (unsigned short)intval;
shortdat = (unsigned short *)(start + (addr & 0xfff));
if (dowrite) {
*shortdat = shortval;
}
if (doread) {
intval = (unsigned int)*shortdat;
}
} else { // bits == 32
intdat = (unsigned int *)(start + (addr & 0xfff));
if (dowrite) {
*intdat = intval;
}
if (doread) {
intval = *intdat;
}
}
if (doread) {
printf("0x%X\n", intval);
}
close(fd);
return intval;
}

Wyświetl plik

@ -0,0 +1,70 @@
// We can't expect that a dereference of an unsigned short * always
// produces a ldrh or strh since the compiler may choose to use
// a byte write instead. Hence, we emit the peeks and pokes using
// inline assembler. --JO
static inline unsigned short PEEK16(unsigned long addr) {
unsigned short ret;
asm volatile (
"ldrh %0, [ %1 ]\n"
: "=r" (ret)
: "r" (addr)
: "memory"
);
return ret;
}
static inline void POKE16(unsigned long addr, unsigned short dat) {
asm volatile (
"strh %1, [ %0 ]\n"
:
: "r" (addr), "r" (dat)
: "memory"
);
}
static inline unsigned long PEEK32(unsigned long addr) {
unsigned long ret;
asm volatile (
"ldr %0, [ %1 ]\n"
: "=r" (ret)
: "r" (addr)
: "memory"
);
return ret;
}
static inline void POKE32(unsigned long addr, unsigned long dat) {
asm volatile (
"str %1, [ %0 ]\n"
:
: "r" (addr), "r" (dat)
: "memory"
);
}
static inline unsigned char PEEK8(unsigned long addr) {
unsigned char ret;
asm volatile (
"ldrb %0, [ %1 ]\n"
: "=r" (ret)
: "r" (addr)
: "memory"
);
return ret;
}
static inline void POKE8(unsigned long addr, unsigned char dat) {
asm volatile (
"strb %1, [ %0 ]\n"
:
: "r" (addr), "r" (dat)
: "memory"
);
}
#define TRUE 0x01
#define FALSE 0x00

Wyświetl plik

@ -0,0 +1,451 @@
#include <unistd.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <assert.h>
#include "peekpoke.h"
#include "ep93xx_adc.h"
#define DATA_PAGE 0x12C00000
#define CALIB_LOC 2027 //location of calibration values
#define NUM_SAMPLES 5
#define NUM_CHANNELS 4
/* globals */
static unsigned long adc_page, syscon_page;
char *dr_page;
double el,az;
/*Calculate the adc value corresponding to 0V*/
//val1 is the ADC val coresponding to 0.833V
//val2 is the ADC val corresponging to 2.5V
int calcZeroVal(int val1, int val2)
{
val2 += 0x10000;
return (int)(val1-(((val2-val1)/(2.5-0.833))*0.833));
}
//return value of 1 indicates the board has no calibration values
//return value of 0 indicates the board has calibration values
int read_calibration(int buf[NUM_CHANNELS][2])
{
int i,j,k = 0;
unsigned short cal[NUM_CHANNELS*2];
// read 16 calibration bytes into buffer
FILE *f = fopen("/etc/ADC-calibration.dat","r");
if (!f) goto empty_calibration;
printf("Non-virgin board detected, evaluating stored "
"calibration values\n");
printf("Stored Calibration values [");
if (fread(cal,NUM_CHANNELS*4,1,f) == 1)
{
for(j=0;j<2;j++)
for(i=0;i<NUM_CHANNELS;i++)
{
printf("0x%x", cal[k]);
buf[i][j] = cal[k];
k++;
if(k < NUM_CHANNELS*2)
printf(", ");
}
printf("]\n");
return 1;
}
empty_calibration:
printf("/etc/ADC-calibration.dat not found or it's not readable\n");
return 0;
}
void write_calibration(int cal[NUM_CHANNELS][2])
{
unsigned short buf[16];
int i,j,k=0;
FILE *f = fopen("/etc/ADC-calibration.dat","w");
//Convert 32 bit vals to 16 bit vals
for(j=0;j<2;j++)
for(i=0;i<NUM_CHANNELS;i++)
{
buf[k] = (unsigned short)cal[i][j];
k++;
}
if (!f) goto unwrite_calibration;
if (fwrite(buf,NUM_CHANNELS*4,1,f) == 1) return;
unwrite_calibration:
printf("Problem writing /etc/ADC-calibration.dat: %m\n");
}
static void erase_calibration()
{
printf("Erasing calibration values...\n");
unlink("/etc/ADC-calibration.dat");
}
int check_calibration(int cal[NUM_CHANNELS][2],
int stored_cal[NUM_CHANNELS][2], int state)
{
double pcnt_diff;
int i,j,erase_cal =0;
int failure = 0;
if(state == 0) //no calibration values
{
printf("Virgin board detected...\n");
for(j=0;j<2;j++)
{
for(i=0;i<NUM_CHANNELS;i++)
{
if(j == 0)
pcnt_diff = (((double)abs(0xa000 -
cal[i][j])) / 0xa000) * 100;
else
pcnt_diff = (((double)abs(0x3300 -
cal[i][j])) / 0x3300) * 100;
if(pcnt_diff > 10)
{
printf("Calculated calibration "
"values out of range...\n");
exit(-1);
}
}
}
write_calibration(cal);
} else //calibration values read
{
for(j=0;j<2;j++)
{
for(i=0;i<NUM_CHANNELS;i++)
{
pcnt_diff = (((double)abs(stored_cal[i][j] -
cal[i][j])) / stored_cal[i][j])
* 100;
if(pcnt_diff > 0.25)
{
if(!failure)
{
printf("Calibration values out"
"of range\n");
failure = 1;
erase_cal = 1;
}
printf("\tChannel %d: %3.3f%%\n",i
, pcnt_diff);
}
}
}
}
if(erase_cal) erase_calibration();
if(failure) return 0;
return 1;
}
void setDR(char *x,int n,int val)
{
if (n < 0 || n > 8)
return;
x[0] = (x[0] & ~(1 << n)) | (val ? (1<<n) : 0);
}
void setD(char *x,int n,int val)
{
if (n < 0 || n > 8)
return;
x[2] = (x[2] & ~(1 << n)) | (val ? (1<<n) : 0);
}
double get_volts(int val, int zero, int range)
{
if(val <= 0x7000)
val = val + 0x10000;
val = val - zero;
return ((double)val * 3.3) / range;
}
void calc_calibration(int calibration[NUM_CHANNELS][2],
int adc_result_1[NUM_CHANNELS][NUM_SAMPLES],
int adc_result_2[NUM_CHANNELS][NUM_SAMPLES])
{
int i, j;
/* zero out our calibration values */
for(i = 0; i < NUM_CHANNELS; i++)
for(j = 0; j < 2; j++)
calibration[i][j] = 0;
//convert 0.833V vals to 0V vals
for(i=0;i<NUM_CHANNELS;i++)
{
for(j=0;j<NUM_SAMPLES;j++)
{
if(i % 2)//odd channels
adc_result_1[i][j] =
calcZeroVal(adc_result_1[i][j],
adc_result_1[0][j]);
else
adc_result_2[i][j] =
calcZeroVal(adc_result_2[i][j],
adc_result_2[1][j]);
}
}
//sum the readings
for(i = 0; i < NUM_CHANNELS; i++)
{
for(j = 0; j < NUM_SAMPLES; j++ )
{
if(i % 2 == 0 )
{
//0.833 volt values
calibration[i][0] = adc_result_2[i][j]
+ calibration[i][0];
//2.5 volt values
calibration[i][1] = adc_result_1[i][j]
+ calibration[i][1];
} else
{
//0.833 volt values
calibration[i][0] = adc_result_1[i][j]
+ calibration[i][0];
//2.5 volt values
calibration[i][1] = adc_result_2[i][j]
+ calibration[i][1];
}
}
}
//printf("Calculated Calibration values [");
for(j = 0; j < 2; j++)
{
for(i = 0; i < NUM_CHANNELS; i++)
{
calibration[i][j] = (calibration[i][j] / NUM_SAMPLES);
//printf("0x%x", calibration[i][j]);
//if((i == NUM_CHANNELS-1) && (j == 1))
//printf("]\n");
//else
//printf(", ");
}
}
}
/************************************************************************
*DESCRIPTION: Read the EP93xx onboard ADC. Discard the first
*two samples then save the next NUM_SAMPLES.
***********************************************************************/
static void read_7xxx_adc(int adc_result[NUM_CHANNELS][NUM_SAMPLES])
{
int i, j, cur_ch;
for(i = 0; i < NUM_CHANNELS; i++)
{
switch(i)
{
case 0:
cur_ch = ADC_CH0;
break;
case 1:
cur_ch = ADC_CH1;
break;
case 2:
cur_ch = ADC_CH2;
break;
case 3:
cur_ch = ADC_CH3;
break;
case 4:
cur_ch = ADC_CH4;
break;
}
//discard first two samples
read_channel(adc_page, cur_ch);
read_channel(adc_page, cur_ch);
//read more samples
for(j = 0; j < NUM_SAMPLES; j++)
{
usleep(10000);
adc_result[i][j] = read_channel(adc_page, cur_ch);
}
}
}
int test_ADC(int calibration[NUM_CHANNELS][2])
{
int adc_result_1[NUM_CHANNELS][NUM_SAMPLES];
int adc_result_2[NUM_CHANNELS][NUM_SAMPLES];
int i, j, return_val;
int failure = 0;
//double voltage,el,az;
double voltage;
setD(dr_page, 0, 1); //ADC1 = ADC3 = 0.833V
setD(dr_page, 2, 1); //ADC0 = ADC2 = 2.5V
read_7xxx_adc(adc_result_1);
setD(dr_page, 0, 0); //ADC1 = ADC3 = 2.5V
setD(dr_page, 2, 1); //ADC0 = ADC2 = 0.833V
read_7xxx_adc(adc_result_2);
el = get_volts(adc_result_1[0][0], 0x9E58, 0xC350);
az = get_volts(adc_result_1[1][0], 0x9E58, 0xC350);
el = (el/3.3) * 180;
az = (az/3.3) * 360;
//printf("\t El.: %3.1f Az.: %3.1f \n", el, az);
//verify results are within range
for(i=0;i<NUM_CHANNELS;i++)
{
for(j=0;j<NUM_SAMPLES;j++)
{
//use the datasheet values
voltage = get_volts(adc_result_1[i][j], 0x9E58, 0xC350);
//printf("\tChannel %d: %3.1f \n", i, voltage);
//even channels 2.5V(+-150mV)
if(i % 2 == 0 &&
(voltage < 2.35 || voltage > 2.65))
{
if(!failure)
{
failure = 1;
//printf("EP93XX ADC out of range\n");
}
//printf("\tChannel %d: %3.3fV"
//"(expected 2.5V +- 150mV)\n", i, voltage);
//odd channels 0.833(+-50mV)
}
else if(i % 2 == 1 &&
(voltage < 0.333 || voltage > 1.333))
{
if(!failure)
{
failure = 1;
//printf( "EP93xx ADC out of range\n");
}
//printf("\tChannel %d: %3.3fV"
// "(expected 0.833V +- 50mV)\n", i, voltage);
}
//use the datasheet values
voltage = get_volts(adc_result_2[i][j], 0x9E58, 0xC350);
//odd channels 2.5V(+-150mV)
if(i % 2 == 1 &&
(voltage < 2.35 || voltage > 2.65))
{
if(!failure)
{
failure = 1;
//printf("EP93XX ADC out of range\n");
}
//printf("\tChannel %d: %3.3fV"
//"(expected 2.5V +- 150mV)\n", i, voltage);
//even channels 0.833(+-50mV)
}
else if(i % 2 == 0 &&
(voltage < 0.333 || voltage > 1.333))
{
if(!failure)
{
failure = 1;
//printf( "EP93xx ADC out of range\n");
}
///printf("\tChannel %d: %3.3fV" "(expected 0.833V +- 50mV)\n", i, voltage);
}
}
}
calc_calibration(calibration, adc_result_1, adc_result_2);
if(failure)
return_val = 0;
else return_val = 1;
return return_val;
}
int main(void)
{
int calibration[NUM_CHANNELS][2];
int stored_calibration[NUM_CHANNELS][2];
int ret_val, state;
int devmem = open("/dev/mem", O_RDWR|O_SYNC);
assert(devmem != -1);
dr_page = mmap(0, getpagesize(), PROT_READ|PROT_WRITE,
MAP_SHARED, devmem, DATA_PAGE);
assert(&dr_page != MAP_FAILED);
adc_page = (unsigned long)mmap(0, getpagesize(), PROT_READ|PROT_WRITE,
MAP_SHARED, devmem, ADC_PAGE);
assert(&adc_page != MAP_FAILED);
syscon_page = (unsigned long)mmap(0, getpagesize(), PROT_READ|PROT_WRITE
, MAP_SHARED, devmem, SYSCON_PAGE);
assert(&syscon_page != MAP_FAILED);
init_ADC(adc_page, syscon_page);
setDR(dr_page, 0, 1);
setDR(dr_page, 1, 0);
setDR(dr_page, 2, 1);
setDR(dr_page, 3, 0);
if(test_ADC(calibration))
{
printf("ADC tested ok(data sheet values)\n");
state = read_calibration(stored_calibration);
if(check_calibration(calibration, stored_calibration, state))
ret_val = 0;
else
ret_val = 1;
}
else
ret_val = 1;
printf("\t El.: %3.1f Az.: %3.1f \n", el, az);
close(devmem);
return ret_val;
}

Wyświetl plik

Wyświetl plik

@ -0,0 +1,438 @@
#include <unistd.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <assert.h>
#include "peekpoke.h"
#include "ep93xx_adc.h"
#define DATA_PAGE 0x12C00000
#define CALIB_LOC 2027 //location of calibration values
#define NUM_SAMPLES 5
#define NUM_CHANNELS 4
/* globals */
static unsigned long adc_page, syscon_page;
char *dr_page;
/*Calculate the adc value corresponding to 0V*/
//val1 is the ADC val coresponding to 0.833V
//val2 is the ADC val corresponging to 2.5V
int calcZeroVal(int val1, int val2)
{
val2 += 0x10000;
return (int)(val1-(((val2-val1)/(2.5-0.833))*0.833));
}
//return value of 1 indicates the board has no calibration values
//return value of 0 indicates the board has calibration values
int read_calibration(int buf[NUM_CHANNELS][2])
{
int i,j,k = 0;
unsigned short cal[NUM_CHANNELS*2];
// read 16 calibration bytes into buffer
FILE *f = fopen("/etc/ADC-calibration.dat","r");
if (!f) goto empty_calibration;
printf("Non-virgin board detected, evaluating stored "
"calibration values\n");
printf("Stored Calibration values [");
if (fread(cal,NUM_CHANNELS*4,1,f) == 1)
{
for(j=0;j<2;j++)
for(i=0;i<NUM_CHANNELS;i++)
{
printf("0x%x", cal[k]);
buf[i][j] = cal[k];
k++;
if(k < NUM_CHANNELS*2)
printf(", ");
}
printf("]\n");
return 1;
}
empty_calibration:
printf("/etc/ADC-calibration.dat not found or it's not readable\n");
return 0;
}
void write_calibration(int cal[NUM_CHANNELS][2])
{
unsigned short buf[16];
int i,j,k=0;
FILE *f = fopen("/etc/ADC-calibration.dat","w");
//Convert 32 bit vals to 16 bit vals
for(j=0;j<2;j++)
for(i=0;i<NUM_CHANNELS;i++)
{
buf[k] = (unsigned short)cal[i][j];
k++;
}
if (!f) goto unwrite_calibration;
if (fwrite(buf,NUM_CHANNELS*4,1,f) == 1) return;
unwrite_calibration:
printf("Problem writing /etc/ADC-calibration.dat: %m\n");
}
static void erase_calibration()
{
printf("Erasing calibration values...\n");
unlink("/etc/ADC-calibration.dat");
}
int check_calibration(int cal[NUM_CHANNELS][2],
int stored_cal[NUM_CHANNELS][2], int state)
{
double pcnt_diff;
int i,j,erase_cal =0;
int failure = 0;
if(state == 0) //no calibration values
{
printf("Virgin board detected...\n");
for(j=0;j<2;j++)
{
for(i=0;i<NUM_CHANNELS;i++)
{
if(j == 0)
pcnt_diff = (((double)abs(0xa000 -
cal[i][j])) / 0xa000) * 100;
else
pcnt_diff = (((double)abs(0x3300 -
cal[i][j])) / 0x3300) * 100;
if(pcnt_diff > 10)
{
printf("Calculated calibration "
"values out of range...\n");
exit(-1);
}
}
}
write_calibration(cal);
} else //calibration values read
{
for(j=0;j<2;j++)
{
for(i=0;i<NUM_CHANNELS;i++)
{
pcnt_diff = (((double)abs(stored_cal[i][j] -
cal[i][j])) / stored_cal[i][j])
* 100;
if(pcnt_diff > 0.25)
{
if(!failure)
{
printf("Calibration values out"
"of range\n");
failure = 1;
erase_cal = 1;
}
printf("\tChannel %d: %3.3f%%\n",i
, pcnt_diff);
}
}
}
}
if(erase_cal) erase_calibration();
if(failure) return 0;
return 1;
}
void setDR(char *x,int n,int val)
{
if (n < 0 || n > 8)
return;
x[0] = (x[0] & ~(1 << n)) | (val ? (1<<n) : 0);
}
void setD(char *x,int n,int val)
{
if (n < 0 || n > 8)
return;
x[2] = (x[2] & ~(1 << n)) | (val ? (1<<n) : 0);
}
double get_volts(int val, int zero, int range)
{
if(val <= 0x7000)
val = val + 0x10000;
val = val - zero;
return ((double)val * 3.3) / range;
}
void calc_calibration(int calibration[NUM_CHANNELS][2],
int adc_result_1[NUM_CHANNELS][NUM_SAMPLES],
int adc_result_2[NUM_CHANNELS][NUM_SAMPLES])
{
int i, j;
/* zero out our calibration values */
for(i = 0; i < NUM_CHANNELS; i++)
for(j = 0; j < 2; j++)
calibration[i][j] = 0;
//convert 0.833V vals to 0V vals
for(i=0;i<NUM_CHANNELS;i++)
{
for(j=0;j<NUM_SAMPLES;j++)
{
if(i % 2)//odd channels
adc_result_1[i][j] =
calcZeroVal(adc_result_1[i][j],
adc_result_1[0][j]);
else
adc_result_2[i][j] =
calcZeroVal(adc_result_2[i][j],
adc_result_2[1][j]);
}
}
//sum the readings
for(i = 0; i < NUM_CHANNELS; i++)
{
for(j = 0; j < NUM_SAMPLES; j++ )
{
if(i % 2 == 0 )
{
//0.833 volt values
calibration[i][0] = adc_result_2[i][j]
+ calibration[i][0];
//2.5 volt values
calibration[i][1] = adc_result_1[i][j]
+ calibration[i][1];
} else
{
//0.833 volt values
calibration[i][0] = adc_result_1[i][j]
+ calibration[i][0];
//2.5 volt values
calibration[i][1] = adc_result_2[i][j]
+ calibration[i][1];
}
}
}
printf("Calculated Calibration values [");
for(j = 0; j < 2; j++)
{
for(i = 0; i < NUM_CHANNELS; i++)
{
calibration[i][j] = (calibration[i][j] / NUM_SAMPLES);
printf("0x%x", calibration[i][j]);
if((i == NUM_CHANNELS-1) && (j == 1))
printf("]\n");
else
printf(", ");
}
}
}
/************************************************************************
*DESCRIPTION: Read the EP93xx onboard ADC. Discard the first
*two samples then save the next NUM_SAMPLES.
***********************************************************************/
static void read_7xxx_adc(int adc_result[NUM_CHANNELS][NUM_SAMPLES])
{
int i, j, cur_ch;
for(i = 0; i < NUM_CHANNELS; i++)
{
switch(i)
{
case 0:
cur_ch = ADC_CH0;
break;
case 1:
cur_ch = ADC_CH1;
break;
case 2:
cur_ch = ADC_CH2;
break;
case 3:
cur_ch = ADC_CH3;
break;
case 4:
cur_ch = ADC_CH4;
break;
}
//discard first two samples
read_channel(adc_page, cur_ch);
read_channel(adc_page, cur_ch);
//read more samples
for(j = 0; j < NUM_SAMPLES; j++)
{
usleep(10000);
adc_result[i][j] = read_channel(adc_page, cur_ch);
}
}
}
int test_ADC(int calibration[NUM_CHANNELS][2])
{
int adc_result_1[NUM_CHANNELS][NUM_SAMPLES];
int adc_result_2[NUM_CHANNELS][NUM_SAMPLES];
int i, j, return_val;
int failure = 0;
double voltage;
setD(dr_page, 0, 1); //ADC1 = ADC3 = 0.833V
setD(dr_page, 2, 1); //ADC0 = ADC2 = 2.5V
read_7xxx_adc(adc_result_1);
setD(dr_page, 0, 0); //ADC1 = ADC3 = 2.5V
setD(dr_page, 2, 1); //ADC0 = ADC2 = 0.833V
read_7xxx_adc(adc_result_2);
//verify results are within range
for(i=0;i<NUM_CHANNELS;i++)
{
for(j=0;j<NUM_SAMPLES;j++)
{
//use the datasheet values
voltage = get_volts(adc_result_1[i][j], 0x9E58, 0xC350);
//even channels 2.5V(+-150mV)
if(i % 2 == 0 &&
(voltage < 2.35 || voltage > 2.65))
{
if(!failure)
{
failure = 1;
printf("EP93XX ADC out of range\n");
}
printf("\tChannel %d: %3.3fV"
"(expected 2.5V +- 150mV)\n", i, voltage);
//odd channels 0.833(+-50mV)
}
else if(i % 2 == 1 &&
(voltage < 0.333 || voltage > 1.333))
{
if(!failure)
{
failure = 1;
printf( "EP93xx ADC out of range\n");
}
printf("\tChannel %d: %3.3fV"
"(expected 0.833V +- 50mV)\n", i, voltage);
}
//use the datasheet values
voltage = get_volts(adc_result_2[i][j], 0x9E58, 0xC350);
//odd channels 2.5V(+-150mV)
if(i % 2 == 1 &&
(voltage < 2.35 || voltage > 2.65))
{
if(!failure)
{
failure = 1;
printf("EP93XX ADC out of range\n");
}
printf("\tChannel %d: %3.3fV"
"(expected 2.5V +- 150mV)\n", i, voltage);
//even channels 0.833(+-50mV)
}
else if(i % 2 == 0 &&
(voltage < 0.333 || voltage > 1.333))
{
if(!failure)
{
failure = 1;
printf( "EP93xx ADC out of range\n");
}
printf("\tChannel %d: %3.3fV"
"(expected 0.833V +- 50mV)\n", i, voltage);
}
}
}
calc_calibration(calibration, adc_result_1, adc_result_2);
if(failure)
return_val = 0;
else return_val = 1;
return return_val;
}
int main(void)
{
int calibration[NUM_CHANNELS][2];
int stored_calibration[NUM_CHANNELS][2];
int ret_val, state;
int devmem = open("/dev/mem", O_RDWR|O_SYNC);
assert(devmem != -1);
dr_page = mmap(0, getpagesize(), PROT_READ|PROT_WRITE,
MAP_SHARED, devmem, DATA_PAGE);
assert(&dr_page != MAP_FAILED);
adc_page = (unsigned long)mmap(0, getpagesize(), PROT_READ|PROT_WRITE,
MAP_SHARED, devmem, ADC_PAGE);
assert(&adc_page != MAP_FAILED);
syscon_page = (unsigned long)mmap(0, getpagesize(), PROT_READ|PROT_WRITE
, MAP_SHARED, devmem, SYSCON_PAGE);
assert(&syscon_page != MAP_FAILED);
init_ADC(adc_page, syscon_page);
setDR(dr_page, 0, 1);
setDR(dr_page, 1, 0);
setDR(dr_page, 2, 1);
setDR(dr_page, 3, 0);
if(test_ADC(calibration))
{
printf("ADC tested ok(data sheet values)\n");
state = read_calibration(stored_calibration);
if(check_calibration(calibration, stored_calibration, state))
ret_val = 0;
else
ret_val = 1;
}
else
ret_val = 1;
close(devmem);
return ret_val;
}

290
ts7400/ts7400.c 100644
Wyświetl plik

@ -0,0 +1,290 @@
/*
* Hamlib ts7400 backend - main file
* Copyright (c) 2001-2009 by Stephane Fillod
*
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library General Public License as
* published by the Free Software Foundation; either version 2 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 Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdlib.h>
#include <string.h> /* String function definitions */
#include <unistd.h> /* UNIX standard function definitions */
#include <math.h>
#include <sys/time.h>
#include <time.h>
#include <hamlib/rotator.h>
#include "serial.h"
#include "misc.h"
#include "register.h"
#include "ts7400.h"
struct ts7400_rot_priv_data {
azimuth_t az;
elevation_t el;
struct timeval tv; /* time last az/el update */
azimuth_t target_az;
elevation_t target_el;
};
static int ts7400_rot_init(ROT *rot)
{
struct ts7400_rot_priv_data *priv;
priv = (struct ts7400_rot_priv_data*)
malloc(sizeof(struct ts7400_rot_priv_data));
if (!priv)
return -RIG_ENOMEM;
rot->state.priv = (void*)priv;
rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__);
rot->state.rotport.type.rig = RIG_PORT_NONE;
priv->az = priv->el = 0;
priv->target_az = priv->target_el = 0;
return RIG_OK;
}
static int ts7400_rot_cleanup(ROT *rot)
{
rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__);
if (rot->state.priv)
free(rot->state.priv);
rot->state.priv = NULL;
return RIG_OK;
}
static int ts7400_rot_open(ROT *rot)
{
rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__);
return RIG_OK;
}
static int ts7400_rot_close(ROT *rot)
{
rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__);
return RIG_OK;
}
static int ts7400_rot_set_position(ROT *rot, azimuth_t az, elevation_t el)
{
struct ts7400_rot_priv_data *priv = (struct ts7400_rot_priv_data *)rot->state.priv;
rig_debug(RIG_DEBUG_VERBOSE,"%s called: %.2f %.2f\n", __func__,
az, el);
priv->target_az = az;
priv->target_el = el;
gettimeofday(&priv->tv, NULL);
return RIG_OK;
}
/*
* Get position of rotor, simulating slow rotation
*/
static int ts7400_rot_get_position(ROT *rot, azimuth_t *az, elevation_t *el)
{
struct ts7400_rot_priv_data *priv = (struct ts7400_rot_priv_data *)rot->state.priv;
struct timeval tv;
unsigned elapsed; /* ms */
rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__);
if (priv->az == priv->target_az &&
priv->el == priv->target_el)
{
*az = priv->az;
*el = priv->el;
return RIG_OK;
}
gettimeofday(&tv, NULL);
elapsed = (tv.tv_sec - priv->tv.tv_sec) * 1000 +
(tv.tv_usec - priv->tv.tv_usec) / 1000;
/*
* Simulate rotation speed of 360 deg per minute
*/
#define DEG_PER_MS (360./60/1000)
if (fabs(priv->target_az - priv->az)/DEG_PER_MS <= elapsed)
{
/* target reached */
priv->az = priv->target_az;
}
else
{
if (priv->az < priv->target_az)
priv->az += (azimuth_t)elapsed*DEG_PER_MS;
else
priv->az -= (azimuth_t)elapsed*DEG_PER_MS;
}
if (fabs(priv->target_el - priv->el)/DEG_PER_MS <= elapsed)
{
/* target reached */
priv->el = priv->target_el;
}
else
{
if (priv->el < priv->target_el)
priv->el += (elevation_t)elapsed*DEG_PER_MS;
else
priv->el -= (elevation_t)elapsed*DEG_PER_MS;
}
*az = priv->az;
*el = priv->el;
priv->tv = tv;
return RIG_OK;
}
static int ts7400_rot_stop(ROT *rot)
{
struct ts7400_rot_priv_data *priv = (struct ts7400_rot_priv_data *)rot->state.priv;
azimuth_t az;
elevation_t el;
rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__);
ts7400_rot_get_position(rot, &az, &el);
priv->target_az = priv->az = az;
priv->target_el = priv->el = el;
return RIG_OK;
}
static int ts7400_rot_park(ROT *rot)
{
rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__);
/* Assume home is 0,0 */
ts7400_rot_set_position(rot, 0, 0);
return RIG_OK;
}
static int ts7400_rot_reset(ROT *rot, rot_reset_t reset)
{
rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__);
return RIG_OK;
}
static int ts7400_rot_move(ROT *rot, int direction, int speed)
{
struct ts7400_rot_priv_data *priv = (struct ts7400_rot_priv_data *)rot->state.priv;
rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__);
rig_debug(RIG_DEBUG_TRACE, "%s: Direction = %d, Speed = %d\n", __func__, direction, speed);
switch(direction) {
case ROT_MOVE_UP:
return ts7400_rot_set_position(rot, priv->target_az, 90);
case ROT_MOVE_DOWN:
return ts7400_rot_set_position(rot, priv->target_az, 0);
case ROT_MOVE_CCW:
return ts7400_rot_set_position(rot, -180, priv->target_el);
case ROT_MOVE_CW:
return ts7400_rot_set_position(rot, 180, priv->target_el);
default:
return -RIG_EINVAL;
}
return RIG_OK;
}
static const char *ts7400_rot_get_info(ROT *rot)
{
rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__);
return "ts7400 rotator";
}
/*
* ts7400 rotator capabilities.
*/
const struct rot_caps ts7400_rot_caps = {
.rot_model = ROT_MODEL_TS7400,
.model_name = "ts7400",
.mfg_name = "LA7LKA",
.version = "0.1",
.copyright = "LGPL",
.status = RIG_STATUS_BETA,
.rot_type = ROT_TYPE_AZEL,
.port_type = RIG_PORT_NONE,
.min_az = -180.,
.max_az = 180.,
.min_el = 0.,
.max_el = 90.,
.priv = NULL, /* priv */
.rot_init = ts7400_rot_init,
.rot_cleanup = ts7400_rot_cleanup,
.rot_open = ts7400_rot_open,
.rot_close = ts7400_rot_close,
.set_position = ts7400_rot_set_position,
.get_position = ts7400_rot_get_position,
.park = ts7400_rot_park,
.stop = ts7400_rot_stop,
.reset = ts7400_rot_reset,
.move = ts7400_rot_move,
.get_info = ts7400_rot_get_info,
};
DECLARE_INITROT_BACKEND(ts7400)
{
rig_debug(RIG_DEBUG_VERBOSE, "ts7400: _init called\n");
rot_register(&ts7400_rot_caps);
return RIG_OK;
}

29
ts7400/ts7400.h 100644
Wyświetl plik

@ -0,0 +1,29 @@
/*
* Hamlib Dummy backend - main header
* Copyright (c) 2001-2008 by Stephane Fillod
*
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library General Public License as
* published by the Free Software Foundation; either version 2 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 Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
#ifndef _ROT_ts7400_H
#define _ROT_ts7400_H 1
extern const struct rot_caps ts7400_rot_caps;
extern const struct rot_caps netrotctl_caps;
#endif /* _ROT_DUMMY_H */