public inbox for ecos-discuss@sourceware.org
 help / color / mirror / Atom feed
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

  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).