News:

Let's find out together what makes a PIC Tick!

Main Menu

ADC and DMA with Pic24HJ / DSPic33

Started by Wimax, Mar 11, 2023, 10:20 PM

Previous topic - Next topic

Wimax

Hello Everybody!

Does anyone have experience in using ADC via DMA with Positron 16 ? I have tried adapting some C code found on the net, but no way to get the desired results.
I was hoping for an example in the manual but there is nothing related to declaring variables in the DMA RAM. :'(

trastikata

Hello,

here's a manual on DMA setup and use. I can write an example code next few days when I have more time if you haven't figure it out, but you need o provide more information on the usage - how many ADC channels, array or single variable in memory as destination, ping-pong or not.

Regards

DMA.pdf 

Wimax

#2
Hello!

I am trying to use a PIC24HJ128GP202 to acquire two signals on channels AN1 and AN5; ideally, I would like to be able to do this simultaneously, but alternating is also acceptable.
So far I have been using a PIC24FJ64GA002 that does not have DMA and have easily handled the transfer of blocks of 16 samples via interrupts (8 from first input and 8 from the other). The sampling frequency is 333.3 Ksps that halves using alternating sampling.
Basically, the acquisition is done automatically by enabling the ADC interrupt and handling it properly.
I would like to try to do the same thing with a PIC24HJ128GP202 by then taking advantage of the higher clock speed for processing. I would like to store the samples using arrays and even the one shot mode should be ok.

