Tom Smith
Joined: 21 Oct 2010 Posts: 2 Location: Orlando, FL
|
dsPIC33FJ32MC304 ADC1 transfer to DMA0 |
Posted: Thu Mar 29, 2012 11:55 am |
|
|
I had trouble getting my project working with ADC conversion data stored using DMA. So that I thought that others might like to see my working code. I found the Microchip datasheet and Family Reference Manual to be of limited help. Sometimes contradictory or inconsistent. Certainly, they were confusing.
The accompanying file does compile with PCD of a recent version.
The file scans five analog inputs, AN4 through AN8 of dsPIC33FJ32MC304.
The file uses scatter/gather storage in DMA0, with one storage word per analog input. This gives a result in DMA RAM that should be about the same as I would have obtained with conversion-order storage. But I was not able to get that to work. But maybe I did something stupid. It happens.
But I did get the scatter/gather working, by persistence and guessing.
Enjoy.
Code: |
/*----------------------------------------------------------------------------
--
-- Fern Creek Electronics Inc
-- PROJECT: Servo Control for Gimbal Stabilized Tilt Axis
-----------------------------------------------------------------------------
--
File is ADC1_DMA0_4.c March 29, 2012
//-----------------------------------------------
Version 4
3-29-2012 4 Create new file by copy and rename from Version 3 of 3-29-2012
Seem to have a successful combination of code lines.
Now clean up the file: take out unused/unneeded lines.
ROM = 5%, RAM = 4% - 4%
//-----------------------------------------------
File Operation:
Timer2 controls the simple PWM output.
Timer2 interrupts at rate of 32 KHz.
Every 16th Timer2 interrupt starts a ADC1 conversion scan of inputs AN4 - AN8.
So that ADC1 conversion scans should happen at 2 KHz, or .5 mSec between scans
Do 12 bit conversions.
Do storage in scatter/gather in DMA0 storage buffer, beginning at 0x1400.
Store one word per analog input, ANx.
(Resulting storage pattern should equal conversion order storage pattern.)
Use peripheral indirect addressing.
ANx data is stored in DMA RAM base address + x. Base address = 0x1400.
Want DMA0 interrupt to occur after the 5th A-D conversion.
DMA0_isr() updates system data processing then terminates.
DMA0_isr() also updates a counter to determine when to do a 200Hz update
within the while(1) loop after every 10th DMA0_isr() execution.
----------------------------------------------------------------------
*/
#include <33FJ32MC304.h> // Has 2X UART
#device adc=16
#fuses EC, NOWDT, PR_PLL, ICSP2, NOJTAG
#use delay (clock=64000000)// 8x 8.0 MHz ext osc, by PLL
#CASE
#ZERO_RAM
#use fast_io(A)
#use fast_io(B)
#use fast_io(C)
#PIN_SELECT OC1 = PIN_B4 // uP pin 33 local PWM signal to LMD18201 DIR
#PIN_SELECT U1TX = PIN_B6 // pin 42 for reprogrammable pins of PIC33F
#PIN_SELECT U1RX = PIN_B5 // pin 41
#PIN_SELECT U2TX = PIN_B8 // pin 44 for reprogrammable pins of PIC33F
#PIN_SELECT U2RX = PIN_B7 // pin 43
#USE rs232(stream =SER_OUT, UART1, baud= 57600) // Get 58.1 kBd, J9,pin 3
//For '304 UART #1 ---------------------------------
#BIT U1STA_TRMT = 0x0222.8 // bit=0 -> tx1 register not empty
#BIT U1STA_OERR = 0x0222.1 //
#WORD U1STA = 0x0222
#BIT UT1INV = U1STA.14
#WORD U1MODE = 0x0220
#BIT UR1INV = U1MODE.4
//-----------------------------------
#WORD OC1RS = 0x0180 //
#WORD OC1R = 0x0182 //
#WORD OC1CON = 0x0184
#WORD T2CON = 0x0110
#WORD PR2 = 0x010C //
#BIT T2CON_TON = T2CON.15
//------
#WORD IFS0 = 0x0084 // Interrupt Flag Status Register 0
#WORD IFS3 = 0x008A
#BIT T2IF = 0x0084.7 // Timer 2 Interrupt Flag
#BIT AD1IF = IFS0.13
// Declarations for DMA Interrupts for access to ADC data
#WORD IEC0 = 0x0094
#BIT DMA0IE = IEC0.4
#BIT DMA0IF = IFS0.4
#WORD ADC1BUF0 = 0x0300 // only one data buffer
#word AD1CON1 = 0x0320
#word AD1CON2 = 0x0322
#word AD1CON3 = 0x0324
#WORD AD1CHS123 = 0x0326
#WORD AD1CHS0 = 0x0328
#WORD AD1PCFGL = 0x032C
#WORD AD1CSSL = 0x0330
#WORD AD1CON4 = 0x0332 // for DMABL<2:0>
#BIT AD1_ADON = AD1CON1.15
#BIT AD1_AD12B = AD1CON1.10
#BIT AD1_DONE = AD1CON1.0
#BIT AD1_SAMP = AD1CON1.1
#BIT AD1_ASAM = AD1CON1.2
#BIT AD1_CSCNA = AD1CON2.10
// Declarations for DMA CHanel 0, for getting outputs from ADC1
#WORD DMA0CON = 0x0380
#WORD DMA0REQ = 0x0382
#WORD DMA0STA = 0x0384
#WORD DMA0STB = 0x0386
#WORD DMA0PAD = 0x0388
#WORD DMA0CNT = 0x038A
#BIT DMA0_CHEN = DMA0CON.15
//#WORD DMA0_0 = 0x1400
//#WORD DMA0_1 = 0x1402
//#WORD DMA0_2 = 0x1404
//#WORD DMA0_3 = 0x1406
#WORD DMA0_4 = 0x1408 // 5th DMA0 storage position, for data from AN4 input
#WORD DMA0_5 = 0x140A
#WORD DMA0_6 = 0x140C
#WORD DMA0_7 = 0x140E
#WORD DMA0_8 = 0x1410 // 9th DMA0 storage position, for data from AN8 input
signed int16 Vtest =0;
signed int16 PS_Sample =0;
signed int16 cur_sense16 =0;
signed int16 cs_vref16 =0;
signed int16 gain_adj_pot16 =0;
signed int16 Vcontrol16 =0;
unsigned int8 dma0INTRcnt =0;
unsigned int8 stabUpdate =0;
unsigned int16 bellCount =0;
unsigned int8 LED_count = 201;
unsigned int8 t2INTcount;
// Function prototypes:
void Setup(void); // Setup the dsPIC33F
void setup_MC304_ADC(void); // Setup for dsPIC33FJ32MC304 ADC1
void setup_MC304_DMA0(void); // Setup DMA0 channel for storing data from ADC1.
//--------- TIMER1 ISR ------------
//
#int_TIMER1
void TIMER1_isr()
{
} // End of TIMER1 ISR
//---------------------------------------------------------------------
//
#int_TIMER2 FAST LEVEL=7
void TIMER2_isr() // come here at PWM freq, 32.0 KHz
//start ADC conversions at 2000 Hz, every 16th TMR2interrupt
{
if (t2INTcount++ == 15)
{
t2INTcount =0;
// Reset the DMA0 counter
DMA0IF = 0;
DMA0_CHEN = 0; // disable DMA Ch 0.
// this process resets the DMA transfer count
// to zero and sets the active DMA buffer
// to the primary buffer.
DMA0_CHEN = 1;
// DMA0IF = 1; // TEST force a run thru DMA0_isr() 3-2702012
// With this test, got a blinking LED = DMA0_isr() works
// Start the ADC1 scan and convert process
AD1IF = FALSE; // Reset the interrupt flag for ADC1
AD1CON1 = 0b1000010011100110 ; // Set to TRUE ASAM and SAMP bits, 2:1
//AD1_ADON = TRUE AD1CON1.15
//AD1_ADSIDL = FALSE AD1CON1.13
//AD1_ADDMABM = FALSE AD1CON1.12 DMA buffers are scatter/gather order
//AD1_AD12B = TRUE AD1CON1.10
// FORM <1:0> = 0b00 AD1CON1 9:8 Integer: 0000 dddd dddd dddd
// AD1 SSRC = AD1CON1<7:5> = 0b111 Auto convert
// AD1_SIMSMA = FALSE AD1CON1.3 no simultaneous sampling: do sequential
// AD1_ASAM = TRUE AD1CON1.2 Sampling auto begins after last conversion
// AD1_SAMP = TRUE AD1CON1.1 S&H amplifier is sampling
//AD1_DONE = FALSE AD1CON1.0 Clear the Done bit
} // End of if(t2INTcount...)...
} // End of Timer2 isr...
//
//--------------------------------------------------------------------------
//
#int_DMA0 Level=4
void DMA0_isr(void)
{ // Want to come here after scanning 5 ADC inputs via ADC CH0: AN4-AN8
// Stop the automatic scanning and sampling
AD1_ASAM = FALSE; // Disable automatic sampling
// begin DMA0_isr signal flag. See it at TP5.
output_high(PIN_B12); // Duration = 560 nSec, period = 500.0 microSec
// 16-bit ADC result is 0000 dddd dddd dddd. Value range is 0 -> 4096
// Copy converted data from ADC buffer locations to system variable locations
Vtest = DMA0_4; // from AN4, uProc pin 23, U3, dsPIC33FJ32MC304
PS_Sample = DMA0_5; // From AN5, uProc pin 24
cur_sense16 = DMA0_6; // from AN6, uProc pin 25
cs_vref16 = DMA0_7; // from AN7, uProc pin 26
gain_adj_pot16 = DMA0_8;// from AN8, uProc pin 27 Via J2 pin 6
Vcontrol16 = DMA0_8; // periph ind, SMPI=4, see AN8 Pot data ! 8:11 AM
// Vcontrol16 = DMA0_4; // periph ind, SMPI=4, see AN4 data, Vtest 8:12
//
// Vcontrol16 = DMA0_5; // periph ind, SMPI=4, see AN5 data, Pwr Sup samp
Vcontrol16 -= 2048; //value range is - 2048 to +2047
// For test send this value out through UART1.
// fputc is done in main() while(1) loop, within if(stabUpdate){ }
//
// Other system code lines here
//
// Update counter for determining when to do the low-freq updates in main()
if ( ++dma0INTRcnt == 10) {dma0INTRcnt =0; stabUpdate = TRUE;}
//
DMA0_CHEN = FALSE; // disable DMA Ch 0.
// this process resets the DMA transfer count
// to zero and sets the active DMA buffer
// to the primary buffer.
DMA0_CHEN = TRUE;
DMA0IF = FALSE;
// AD1IF = FALSE; // Reset the interrupt flag for ADC1
output_low(PIN_B12); // begin DMA0_isr signal flag. See it at TP5
// 3-29-2012 Duration = 560 nSec
// Period = 500.0 uSec
// DMA0IF = 0; // new here, 3-28
} // End of DMA0_isr()
//-----------------------------------------------------------------
void setup_MC304_ADC(void) // Called in Setup()
// For use with FCE CCA with dsPIC33FJ32MC304 and PWM LMD18201T
{
// Disable the ADC1
AD1_ADON = FALSE; // bit 15 in AD1CON1
// Declare which ANx inputs should be digital I/O ( '304 User Manual pg 286)
AD1PCFGL = 0b0000000000001111 ; // AN0, AN1,AN2, AN3 are digital pins( 1)
// AN4,AN5, AN6, AN7,AN8 are analog pins(0)
// Select the way conversion results are presented, in AD1CON1
AD1CON1 = 0b0000010011100000 ; //
// x ADC disable bit: ADON=0, a.k.a. AD1_ADON
// 0 bit 13 = ADSIDL =0 contine op in idle mode
// 0 bit 12 =ADDMABM: scatter/gather mode
// x bit 11 unimplemented
// 1 bit 10 = AD12B = 1, for 12-bit operation
// 00 bits 9,8 = Format = 00 = unsigned integer
// 0000 dddd dddd dddd = 0->4K
// 111 bits 7:5 SSRC =automatic conversion trigger
// 0 bit 4: unimplemented
// 0 bit 3: SIMSAM = 0 = sequential sampling
// 0 ASAM=0, sampling begins when SAMP is set
// 0 SAMP set this bit to 1 to begin sampling
// with SSRC not=000, hdw auto clears the bit
// x Done bit
// Select voltage reference in AD1CON2
AD1CON2 = 0b0000010000010000 ; //
// 000 15:13 VCFG RefVoltConfg Vrefh=AVDD, Vrefl=AVSS
// 1 10 CSCNA Scan the inputs
// 00 9:8 CHPS is Ignored for AD12B = 1 = 12 bit conv.
// 0000 5:2 SMPI Interrupts end every sample/conv. seq.
// 0100 5:2 SMPI Interrupts after 5 sample/conv. seq.
// 0 1 BUFM Start filling buffer from beginning
// 0 0 ALTS Always use MUXA input select
//
//
//
//
// Select analog conversion clock, in AD1CON3
AD1CON3 = 0b1000000000001111 ;
// 0 15 ADRC =0 = Use fosc = 64 MHz
// (Use RC osc for converting in sleep mode.)
// (nom freq of RC osc is 4 MHz, 250 nSec.)
// 00010 12:8 SAMC 2 Tad
// 00001111 7:0 ADCS 15+1 = 16 -> 1/16 -> Tad = 250 nS
// Configure DMA buffer words per analog input
AD1CON4 = 0b0000000000000000; // One buffer word per analog input.
// 000 // DMABL (UM page 281) (FRM ADC page 47)
// // 000 -> allocate one word of buffer per ANx
// // AD1CON4 is ignored for conversion-order mode.
AD1CHS0 =0b0000000000001000;
// 0 15 CH0NB= 0 = Ch0 neg input = Vref- = Vss
// 00 14:13 unimplemented
// 00000 12:8 CH0SB positive input select AN0 - AN8
// 0 7 CH0NA = 0 Ch 0 neg input is Vref-
// 00 6:5 unimplemented
// 01000 4:0 CH0SA positive input select = AN8
// 4:0 ignored for CSCNA = 1, do scanning
// AD1CH123 is not used for AD12B = 1
// AD1CSSH is not available for 'MC304
AD1CSSL =0b0000000111110000; // ADC1 Input Scan Select Register Low
// 87654 10 Scan inputs at AN8, AN7, AN6, AN5, AN4
// Turn on the AD Converter
AD1_ADON = TRUE; // Turn on the AD converter
} // End of setup_MC304_ADC();
//----------------------------------------------------------------------
void setup_MC304_DMA0(void)
{
DMA0CON = 0b0000000000100001; //
// 0 15 CHEN 1= Channel enabled, 0 =disabled
// 0 14 SIZE = word
// 0 13 DIR, from peripheral to DMA
// 0 12 HALF
// 0 11 NULLW
// 10 5:4 AMODE=10 peripheral indirect addressing
// 01 5:4 AMODE=01 Register indirect wo/ post-incr
// 00 5:4 AMODE=00 Register indirect w/ post-incr
// 01 1:2 MODE = one-shot, no ping-pong
// 00 1:2 MODE = continuous, no ping-pong
DMA0REQ = 0b0000000000001101; // IRQ comes from ADC1 convert done bit
// 0 // 15 FORCE = 0
// 0001101 // IRQSEL 6:0 DMA reads from ADC1BUF0 ( 0x0300)
// value = 0x0D,per Table 8-1 & FMR:DMA p38-6
DMA0STA = 0x0000; // Offset Start RAM at location 0x1400 DMA RAM
DMA0STB = 0x0020;
DMA0PAD = 0x0300; // address of data source from peripheral
// is address of ADC1BUF0 register
DMA0CNT = 0b0000000000000100; // DMA0 transfer count register
// DMA0CNT =4 get DMA IRQ after fifth DMA
// DMA0CNT =0 get DMA IRQ each DMA
DMA0_CHEN = TRUE; // Enable the DMA channel 0 after getting it setup.
} // End of setup_MC304_DMA()
//-----------------------------------------------------------------
void main()
{
Setup(); // Setup the PIC33F Hardware etc. See line 1112.
// Enables the interrupts
// Calls a function to set up the 12-bit ADC
// Calls a function to setuo DMA0
//-- Begin interative part of Main() --------------
while (1)
{
if(stabUpdate) // stabUpdate is set TRUE in DMA0_isr()
// Come here once every 5 mSec
{
stabUpdate = FALSE;
bellCount++; // bellCount is int16, rolls over every ... bells or loops
// Lines of system data processing code here
// Do a slow 200Hz update of system data here, per counter in DMA0_isr()
// Send test data message via UART1 and CCA J9 ------------------------
if ((bellCount+1)%2)
{
fputc( make8(Vcontrol16,1),SER_OUT); // Use to see conversion data
fputc( make8(Vcontrol16,0),SER_OUT);
}
// Heartbeat LED blinking -------------------------------------
if (LED_count++ >200) LED_count =0;
output_bit(PIN_B14,(LED_count<11));
output_bit(PIN_C5,(LED_count<11));
} // End of if(stabUpdate)...
} // End of while(1)...
} // End of main()...
//----------------------------------------------------------------------
void Setup(void) // for PIC33FJ32MC304 on FCE motor controller CCA
{
// Set up the uP inputs and outputs
output_a (0b0000010010011111); // Have A0 - A4,A7,A9,A10
set_tris_a (0b0000001000000100);
// A0 pin 19 out IMU_RST\
// A1 pin 20 out IMU_CS\
// A2 pin 30 input oscillator input
// A3 pin 31 out clkout
// A4 pin 34 out BRAKE
// A7 pin 13 out not used
// A8 pin 32 out Enable to LMD18201
// A9 pin 35 input PWM_Overtemp
// A10 pin 12 out not used
output_b (0b0000000000000000);
set_tris_b(0b1000001010101100); //Have B0 - B15
// RB0 pin 21 output uP_SDO SPI PPS
// RB1 pin 22 output uP_SCLK SPI PPS
// RB2 pin 23 AN4,analog TEST_ANALOG
// RB3 pin 24 AN5,analog PS Sample
// RB4 pin 33 RP4 output OC1 to PWM PPS
// RB5 pin 41 RP5 input UART1RX PPS
// RB6 pin 42 RP6 output UART1TX PPS
// RB7 pin 42 input NC RP7 to U8-R2OUT 10
// RB8 pin 44 output NC RP8 to U8-T2IN 9
// RB9 pin 1 input QEA
// RB10 pin 8 output PGED2
// RB11 pin 9 output PGEC2
// RB12 pin 10 RP12 output TP5 testpoint
// RB13 pin 11 RP13 output NC
// RB14 pin 14 RP14 output LED2
// RB15 pin 15 RP15 input uP_SDI SPI
// Oct.18,2011: RB15 used for input an RC pulse
output_c (0b0000000000000000);
set_tris_c(0b0000000011011111); //Have C0 - C9
// RC0 pin 25 input AN6,Analog,current sense
// RC1 pin 26 input AN7,Analog, V_REF
// RC2 pin 27 input AN8,Analog,GAIN_ADJUST
// RC3 pin 36 input LIMIT_CW_NOT
// RC4 pin 37 input LIMIT_CCW_NOT
// RC5 pin 38 output LED_ON
// RC6 pin 2 input QEB
// RC7 pin 3 input QE_INDX
// RC8 pin 4 output TP7 QE DIR
// RC9 pin 5 output NC
setup_oscillator(OSC_CRYSTAL,64000000,8000000);
// target freq = 64 MHz, source freq = 8 MHz
setup_MC304_ADC(); // an FCE-created routine for setting up the ADC
setup_MC304_DMA0();
disable_interrupts(INT_RDA);
setup_timer1(TMR_INTERNAL|TMR_DIV_BY_8);
// Fcy = Fosc/2 = 64 MHx/2 = 32MHz
// T1 clock = 32MHz/8 = 4 MHz (next possible divisor is 64)
setup_timer2(TMR_INTERNAL|TMR_DIV_BY_1, 1000) ; // reset after 1000 cycles,
// Setup the Output Compare 1 operation
// Per Microchip PIC24F FRM, Sec 16, page 16-25
OC1CON = 0x0000; // Mode, OC1 disabled
OC1R = 0x01f4; // initial duty cycle = 500
OC1RS = 0x01f4; // duty cycle = 500
OC1CON = 0x0006; // Mode for simple PWM, OC mode bits
PR2 = 0x03E8; // PWM period = 1000
T2CON_TON = TRUE; // Enable Timer2
setup_timer3(TMR_DISABLED|TMR_DIV_BY_1);
DMA0_CHEN = 0; // disable DMA Ch 0.
// this process resets the DMA transfer count
// to zero and sets the active DMA buffer
// to the primary buffer.
DMA0IF = 0; // reset the DMA0 interrupt flag.
AD1IF = FALSE; // Reset the interrupt flag for ADC1
enable_interrupts(INT_TIMER2);
// enable_interrupts(INT_ADC1);
enable_interrupts (INT_DMA0);
// enable_interrupts (INTR_NESTING);
// enable_interrupts (INTR_NORMAL);
enable_interrupts(INTR_GLOBAL);
DMA0_CHEN = TRUE;
} // End of Setup()...
//----------------------------------------------------------------------
/////////////// END //////////////////////// END ///////// |
_________________ Tom Smith |
|