From: Robert Brusa <bob.brusa@gmail.com>
To: "Steven Clugston" <steven.clugston@newcastle.ac.uk>
Cc: MailingList:ecos-discuss <ecos-discuss@ecos.sourceware.org>;
Subject: Re: [ECOS] ARM7 ADC drivers - any progress?
Date: Mon, 18 May 2009 14:16:00 -0000 [thread overview]
Message-ID: <op.ut4pxzbokeg3uf@localhost> (raw)
In-Reply-To: <4DCF6DBD3535F742BB167C528BBEE9803824B9D6A5@EXSAN01.campus.ncl.ac.uk>
[-- Attachment #1: Type: text/plain, Size: 1332 bytes --]
On Mon, 18 May 2009 15:50:32 +0200, Steven Clugston
<steven.clugston@newcastle.ac.uk> wrote:
> Hi
>
> I want to use an ARM7 board running eCos for a battery charge monitor /
> UPS type application for which I need ADC support.
>
> I need a small and cheap standalone board for this and I have identified
> Olimex SAM7-MT-256 (AT91SAM7S256) and Olimex LPC-MT-2138 as possible
> candidates.
> Reading back through the archives there has been recent discussion about
> an AT91 ADC driver and an LPC2xxx ADC driver modified from the LPC24xx
> driver:
>
> AT91:
> http://sourceware.org/ml/ecos-discuss/2008-10/msg00018.html
>
> LPC2xxx:
> http://sourceware.org/ml/ecos-discuss/2008-12/msg00176.html
>
> Has there been any more progress with these?
>
> Are they likely to be contributed or made available anytime soon?
>
> Thanks,
>
> Steven
>
> --
> Before posting, please read the FAQ: http://ecos.sourceware.org/fom/ecos
> and search the list archive: http://ecos.sourceware.org/ml/ecos-discuss
>
Hi Steven
I am not familiar with the processors you mention, but I have written a
module
that uses the adc of the AT91SAM7X. Its not ready to go into ecos (as part
of ecos), but it integrates smoothly into an ecos-based application - and
it works. It may (or may not :-( ) be helpfull for you.
Best regards
Robert
[-- Attachment #2: sun.c --]
[-- Type: application/octet-stream, Size: 12274 bytes --]
/*==============================================================================
System : INTRA S/W Copyright 2008..
================================================================
BRUSAG, Sensorik & messtechn. ph: +41 44 926 74 74
Entwicklungen, fx: +41 44 926 73 34
Chapfwiesenstr. 14 em: rbrusa@brusag.ch
CH-8712 Staefa (Switzerland) http://www.brusag.ch
================================================================================
SWSystem: eCOS and toolchain arm-elf-gcc (GCC) 3.2.1 (eCosCentric)
Target: INTRA Controller with AT91SAM7X-256
Abstract: Sunsensor and low level handling of ADC of the AT91SAM7X.
channel assignments are:
0: UPWR VDC DC power (24 VDCnom) from a voltage divider
1: UTEMP °C KTY13-6-based temperature sensor of board temperature
2: UCUR0 mA current from base-shunt of motor0-driver
3: UCUR1 mA current from base-shunt of motor1-driver
4: q0 V Signal from sun-sensor quadrant 0
5: q1 V Signal from sun-sensor quadrant 1
4: q2 V Signal from sun-sensor quadrant 2
7: q3 V Signal from sun-sensor quadrant 3
This ADC-handler uses timer-counter 2 to trigger periodic sampling of all its 8
channels. Data are stored in ram by dma-transfer. The interrupt routine and the
associeted DSR implement higher level funktionality.
Except for ch2 and 3, all all signals are slowly varying. ch2 and 3,
are filtered with a 10k/100n=1 ms-RC-filter. Let's go for a 1 kHz sampling
of all channels. We then take mean values sampled during 20 ms (50 Hz lines)
At places with a 60 Hz line frequncy, the sampling is set for 5/6 ms.
Irrespective of the lf, a full period consists of 20 readings.. We use TC2
to produce periodic trigger events for the ADCs DMA.
The ADC is read in an interrupt driven loop. We calculate means of all 8
channels and these 8 readings are available by suitable function/routine
calls.
--------------------------------------------------------------------------------
Edit history:
20-Oct-08 RWB: Creation
*/
#include <cyg/hal/drv_api.h>
#include <cyg/infra/cyg_type.h> // basic types for AT91-hardware
#include <cyg/hal/hal_io.h> // registers and fields of AT91-hardware
#include "ihw.h"
#include "sun.h"
#include "EEProm.h"
#define DMACNT 8 // size of dma-buffer
#define SAMPLES (20 * 5) // number of adc-samples - a full periode
// of the line frequency corresponds to 20 samples and one should always go
// for an integer multiple of this period.
#define VREF 3.3 // just for now - later from eeprom?
#define ADRES VREF/1023.0 // ad resolution in volts
typedef struct adc_t {
cyg_uint16 adcbuf[DMACNT]; // data-buffer for dma - actually number of channels
cyg_uint32 ig,ip; // get and put pointer for sumadc
cyg_uint32 sumadc[2][8]; // data for adc-isr - 2 ring-buffers
cyg_uint32 scancnt[2]; // number of scans in cor buffer
cyg_uint32 sumsvd[8]; // saved sum of SAMPLES readings
cyg_uint32 svdcnt; // counter of summed values
cyg_uint32 scnt; // increments with each update of sumsvd/phys
float volt[8]; // will get volts computed from sumadc
} adc_t;
// module-variables ============================================================
adc_t adcdata; // buffers for DMA and calculations
static cyg_interrupt adc_int; // memory for interupt handler
static cyg_handle_t adc_handle; // handle for interrupt handler
#if SUN_TST & 1
#include <cyg/io/io.h>
static cyg_io_handle_t myio1;
static cyg_uint32 showcnt = 0;
#endif
// internal functions ==========================================================
void SetupADCdata( void )
{
cyg_uint32 j, ch;
for (j = 0; j < 2; j++) {
adcdata.scancnt[j] = 0; // isr will reset sums if scancnt is 0
}
adcdata.svdcnt = 0;
for (ch = 0; ch < 8; ch++) {
adcdata.sumsvd[ch] = 0;
adcdata.volt[ch] = 0.0;
}
adcdata.svdcnt = 0; adcdata.scnt = 0;
adcdata.ip = 0; adcdata.ig = 0; // buffers empty
} // end SetupADCdata
// interrupt service routines ==================================================
cyg_uint32 adc_isr(cyg_vector_t vector, cyg_addrword_t data)
{
cyg_uint32 ch, result, sr;
HAL_READ_UINT32(AT91_ADC + AT91_ADC_SR, sr); //get status, but required?
if ( sr != 0xC0000){
HAL_WRITE_UINT32(AT91_ADC + AT91_ADC_RPR, (cyg_uint32)&adcdata.adcbuf);
HAL_WRITE_UINT32(AT91_ADC + AT91_ADC_RCR, DMACNT);
cyg_drv_interrupt_acknowledge(vector );
return CYG_ISR_HANDLED; // overrun or other bad data
}
if (adcdata.scancnt[adcdata.ip]) {
for (ch = 0; ch < 8; ch++) // sum up readings stored in adcbuf
adcdata.sumadc[adcdata.ip][ch] += adcdata.adcbuf[ch];
}
else { // reset sums to first value
for (ch = 0; ch < 8; ch++) // sum up readings stored in adcbuf
adcdata.sumadc[adcdata.ip][ch] = adcdata.adcbuf[ch];
}
adcdata.scancnt[adcdata.ip]++; // count number of scans
if (adcdata.scancnt[adcdata.ip] >= SAMPLES) { // need to switch sum-buffers?
adcdata.ip = (adcdata.ip + 1) & 1; // Yes, buffer 0 or 1
if (adcdata.ip == adcdata.ig) // bufferoverflow?
adcdata.ig = (adcdata.ig + 1) & 1; // drop outdated values
adcdata.scancnt[adcdata.ip] = 0; // will force a reset of sums
result = (CYG_ISR_CALL_DSR | CYG_ISR_HANDLED);
} else result = CYG_ISR_HANDLED;
// update dma address and counter
HAL_WRITE_UINT32(AT91_ADC + AT91_ADC_RPR, (cyg_uint32)&adcdata.adcbuf);
HAL_WRITE_UINT32(AT91_ADC + AT91_ADC_RCR, DMACNT);
cyg_drv_interrupt_acknowledge(vector );
return result;
} // end adc_isr
void adc_dsr(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data)
{
cyg_uint32 ch;
float nrez;
cyg_drv_interrupt_mask(vector); // critical section ahead
adcdata.svdcnt = adcdata.scancnt[adcdata.ig];
for (ch = 0; ch < 8; ch++)
adcdata.sumsvd[ch] = adcdata.sumadc[adcdata.ig][ch];
adcdata.ig = ( adcdata.ig + 1) & 1; // switch to next buffer
cyg_drv_interrupt_unmask(vector); // no longer critical
nrez = ADRES / adcdata.svdcnt;
for (ch = 0; ch < 8; ch++) // compute
adcdata.volt[ch] = nrez * adcdata.sumsvd[ch]; // converts to volt
adcdata.scnt++; // new reading available
} // end adc_dsr
// exported functions ==========================================================
cyg_uint32 GetADC( cyg_uint32 ch)
{
cyg_uint32 result;
// returns last available mean of ADC-readings from channel ch = [0..7]
// ch == 8 returns adcdata.svdcnt, ch > 8 return sample-counter
cyg_drv_dsr_lock();
if (ch < 8) result = adcdata.sumsvd[ch];
else if (ch == 8 ) result = adcdata.svdcnt;
else result = adcdata.scnt;
cyg_drv_dsr_unlock();
return result;
}
float GetVolt(cyg_uint32 ch)
// returns mean of adc-reading converted to volts. ch must be in the range 0..7
// else a negative number (-1.0) is returned.
{
float result;
cyg_drv_dsr_lock();
if (ch < 8) result = adcdata.volt[ch];
else result = -1.0;
cyg_drv_dsr_unlock();
return result;
} // end GetVolt
float GetSig(cyg_uint32 ch)
{
// return physical parameter attached to ch=[0..7] in suitable units. The
// evaluation uses the currently available mean value of the cor. channel.
// Units - see above - just volts for now
static const float c0 = 18.6; // is heuristic - (R30 + R69) / R69
static const float c1[2] = {-99.42, 196.63 / VREF}; // from old fw
float result;
cyg_drv_dsr_lock();
switch (ch) {
case 0: result = adcdata.volt[ch] * c0; break; // Vpower
case 1: result = adcdata.volt[ch] * c1[1] + c1[0]; break; // Temperature
case 2: result = 666.67 * adcdata.volt[ch]; break; // I0 in mA
case 3: result = 666.67 * adcdata.volt[ch]; break; // I1 in mA
case 4:; case 5:; case 6:;
case 7: result = VREF - adcdata.volt[ch]; break; // sun-sensor
default:
result = -1.0;
} // switch ch
cyg_drv_dsr_unlock();
return result;
} // end GetSig
void GetSun(float *q)
{
// returns voltages of the 4 quadrants as computed from last available means.
int j, k;
cyg_interrupt_disable();
for ( j = 0; j < 4; j++) {
q[j] = VREF - adcdata.volt[j+4];
if (q[j] > 0.2)
k = 0;
} // end for j
cyg_interrupt_enable();
}
#define MINSUN 0.5 // minimum level of sun
/* deviations of primary and secondary axis in app. encoder-counts
* returns true if enough sun - sq is the mean of the 4 quadrants
*/
bool Read4Angle(double *p, double *s, double *qm)
{
int j;
float q[4];
double sum;
GetSun(q);
sum = q[0];
for (j = 1; j < 4; j++) sum += q[j];
*qm = sum / 4.0;
if (sum > MINSUN) {
*p = ((q[0] + q[1]) - (q[2] + q[3])) / sum;
*s = ((q[0] + q[3]) - (q[1] + q[2])) / sum;
*p = (*p * SUN2RAD - eeram.sofs[0]) * RAD2EC;
*s = (*s * SUN2RAD - eeram.sofs[1]) * RAD2EC;
/* p is negative, if sun leads
* s is negative, if sun leads (sun higher than pointing)
*/
return true;
} // if sum
else {
*p = 0; *s = 0;
return false;
} // end if sum else
}
void IniSun( cyg_uint32 linefreq ) // 50 or 60 allowed
{ cyg_uint32 reg;
Iniihw();
SetupADCdata();
// use TC2 to kick sampling at a rate of 1000 readings p. s. for each channel
// enable peripheral clocks for TC2
HAL_WRITE_UINT32(AT91_PMC + AT91_PMC_PCER,
AT91_PMC_PCER_TC2 | AT91_PMC_PCER_ADC );
HAL_WRITE_UINT32(AT91C_BASE_TC2 + AT91_TC_BMR, 0); // do not care about XCn
//configure TC2 to produce a pulse on TIOA2 every ms
// sw-trigger and RC-match set TIOA, RA-match clears it.
HAL_WRITE_UINT32(AT91C_BASE_TC2 + AT91_TC_CMR, AT91_TC_CMR_CLKS_MCK8 |
AT91_TC_CMR_CPCTRG | AT91_TC_CMR_WAVE | AT91_TC_CMR_ACPA_CLEAR |
AT91_TC_CMR_ACPC_SET | AT91_TC_CMR_ASWTRG_SET);
//set RC for period and RA for pulse length
if (linefreq == 60) linefreq = 5000; // 5/6 ms for 60 Hz lf
else linefreq = 6000; // 1 ms timing with 6 MHz clk for 50 Hz lf
HAL_WRITE_UINT32(AT91C_BASE_TC2 + AT91_TC_RC, linefreq); // clk is 6 MHz
HAL_WRITE_UINT32(AT91C_BASE_TC2 + AT91_TC_RA, 3000); // app. 20% duty-cycle
// TC2 now ready to go, but configure ad first
// 5 MHz is maximum clock rate for 10-bit adc
#define MY_SHTIM (13 << 24) // sample hold+conversion is 13+11=24
#define MY_STARTUP (7 << 16) // I do not understand this parameter
#define MY_PRESCALE (23 << 8) // MCK:48 MHz ==> AD-Clk is 1 MHz
#define MY_TRGEN 1 // trigger enable
HAL_WRITE_UINT32(AT91_ADC + AT91_ADC_MR, MY_SHTIM | MY_STARTUP |
MY_PRESCALE | AT91_ADC_MR_TRGSEL_TIOA2 | MY_TRGEN); //
HAL_WRITE_UINT32(AT91_ADC + AT91_ADC_CHER, 0xff); // sample all 8 channels
HAL_WRITE_UINT32(AT91_ADC + AT91_ADC_RPR, (cyg_uint32)&adcdata.adcbuf);
HAL_WRITE_UINT32(AT91_ADC + AT91_ADC_RCR, DMACNT);
// no set up an ISR for the ADC when the buffer with the 8 readings is in
cyg_drv_interrupt_create( CYGNUM_HAL_INTERRUPT_ADC,
1, // priority
(cyg_uint32)&adcdata, // data required
&adc_isr, // service routine
&adc_dsr, // DSR required
&adc_handle,
&adc_int); // storage area for isr
cyg_drv_interrupt_attach( adc_handle);
cyg_drv_interrupt_unmask( CYGNUM_HAL_INTERRUPT_ADC );
HAL_WRITE_UINT32(AT91_ADC + AT91_ADC_IER, (1 << 19)); // RxBuff-IR
HAL_WRITE_UINT32(AT91_ADC + AT91_ADC_PTCR, 1); //enable transfer
HAL_READ_UINT32(AT91_ADC + AT91_ADC_SR, reg );
// start timer - this also sets TIOA2 and hence triggers a first reading
HAL_WRITE_UINT32(AT91C_BASE_TC2 + AT91_TC_CCR, AT91_TC_CCR_CLKEN |
AT91_TC_CCR_TRIG); // sw-trigger&clk ena
} // end IniSun
#if SUN_TST & 1
#include <stdio.h>
#include <string.h>
void Show(void )
{
char zeile[150];
cyg_uint32 ch, err, cv, cmr, sr, chsr, imr, chsr2;
if (showcnt == 0) {
err = cyg_io_lookup( "/dev/tty1", &myio1 );
sprintf(zeile,"\nsun %s at %s\n%4u", __DATE__, __TIME__, showcnt);
}
else {
HAL_READ_UINT32(AT91C_BASE_TC2 + AT91_TC_CV, cv );
HAL_READ_UINT32(AT91C_BASE_TC2 + AT91_TC_CMR, cmr );
HAL_READ_UINT32(AT91C_BASE_TC2 + AT91_TC_SR, sr );
HAL_READ_UINT32(AT91_ADC + AT91_ADC_SR, chsr );
HAL_READ_UINT32(AT91_ADC + AT91_ADC_IMR, imr );
HAL_READ_UINT32(AT91_ADC + AT91_ADC_SR, chsr2 );
sprintf(zeile,"\n%4u cv:%6d cmr:%8x sr:%8x chsr:%8x imr:%8x chsr:%8x",
showcnt, cv, cmr, sr, chsr, imr, chsr2);
}
showcnt++;
ch = strlen(zeile);
cyg_io_write(myio1, zeile, &ch);
} // end show
#endif
// EOF
[-- Attachment #3: sun.h --]
[-- Type: application/octet-stream, Size: 3512 bytes --]
#ifndef SUN_H_
#define SUN_H_
/*==============================================================================
System : INTRA S/W Copyright 2008..
================================================================
BRUSAG, Sensorik & messtechn. ph: +41 44 926 74 74
Entwicklungen, fx: +41 44 926 73 34
Chapfwiesenstr. 14 em: rbrusa@brusag.ch
CH-8712 Staefa (Switzerland) http://www.brusag.ch
================================================================================
SWSystem: eCOS and toolchain arm-elf-gcc (GCC) 3.2.1 (eCosCentric)
Target: INTRA Controller with AT91SAM7X-256
Abstract: Sunsensor and low level handling of ADC of the AT91SAM7X.
channel assignments are:
0: UPWR VDC DC power (24 VDCnom) from a voltage divider
1: UTEMP °C KTY13-6-based temperature sensor of board temperature
2: UCUR0 mA current from base-shunt of motor0-driver
3: UCUR1 mA current from base-shunt of motor1-driver
4: q0 V Signal from sun-sensor quadrant 0
5: q1 V Signal from sun-sensor quadrant 1
4: q2 V Signal from sun-sensor quadrant 2
7: q3 V Signal from sun-sensor quadrant 3
This ADC-handler uses timer-counter 2 to trigger periodic sampling of all its 8
channels. Data are stored in ram by dma-transfer. The interrupt routine and the
associeted DSR implement higher level funktionality.
Except for ch2 and 3, all all signals are slowly varying. ch2 and 3,
are filtered with a 10k/100n=1 ms-RC-filter. Let's go for a 1 kHz sampling
of all channels. We then take mean values sampled during 20 ms (50 Hz lines)
At places with a 60 Hz line frequncy, the sampling is set for 5/6 ms.
Irrespective of the lf, a full period consists of 20 readings.. We use TC2
to produce periodic trigger events for the ADCs DMA.
The ADC is read in an interrupt driven loop. We calculate means of all 8
channels and these 8 readings are available by suitable function/routine
calls.
--------------------------------------------------------------------------------
Edit history:
20-Oct-08 RWB: Creation
*/
#include <cyg/infra/cyg_type.h> // basic types for AT91-hardware
#include <cyg/hal/hal_io.h> // registers and fields of AT91-hardware
//#include "ihw.h"
//#define AT91_PMC_PCER_ADC (1 << 17) // still missing in cyg/hal/var_io.h
#define SUN_TST 0 // use bit-wize. 0 suppresses all tests
#if SUN_TST & 1
extern void Show( void );
#endif
extern cyg_uint32 GetADC( cyg_uint32 ch);
// returns last available mean of ADC-readings from channel ch = [0..7]
// ch == 8 returns adcdata.svdcnt, ch > 8 return sample-counter
extern float GetVolt(cyg_uint32 ch);
// returns mean of adc-reading converted to volts. ch must be in the range 0..7
extern float GetSig(cyg_uint32 ch);
// return physical parameter attached to ch=[0..7] in suitable units. The
// evaluation uses the currently available mean value of the cor. channel.
// Units - see above
extern void GetSun(float *q);
// returns voltages of the 4 quadrants as computed from last available means.
extern bool Read4Angle(double *p, double *s, double *qm);
/* deviations of primary and secondary axis in app. encoder-counts
* returns true if enough sun - qm is the mean of the 4 quadrants
*/
extern void IniSun( cyg_uint32 linefreq ); // 50 or 60 allowed
//initializes TC2 and ADC for 1ms sampling on all 8 channels
#endif /*SUN_H_*/
[-- Attachment #4: Type: text/plain, Size: 148 bytes --]
--
Before posting, please read the FAQ: http://ecos.sourceware.org/fom/ecos
and search the list archive: http://ecos.sourceware.org/ml/ecos-discuss
next prev parent reply other threads:[~2009-05-18 14:16 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2009-05-18 13:50 Steven Clugston
2009-05-18 14:16 ` Robert Brusa [this message]
2009-05-18 14:43 ` Steven Clugston
2009-08-20 10:23 ` Steven Clugston
2009-08-20 15:38 ` Robert Brusa
2009-09-01 8:58 ` Steven Clugston
2009-09-01 9:50 ` Andrew Lunn
2009-09-01 10:40 ` Steven Clugston
2009-09-03 14:26 ` Bob Brusa
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=op.ut4pxzbokeg3uf@localhost \
--to=bob.brusa@gmail.com \
--cc=ecos-discuss@ecos.sourceware.org \
--cc=steven.clugston@newcastle.ac.uk \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).