A little code in Positron16 would be really helpful to see if the settings are correct as well as how the samples are handled in DMA.
I have done several tests, the DMA interrupt seems to trigger, but I don't see any samples stored. :'(


trastikata

Here's some basic code I wrote for a test board with dsPIC33EP256MU806 I had at hand.

I tested the code and it works. For clarity I omitted some of the includes, thus the code can't be directly compiled. The register set-up is for simultaneous sampling of AN2 and AN0 and placing the results in RAM buffer using DMA.

I didn't include error traps or flag checking etc. Something with the DMA interrupt set-up is amiss and doesn't generate the interrupt when the DMA (RAM) buffer flips to the beginning of the array, but I don't have the time now to read the datasheet more in details.

Anyway the continuous simultaneous ADC sampling and direct result placement of both ADC channels in the RAM works - I checked it by reading and printing the results from the array directly. So it will give you a starting point how to handle the DMA.

Device = 33EP256MU806

Declare Xtal = 140

Dim wAdcResult[32] As Word DMA
Dim dwAddressOfRamArray As Dword
Dim wAddressOfRamArrayH As Word
Dim wAddressOfRamArrayL As Word

Dim a As Byte

Main:
    Clear

    SetADC()
    SetDMA()
   
    While 1 = 1
        For a = 0 To 31
            PrintAdc(wAdcResult[a], a)
            DelayMS 500
        Next
    Wend
   
    End
   
Proc SetDMA()
    INTCON2.15 = 1              'GIE = 1
    IFS0.4 = 0                  'DMA0 – DMA Channel 0 IF clear
    IPC1.2 = 0                  'IP = 1
    IPC1.1 = 0
    IPC1.0 = 1
    IEC0.4 = 1                  'DMA0 – DMA Channel 0 IE = 1
   
    dwAddressOfRamArray = AddressOf(wAdcResult)
    wAddressOfRamArrayH.Byte1 = dwAddressOfRamArray.Byte3
    wAddressOfRamArrayH.Byte0 = dwAddressOfRamArray.Byte2
    wAddressOfRamArrayL.Byte1 = dwAddressOfRamArray.Byte1
    wAddressOfRamArrayL.Byte0 = dwAddressOfRamArray.Byte0
   
    DMA0CON.15 = 1              'CHEN: Channel Enable bit
   
    DMA0REQ.7 = 0               'IRQSEL<7:0>: DMA Peripheral IRQ Number Select bits /  ADC1 – ADC1 convert done
    DMA0REQ.6 = 0
    DMA0REQ.5 = 0
    DMA0REQ.4 = 0
    DMA0REQ.3 = 1
    DMA0REQ.2 = 1
    DMA0REQ.1 = 0
    DMA0REQ.0 = 1
   
    DMA0STAH = wAddressOfRamArrayH  'DMA CHANNEL X START ADDRESS REGISTER A (HIGH)
    DMA0STAL = wAddressOfRamArrayL  'DMAXSTAL: DMA CHANNEL X START ADDRESS REGISTER A (LOW)
   
    DMA0PAD = 0x0300            'DMAxPAD Register (Values to Read from Peripheral) / 0x0300 (ADC1BUF0)   
   
    DMA0CNT = 31                'CNT<13:0>: The number of DMA transfers = CNT<13:0> + 1.       
EndProc

Proc SetADC()
    ANSELB.2 = 1                'AN2 set to analog
    ANSELB.0 = 1                'AN0 set to analog

    'Set ADC1
    AD1CON1.15 = 0              'ADON: ADC Operating Mode bit OFF
    AD1CON1.12 = 1              'ADDMABM: DMA buffers are written in the order of conversion; the module provides an address to the DMA
                                '              channel that is the same as the address used for the non-DMA stand-alone buffer 
    AD1CON1.10 = 0              'AD12B: 10-bit, 1-channel ADC operation
    AD1CON1.7 = 1               'SSRC<2:0>: Sample Clock Source Select bits - Internal counter ends sampling and starts conversion (auto-convert)
    AD1CON1.6 = 1
    AD1CON1.5 = 1
    AD1CON1.3 = 1               'SIMSAM: samples CH0 and  CH1 simultaneously / CHPS<1:0> = 01
    AD1CON1.2 = 1               'ASAM: Sampling begins immediately after the last conversion; SAMP bit is auto-set
   
    AD1CON2.9 = 0               'CHPS<1:0>: Channel Select bits / Converts CH0 and CH1
    AD1CON2.8 = 1
    AD1CON2.6 = 0               'SMPI<4:0>: Increment Rate bits / When ADDMAEN = 1: Increments the DMA address after completion of every sample/conversion operation
    AD1CON2.5 = 0
    AD1CON2.4 = 0
    AD1CON2.3 = 0
    AD1CON2.2 = 0
    AD1CON2.1 = 0
       
    AD1CON3.12 = 1              'SAMC<4:0>: Auto-Sample Time bits 16 TAD
    AD1CON3.11 = 0
    AD1CON3.10 = 0
    AD1CON3.9 = 0
    AD1CON3.8 = 0
    AD1CON3.7 = 0               'ADCS<7:0>: ADC Conversion Clock Select bits = 17
    AD1CON3.6 = 0
    AD1CON3.5 = 0
    AD1CON3.4 = 1
    AD1CON3.3 = 0
    AD1CON3.2 = 0
    AD1CON3.1 = 0
    AD1CON3.0 = 1
   
    AD1CON4.8 = 1               'ADDMAEN: Conversion results are stored in ADCxBUF0 register for transferring to RAM using DMA
    AD1CON4.2 = 1               'DMABL<2:0>: Selects Number of DMA Buffer Locations per Analog Input bits / Allocates 16 words of buffer to each analog input
    AD1CON4.1 = 0
    AD1CON4.0 = 0
   
    AD1CHS0.4 = 0               'CH0SA<4:0>: Channel 0 Positive Input Select for Sample A bits - set to AN2
    AD1CHS0.3 = 0
    AD1CHS0.2 = 0
    AD1CHS0.1 = 1
    AD1CHS0.0 = 0
   
    AD1CON1.15 = 1              'ADON: ADC Operating Mode bit ON
EndProc   
   
   
       

Wimax

Thank you very much ! I'm studying the code trying to adapt it to the PIC24HJ128GP202.

I see some differences, the first is this:
 DMA0STAH = wAddressOfRamArrayH  'DMA CHANNEL X START ADDRESS REGISTER A (HIGH)
DMA0STAL = wAddressOfRamArrayL  'DMAXSTAL: DMA CHANNEL X START ADDRESS REGISTER A (LOW)

The PIC24HJ128GP202 has two 16-bit registers dedicated to each DMA Channel, in this case DMA0STA and DMA0STB, where I have to write the offset of the addresses of the primary and secondary buffers in DMA memory.
wAdcResult is the only variable (array) declared in that memory area, I would expect to have a zero offset from address 0x2000 (start of DMA memory according to the datasheet). The "dwAddressOfRamArray" variable does not return zero... I wonder if the compiler allocates "wAdcResult" elsewhere in the DMA RAM ? If so, I have to calculate the actual offset to be entered in the registers DMA0STA and DMA0STB.

trastikata

QuoteThe PIC24HJ128GP202 has two 16-bit registers dedicated to each DMA Channel, in this case DMA0STA and DMA0STB

If no ping-pong buffering is required, you will need only DMA0STA

Quotein this case DMA0STA and DMA0STB, where I have to write the offset of the addresses of the primary and secondary buffers in DMA memory.

In case you want to use the ping-pong mode, then just set two buffers, for example if a buffer of 32 ADC values is required:

Dim wAdcResultA[16] As Word DMA
Dim wAdcResultB[16] As Word DMA


QuoteIf so, I have to calculate the actual offset to be entered in the registers DMA0STA and DMA0STB.

No, those registers contain the actual address of the variable in the RAM, thus you don't calculate anything, simply get the RAM address of each array buffer.

QuotewAdcResult is the only variable (array) declared in that memory area, I would expect to have a zero offset from address 0x2000 (start of DMA memory according to the datasheet). The "dwAddressOfRamArray" variable does not return zero... I wonder if the compiler allocates "wAdcResult" elsewhere in the DMA RAM ?

DMA0STA and DMA0STB contain the actual RAM address, no offsets needs to be calculated. Since those registers are 16b wide,the code is somewhat simplified because you can set those registers inline. The code in your case becomes, again if ping-pong buffering is required:

Dim wAdcResultA[16] As Word DMA
Dim wAdcResultB[16] As Word DMA
....
DMA0STA = AddressOf(wAdcResultA) 
DMA0STB = AddressOf(wAdcResultB) 

Regards

Wimax

Hello Trastikata,

Thank you very much for you help !
I tried to adapt the code for the PIC24HJ. After several trials I was able to run the converter in alternating mode on CH0 (probably it's more versatile for the inputs I need to acquire) using DMA and peripheral address generation in sequential mode using a 16-words buffer without ping-pong.
I performed static acquisitions for preliminary tests at the moment , but I will try with more complex signals as soon as possible.
In practice the PIC24HJ now operates in a very similar way to the PIC24FJ, which has 16 internal 16-bit buffer registers, while performing transfers using DMA.
I have tried enabling both continuous and one-shot ping-pong modes, but I cannot get proper sampling in either sequential or scatter/gather mode. I make no secret of the fact that the latter mode would be very convenient as I already have the samples sorted in the two buffers in the DMA RAM.

Wimax

With a little persistence and an extra bit of time, finally the ADC of the 24HJ is working properly with simultaneous sampling on two channels at over 660 Ksps in Ping-pong !

Thanks again for the tips!  ;)

diebobo

Quote from: Wimax on Mar 27, 2023, 07:42 PMWith a little persistence and an extra bit of time, finally the ADC of the 24HJ is working properly with simultaneous sampling on two channels at over 660 Ksps in Ping-pong !

Thanks again for the tips!  ;)

Got some code to show ? Always handy for others in the future 😁

Wimax

Quote from: diebobo on Mar 27, 2023, 08:19 PM
Quote from: Wimax on Mar 27, 2023, 07:42 PMWith a little persistence and an extra bit of time, finally the ADC of the 24HJ is working properly with simultaneous sampling on two channels at over 660 Ksps in Ping-pong !

Thanks again for the tips!  ;)

Got some code to show ? Always handy for others in the future 😁



I extracted the code from a larger programme, it is compiled correctly by Positron16, but I have not tested it in a stand-alone manner.

The code samples two channels (AN0 and AN1) simultaneously at about 666 Ks/s, storing 512 samples for each channel.


'****************************************************************
'*  Name    : ADC_DEMO_PIC24HJ128GP202.BAS                      *
'*  Author  : [Max]                                             *
'*  Notice  : Copyright (c) 2023 by MAX                         *
'*          : All Rights Reserved                               *
'*  Date    : 28/03/2023                                        *
'*  Version : 1.0                                               *
'*  Notes   :                                                   *
'*          :                                                   *
'****************************************************************


Device = 24HJ128GP202

Declare Xtal= 79.23

PLL_Setup(43, 2, 2, $0300)   ' Use an external XTAL @ 7.37 MHz

Config FBS = BWRP_WRPROTECT_OFF
Config FGS = GWRP_OFF
Config FOSCSEL = FNOSC_PRIPLL, IESO_OFF
Config FOSC = POSCMD_XT, IOL1WAY_OFF, FCKSM_CSECME
Config FWDT = WDTPOST_PS256, WINDIS_OFF, FWDTEN_OFF
Config FPOR = FPWRT_PWR128, ALTI2C_OFF
Config FICD = ICS_PGD1, JTAGEN_OFF

Dim wAdcResult1[16] As Word DMA     'Define first buffer of 16 Word in DMA memory
Dim wAdcResult2[16] As Word DMA     'Define second buffer of 16 Word in DMA memory
Dim dwAddressOfRamArray1 As Dword   'Will point to first buffer address
Dim dwAddressOfRamArray2 As Dword   'Will point to second buffer address
Dim XX[520] As Word  ' Array of samples of AN0
Dim YY[520] As Word  ' Array of samples of AN1
Dim b As Word ' Counter variable
Dim a As Byte ' Counter variable
Dim KK As Byte' Counter variable
Dim c As Byte ' Counter variable
Dim FLV As Bit ' Ping-pong buffer flag
Dim dma_numcampioni As Byte

' Set input ports for ADC -- AN1 & AN0
TRISA.1 = 1     
TRISA.0 = 1     
AD1PCFGLbits_PCFG1 =0     
AD1PCFGLbits_PCFG0 =0   
IFS0bits_AD1IF = 0' // Clear the A/D interrupt flag bit
IEC0bits_AD1IE = 0' // Do Not Enable A/D interrupt
dma_numcampioni=16 ' Samples to tranfer via DMA
FLV=0 ' Reset Ping-pong flag

    Isr DMA1Interrupt
   
        If FLV=0 Then      ' Select first buffer
       
               a=0
               Repeat
               b=a+KK*8
               c=a*2
               XX[b]=wAdcResult1[c]
               YY[b]=wAdcResult1[c+1]
               Inc a,1
               Until a>7
               Inc KK,1   ' Increase counter, 8 samples for AN0 and 8 samples for AN1
               
         Else
         
               a=0       ' Select second buffer
               Repeat
               b=a+KK*8
               c=a*2
               XX[b]=wAdcResult2[c]
               YY[b]=wAdcResult2[c+1]
               Inc a,1
               Until a>7
               Inc KK,1   ' Increase counter, 8 samples for AN0 and 8 samples for AN1
               
               
         EndIf     
               
         FLV=~FLV   ' Ping-pong flag     
         
         IFS0bits_DMA1IF = 0 'Clear the DMA1 Interrupt Flag 
       
    EndIsr

ADC_Configure ()     ' Configure ADC
DMA_Config()         ' Configure DMA
DelayMS 100


Main:

KK=0 ' Reset block counter
IEC0bits_DMA1IE  = 1 ' DMA interrupt ON
While KK <64:Wend   ' Acquire 64*8 samples for each channel
IEC0bits_DMA1IE  = 0' DMA interrupt OFF

' Here there are XX and YY vectors filled with samples from AN0 & AN1

End




Proc ADC_Configure()
   
' Sampling frequency calculation example:
' TCY=To/2
' TAD=TCY*(ADCS+1)
' SAMC = Auto Sample Time
' T_sampling=2*12*TAD+SAMC*TAD
' F_Sampling=1/T_sampling

' Es. FCY=79.2275 MHz
' To=12.62188 ns
' TCY=25.2436 ns
' ADCS=1
' SAMC=6
' F_Sampling=660.22 KHz


AD1CON1bits_ADON = 0' Turn Off the A/D converter

AD1CON1bits_ADSIDL = 1'  Stop conversions in SLEEP mode

AD1CON1bits_ADDMABM = 1' Write sequentially

AD1CON1bits_AD12B = 0'   10 Bit resolution

AD1CON1bits_FORM1 = 0' Unsigned integer mode
AD1CON1bits_FORM0 = 0' Unsigned integer mode

AD1CON1bits_SSRC2 = 1'   Internal clock as trigger
AD1CON1bits_SSRC1 = 1'   Internal clock as trigger
AD1CON1bits_SSRC0 = 1'   Internal clock as trigger

AD1CON1bits_SIMSAM = 1' Simultaneous sampling
AD1CON1bits_ASAM = 1'   Autosampling on

AD1CON2bits_VCFG2 = 0'  Use Internal reference (AVdd, AVss)
AD1CON2bits_VCFG1 = 0'
AD1CON2bits_VCFG0 = 0'

AD1CON2bits_CSCNA = 0'  Do not scan multiple inputs
     
AD1CON2bits_CHPS1 = 0' Sample CHO & CH1
AD1CON2bits_CHPS0 = 1' Sample CHO & CH1                   

AD1CON2bits_SMPI3 = 0' 
AD1CON2bits_SMPI2 = 0' 
AD1CON2bits_SMPI1 = 0'
AD1CON2bits_SMPI0 = 1' Increments the DMA address after completion of every 2nd sample/conversion operation

AD1CON2bits_BUFM = 0'   Always starts filling buffer at address 0x0
AD1CON2bits_ALTS = 0'   Alternate mode OFF

AD1CON3bits_ADRC = 0'   Use system clock

AD1CON3bits_SAMC0=0
AD1CON3bits_SAMC1=1  ' Auto Sample Time = 6 * TAD
AD1CON3bits_SAMC2=1
AD1CON3bits_SAMC3=0 
AD1CON3bits_SAMC4=0

AD1CON3bits_ADCS0=1  ' ADC @ 666 Ksps  - 2 channels with simultaneous sampling -
AD1CON3bits_ADCS1=0   
AD1CON3bits_ADCS2=0     
AD1CON3bits_ADCS3=0
AD1CON3bits_ADCS4=0
AD1CON3bits_ADCS5=0
AD1CON3bits_ADCS6=0
AD1CON3bits_ADCS7=0

AD1CON4bits_DMABL2 = 1' Allocates 16 words of buffer to each analog input
AD1CON4bits_DMABL1 = 0'
AD1CON4bits_DMABL0 = 0'

AD1CHS0bits_CH0SA4 = 0' CHO MUX A, Positive input is AN1
AD1CHS0bits_CH0SA3 = 0'
AD1CHS0bits_CH0SA2 = 0'
AD1CHS0bits_CH0SA1 = 0'
AD1CHS0bits_CH0SA0 = 1'

AD1CHS0bits_CH0NA = 0'   CHO MUX A, Negative Input is Vref- (AVss)
 
AD1CHS123bits_CH123SA=0   ' CH1 select input AN0
AD1CHS123bits_CH123NA0=0  ' Negative Input is Vref- (AVss)
AD1CHS123bits_CH123NA1=0  ' Negative Input is Vref- (AVss)

AD1CON1bits_ADON = 1' Turn on the A/D converter

EndProc
 
 
Proc DMA_Config()

    dwAddressOfRamArray1 = AddressOf(wAdcResult1) 'physical address of the first  DMA buffer
    dwAddressOfRamArray2 = AddressOf(wAdcResult2) 'physical address of the second DMA buffer
   
    DMA1CONbits_SIZE = 0'    Transfer data in Word form (2 bytes)
    DMA1CONbits_DIR = 0'     Transfer data from ADC to RAM
    DMA1CONbits_HALF = 0'    Trigger the interrupt when you have finished transferring ALL the data you need to
    DMA1CONbits_NULLW = 0'   
   
    DMA1CONbits_AMODE1 = 0'  Register Indirect with Post-Increment mode
    DMA1CONbits_AMODE0 = 0' 
   
    DMA1CONbits_MODE1_DMA1CON  = 1'   Continuous, Ping-Pong modes enabled
    DMA1CONbits_MODE0_DMA1CON  = 0'   
   
    ' IRQ_Selection = 13
    DMA1REQbits_IRQSEL0=1
    DMA1REQbits_IRQSEL1=0
    DMA1REQbits_IRQSEL2=1
    DMA1REQbits_IRQSEL3=1
    DMA1REQbits_IRQSEL4=0
    DMA1REQbits_IRQSEL5=0
    DMA1REQbits_IRQSEL6=0
   
    DMA1REQbits_FORCE= 0
   
    DMA1STA = dwAddressOfRamArray1 ' Point to physical address of the first  DMA buffer
    DMA1STB = dwAddressOfRamArray2 ' Point to physical address of the second DMA buffer
   
    DMA1PAD=0x0300 ' Address of register ADC1BUF0
    DMA1CNT = dma_numcampioni-1 ' Number of transfers to be made before triggering the interrupt
   
    IFS0bits_DMA1IF = 0' Clear the DMA interrupt flag bit
    DMA1CONbits_CHEN=1 ' Enable DMA

EndProc

I hope it can be useful  ;)