Other Parts Discussed in Thread: TMS320C6748, SYSBIOS
Hello,
Considering an asynchronous 16bit device, attached to EMIFA CS4, how can I configure the EDMA to transfer 16bit data?
Thank you in advance,
David.
This thread has been locked.
If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.
Other Parts Discussed in Thread: TMS320C6748, SYSBIOS
Hello,
Considering an asynchronous 16bit device, attached to EMIFA CS4, how can I configure the EDMA to transfer 16bit data?
Thank you in advance,
David.
David,
Which device are you using?
Do you want to make 32-bit data fit the 16-bit EMIFA bus width (2 transfers each)? Or do you want to just access 16-bit values one-at-a-time?
C6000 devices access memory as byte-addressable. A table of memory addresses would be helpful, showing how you want the values to reside in memory and on the EMIFA device. Also, the nature of the device on the EMIFA bus could help.
Regards,
RandyP
Hi Randy,
Thank you for replying. Our device is a AD7606-4 which after a conversion you can read 4 samples one after the other as 16bit data, so I would say it's one-at-a-time. This 4 samples needs to go in 4 buffers. My setup so far is using StarterWare:
int16_t Buffers[5000][4];
EDMA3CCPaRAMEntry paramSet;
uint8_t *p = (uint8_t *)¶mSet;
uint32_t i = 0;
uint32_t tccNum = EDMA3_CHA_GPIO_BNKINT8;
uint32_t chNum = EDMA3_CHA_GPIO_BNKINT8;
/* Clean-up the contents of structure variable. */
for (i = 0; i < sizeof(paramSet); i++)
{
p[i] = 0;
}
/* Fill the PaRAM Set with Receive specific information. */
/* srcAddr holds address of SPI Rx FIFO. */
paramSet.srcAddr = (uint32_t) (SOC_EMIFA_CS4_ADDR);
/* destAddr is address of memory location named buffer. */
paramSet.destAddr = (uint32_t) &Buffers[0][0];
/* aCnt holds the number of bytes in an array. */
paramSet.aCnt = (uint16_t) sizeof(int16_t);//AD7606 16bit
/* bCnt holds the number of such arrays to be transferred. */
paramSet.bCnt = (uint16_t) 4;//adc ch
/* cCnt holds the number of frames of aCnt*bBcnt bytes to be transferred. */
paramSet.cCnt = (uint16_t) 5000;
/* The srcBidx should not be incremented since it is a h/w register. */
paramSet.srcBIdx = 0;
/* The destBidx should be incremented for every byte. */
paramSet.destBIdx = 1;
/* A sync Transfer Mode. */
/* srCIdx set to zero since ASYNC Mode is used. */
paramSet.srcCIdx = 0;
//difference between the starting addresses of each array ->see Bursting peripherals chapter in Technical Reference Manual, page 555
paramSet.destCIdx = 5000 * sizeof(int16_t);
/* Linking transfers in EDMA3 are not used. */
paramSet.linkAddr = (uint16_t)0xFFFF;
paramSet.bCntReload = 0;
paramSet.opt = 0x00000000;
/* Set TCC field in OPT with the tccNum. */
paramSet.opt |= ((tccNum << EDMA3CC_OPT_TCC_SHIFT) & EDMA3CC_OPT_TCC);
/* EDMA3 Interrupt is enabled and Intermediate Interrupt Disabled.*/
paramSet.opt |= (1 << EDMA3CC_OPT_TCINTEN_SHIFT);
/* Now write the PaRam Set to EDMA3.*/
EDMA3SetPaRAM(SOC_EDMA30CC_0_REGS, chNum, ¶mSet);
/* EDMA3 Transfer is Enabled. */
EDMA3EnableTransfer(SOC_EDMA30CC_0_REGS, chNum, EDMA3_TRIG_MODE_EVENT);
I've setup a GPIO pin as input interrupt whenever the ADC Busy signal has a falling edge.
I'm still learning how to debug the code using EDMA peripheral, hopefully you can have a look and spot anything wrong.
Best regards,
David.
David,
Which device are you using?
From the code, I can tell it has EDMA3. In the Training section of TI.com, there is a training video set for the C6474. In particular, the EDMA3/QDMA/IDMA Module may help you understand some of the features and options available within the EDMA3 module. You can find the complete video set here.
I have not used StarterWare, yet. Is your setup of the EDMA based on StarterWare examples?
When the ADC Busy signal has a falling edge, how much do you want to transfer? Where do you want it to go, buffer arrangement, etc.?
There are more than one register setting (dstBIdx, dstCIdx, bCntReload) that I think may be wrong, but some of these questions will help me understand better. And watching the EDMA3 training video may help you understand some of the registers better. EDMA3 is very similar from one device to another, so I doubt you are using the C6474 but this video may still be useful.
Regards,
RandyP
P.S. - This is the first case I have seen where someone used the SyntaxHighlighter. What do you think of it? Your last four lines are wrapped by the SyntaxHighlighter, too, even though they are after the code segment. Could you tell me, please, if you used the SH for those lines, or if you typed those lines after inserting the SH code, or before inserting the SH code?
Hi Randy,
I forgot to mention, it's the TMS320C6748 device. The setup is based on EDMA StarterWare examples which I successfully used with SPI.
When ADC Busy has a falling edge, I need to transfer 4 samples, each 16bit. It needs to go in Buffers array, declared as Buffers[5000][4] (of course, I can change the arrangement to suit DMA transfer better). Each of the 4 samples represent a different channel so I would prefer they go into their own array. I'll start watching the videos, thank you for the link.
The syntax highlighter it's very nice to use, it allows separating the code from comments quite nicely. If I remember correctly, I typed the first lines, then started the SyntaxHighlighter, pasted the code, pressed Insert button, back in the editing area pressed Enter to separate the text better, typed in the last comments and pressed Post button, but it appeared as it is now. I tried to go back re-edit the post, but I couln't find a button.
Best regards,
David.
David,
It sounds like you would prefer to declare Buffers[4][5000], which can be done and supported by the EDMA3.
Thank you for the feedback on the SyntaxHighlighter. I will pass your comments on to the forum support team. To edit the post, there is a pencil icon in the bottom left corner of the post, if you are logged in, and this allows you to edit your posts. If it is not there, let me know in your next reply. But please do not edit it right now. I would like the support team to see the effects in your post as they are.
Regards,
RandyP
Hi Randy,
I had a look at the slides, but still confused about BIDX and CIDX to address buffer positioning. Not to mention, the Error Handler Interrupt is triggered instead of my Dma handler. I was wondering if you could provide an example of how would you setup the EDMA for filling such a buffer, knowing that gpio event triggers availability of 4 samples and sample 0 needs to go in Buffer[0][n], sample 1 into Buffer[1][n], sample 2 into Buffer[2][n], sample 3 into Buffer[3][n] and after n reaches 5000, to receive an interrupt. At the moment I have this:
int16_t Buffer[4][5000];
EDMA3CCPaRAMEntry paramSet;
uint8_t *p = (uint8_t *)¶mSet;
uint32_t i = 0;
uint32_t tccNum = EDMA3_CHA_GPIO_BNKINT8;
uint32_t chNum = EDMA3_CHA_GPIO_BNKINT8;
GPIOBankIntEnable(SOC_GPIO_0_REGS, SYS_INT_GPIO_B8INT);
/* Clean-up the contents of structure variable. */
for (i = 0; i < sizeof(paramSet); i++)
{
p[i] = 0;
}
/* Fill the PaRAM Set with Receive specific information. */
/* srcAddr holds address of SPI Rx FIFO. */
paramSet.srcAddr = (uint32_t) (SOC_EMIFA_CS4_ADDR);
/* destAddr is address of memory location named buffer. */
paramSet.destAddr = (uint32_t) &Buffer[0][0];
/* aCnt holds the number of bytes in an array. */
paramSet.aCnt = (uint16_t) sizeof(int16_t);//T=int16_t for AD7606
/* bCnt holds the number of such arrays to be transferred. */
paramSet.bCnt = (uint16_t) 4; //channels
/* cCnt holds the number of frames of aCnt*bBcnt bytes to be transferred. */
paramSet.cCnt = (uint16_t) 5000;
/* The srcBidx should not be incremented since it is a h/w register. */
paramSet.srcBIdx = 0;
/* The destBidx should be incremented for every sample. */
paramSet.destBIdx = 5000 * sizeof(int16_t); //is it correct?
/* A sync Transfer Mode. */
/* srCIdx set to zero since ASYNC Mode is used. */
paramSet.srcCIdx = 0;
//difference between the starting addresses of each array ->see Bursting peripherals chapter in Technical Reference Manual, page 555
paramSet.destCIdx = sizeof(int16_t);///or swap with destbIdx ?
/* Linking transfers in EDMA3 are not used. */
paramSet.linkAddr = (uint16_t)0xFFFF;
paramSet.bCntReload = 0;
paramSet.opt = 0x00000000;
/* Set TCC field in OPT with the tccNum. */
paramSet.opt |= ((tccNum << EDMA3CC_OPT_TCC_SHIFT) & EDMA3CC_OPT_TCC);
/* EDMA3 Interrupt is enabled and Intermediate Interrupt Disabled.*/
paramSet.opt |= (1 << EDMA3CC_OPT_TCINTEN_SHIFT);
//do I need FIFO queue 16bit?
//paramSet.opt |= (EDMA3CC_OPT_FWID_16BIT << EDMA3CC_OPT_FWID_SHIFT);
paramSet.opt |= EDMA3CC_OPT_SYNCDIM; //enable AB sync transfer
EDMA3SetPaRAM(SOC_EDMA30CC_0_REGS, chNum, ¶mSet);
/* EDMA3 Transfer is Enabled. */
EDMA3EnableTransfer(SOC_EDMA30CC_0_REGS, chNum, EDMA3_TRIG_MODE_EVENT);
I don't mind if you have it as CSL example.
Best regards,
David.
David,
How did you avoid the SyntaxHighlighter's corruption of your last 3 lines? The forum team was looking into this, and fixed your previous post.
At first, I was sad to hear that you were still confused. But your code looks like you have it completely under control. The indexing looks right to me.
David Luca said:paramSet.opt |= ((tccNum << EDMA3CC_OPT_TCC_SHIFT) & EDMA3CC_OPT_TCC);
This line does not look right. None of the other .opt assignments have an & term included, so what is the value of EDMA3CC_OPT_TCC and what is it for?
Unsolicited programming advice: Change the p pointer from a byte pointer to a word pointer. The C6000 architecture is happiest when it is doing word accesses. Even though you may not care about the time it takes to do the clearing loop, it will go much faster if you use a word pointer, and it will generally be best to avoid 8-bit and 16-bit data types when you are not packing data into structs or arrays. This is especially true with loop indexes or counters, and you have the good choice of uint32_t for i.
David Luca said://do I need FIFO queue 16bit?//paramSet.opt |= (EDMA3CC_OPT_FWID_16BIT << EDMA3CC_OPT_FWID_SHIFT);
You definitely do not want to be using the FIFO mode or CONST mode. This particular field will have no effect when SAM==DAM==0.
David Luca said:the Error Handler Interrupt is triggered instead of my Dma handler
Which Error Handler interrupt is this? Either the interrupt routing is incorrect, or the DMA transfer has some serious problem, or both. I may not be able to give any true advice here, but that will not stop me from more comments.
The way I start out debugging a problem like this is to replace the last line (enabling the event trigger) with a series of lines like this
RandyP said:EDMA3 Trigger Transfer(SOC_EDMA30CC_0_REGS, chNum, ...); for (i=0;i<1000;i++);
a. Copy that line so it is listed 5 times.
b. Change CCNT to 4 and dstBIdx to 4*2 (for debug ease).
c. Set breakpoints on each of the 4 lines and the line after the fifth one, plus in both of the ISRs.
d. Initialize the buffer with something other than 0 so you will see if data gets there.
e. Use the Debug Build configuration so the for-i-loop does not go away.
f. Open one browser window on the Buffer array and another on the Param for this channel. I like to make the Param display be 8 words wide using TI format for max data showing.
g. Run to the first breakpoint and make sure everything looks ready to run. Look at the EDMA3 registers to make sure everything looks right, especially ER, EMR, SER, IPR, IER, and the Param and the Buffer.
h. Run to the second breakpoint and see whether the right things have changed. The first location of Buffer should have changed, and Buffer+4(*2) and so on, and the Param should have adjusted to be ready for the next sample.
i. Repeat the run-and-check until you get to the fifth breakpoint. At this point, the Param should have been cleared and the ISR triggered.
Regards,
RandyP
Hi Randy,
Same as before, pressing few times <Enter> after the code was inserted. The EDMA3CC_OPT_TCC value is actually a bit mask defined in StarterWare to mask the register when setting the TCC channel. The paramSet.opt is initially set to 0x00000000 so the options are added by using OR (|=). I was getting the Interrupt Error Handler because I forgot to call the request channel function, but it works now, I appreciate all your help for understanding EDMA peripheral, the slides were very helpful too and the debugging sequence you suggested.
Thank you and best regards,
David.
David, Randy,
This might perhaps not be the right forum to ask.
But, I'm about to connect a AD7606 to the TMS320C6748 evaluation board. I intend to use SPI and use AD7606 BUSY signal as an interrupt trigger for the SPI read. I'm curious to know what clock you use to generate the CONVST signal (trigger the AD sampling) in your design, David. I thought I could use a frame clock from the McASP. Is that possible or are there better ways?
Regards,
Henrik
Hi Henrik,
My option was to connect CONVST signals to Timer64P0_OUT1, pin E16 of C6748 and the timer was configured to generate pulses to the ADC.
The following is the StarterWare code that I used to select timer pulse functionality on the pin:
//initialise Timer HW #define PINMUX4_ADC_CONVST_ENABLE (SYSCFG_PINMUX4_PINMUX4_3_0_TM64P0_OUT12 << SYSCFG_PINMUX4_PINMUX4_3_0_SHIFT) HWREG(SOC_SYSCFG_0_REGS + SYSCFG0_PINMUX(4)) = (HWREG(SOC_SYSCFG_0_REGS + SYSCFG0_PINMUX(4)) & (~(SYSCFG_PINMUX4_PINMUX4_3_0))) | (PINMUX4_ADC_CONVST_ENABLE); HWREG(SOC_TMR_0_REGS + TMR_GPDATGPDIR) |= TMR_GPDATGPDIR_GPDIRO12;//pin set as output HWREG(SOC_TMR_0_REGS + TMR_GPINTGPEN) &= ~TMR_GPINTGPEN_GPENO12; HWREG(SOC_TMR_0_REGS + TMR_TCR) = 0; /* Configuration of Timer */ TimerConfigure(SOC_TMR_0_REGS, TMR_CFG_64BIT_CLK_INT); /* Set the 64 bit timer period */ TimerPeriodSet(SOC_TMR_0_REGS, TMR_TIMER12, (uint32_t)period); TimerPeriodSet(SOC_TMR_0_REGS, TMR_TIMER34, (uint32_t)(period >> 32)); TimerCounterSet(SOC_TMR_0_REGS, TMR_TIMER12, 0); TimerCounterSet(SOC_TMR_0_REGS, TMR_TIMER34, 0); TimerEnable(SOC_TMR_0_REGS, TMR_TIMER12, TMR_ENABLE_CONTRELOAD); TimerPulseWidthSet(SOC_TMR_0_REGS, TMR_TIMER12, TMR_PULSE_WIDTH_1_CLK); TimerPulseModeSet(SOC_TMR_0_REGS, TMR_TIMER12); TimerInvertOUTEnable(SOC_TMR_0_REGS, TMR_TIMER12);
The pulses are generated considering 24MHz timer 0 and are around 44ns, timer pulse can generate up to 4 clocks per pulse ~168ns. This way no CPU interrupts are involved to generate convst signals.
Best regards,
David.
David,
I tried to connect AD7606 to L138LCDK now. Can you share your final SPI and EDMA initialization odf your AD7606 routine files here?
Hi Dmitry1,
I pasted below some of my GPIO initialization code and how I can read a value directly, however I'm not reading via SPI, the ADC is attached to EMIFA interface on CS2 and is triggered by timer and values are transferred via EDMA on busy input falling so I don't think is much help to you.
void ADC7606::Init()
{
g_arGpioOutputs[OUT_ADC_RESET].SetDir(OUTPUT);
g_arGpioOutputs[OUT_ADC_RESET].Write(0);
//Reset();
g_arGpioOutputs[OUT_ADC_RESET].Write(1);
for(uint32_t i = 0; i < 1000; i++)
{
;// reset adc wait here
}
g_arGpioOutputs[OUT_ADC_RESET].Write(0);
g_arGpioOutputs[OUT_ADC_OS0].SetDir(OUTPUT);
g_arGpioOutputs[OUT_ADC_OS0].Write(0);
g_arGpioOutputs[OUT_ADC_OS1].SetDir(OUTPUT);
g_arGpioOutputs[OUT_ADC_OS1].Write(0);
g_arGpioOutputs[OUT_ADC_OS2].SetDir(OUTPUT);
g_arGpioOutputs[OUT_ADC_OS2].Write(0);
g_arGpioInputs[IN_ADC_BUSY].SetDir(INPUT); //prepare busy input when it goes to 0 to read data
// g_arGpioOutputs[OUT_ADC_CONVST].SetDir(OUTPUT);//do NOT use it when TimerConvSt is initialized!
// g_arGpioOutputs[OUT_ADC_CONVST].Write(1); //needs to stay in 1for conv start goes to 0
g_arGpioOutputs[OUT_ADC_OS1].SetDir(OUTPUT);//TODO see TOTUS-102, this line should not be needed
//select filter
filter = 0; //see datasheet
g_arGpioOutputs[OUT_ADC_OS0].Write(filter & 0x01);
g_arGpioOutputs[OUT_ADC_OS1].Write((filter & 0x02) >> 1);
g_arGpioOutputs[OUT_ADC_OS2].Write((filter & 0x04) >> 2);
}
void AD7606::Read()
{
uint32_t timeout = 10;
g_arGpioOutputs[OUT_ADC_CONVST].Write(0); //generate conv start
// for(uint32_t i=0; i < 5; i++)
{
;//do nothing
}
g_arGpioOutputs[OUT_ADC_CONVST].Write(1); //happens at some time later
while(IsBusy() == false && timeout-- > 1)
{
Task_sleep(10);
}
if(timeout <= 1)
{
for(uint8_t i = 0; i < 4; i++)
{
m_iCh[i] = 0;
}
return;
}
timeout = 10;
while(IsBusy() && timeout-- > 1)
{
Task_sleep(10);
}
if(timeout <= 1)
{
for(uint8_t i = 0; i < 4; i++)
{
m_iCh[i] = 0;
}
return;
}
//for(uint8_t i = 0; i < 4; i++)//reading should be faster
{
m_iCh[0] = *AD7606_BASE_ADDR;
m_iCh[1] = *AD7606_BASE_ADDR;
m_iCh[2] = *AD7606_BASE_ADDR;
m_iCh[3] = *AD7606_BASE_ADDR;
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool AD7606::IsBusy()
{
return g_arGpioInputs[IN_ADC_BUSY].Read();
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
int16_t AD7606::GetChCounts(uint8_t channel)
{
if(channel >= 4)
{
return 0.0f;
}
return m_iCh[channel];
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////
float32_t AD7606::GetChVoltage(uint8_t channel)
{
if(channel >= 4)
{
return -10.0f; //wrong channel! don't use it in calculations
}
// return 10.0f*(float32_t)m_iCh[channel]/65535.0f; //original
return GetCounts2Voltage(m_iCh[channel]);
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////
float32_t GetCounts2Voltage(int16_t counts)
{
return ((float32_t)counts * 2.5f * 5.0f) / (32768.0f * ADC_VREF);
}
Best regards,
David.
Hi Dmitriy1,
The driver we wrote became quite complicated but I can give you a few hints. The GPIO bank can trigger interrupt but also EDMA event which was set up using LLD EDMA drivers so that EDMA transfers into internal memory the data. My application is running in SYSBIOS and a PSP driver was changed to initialize EDMA and provide pointers to where EDMA should fill the data from ADC. You have to also use linking EDMA events so that it always reloads the next pointer at end of completion in the EDMA interrupt.
Best regards,
David.
Thank you for response!
I tried to catch GPIO interrupts first. I tried to use HWI for this.
I want to use GP2[15] for receiving interrupt.
My steps:
1) initialization GPIO pins as input
2) initialization GPIO rising-edge interrupt, and enable interrupt globally (I use StarterWare function for this)
/* Sets the pin 48(GP2[15]) as input for receive interrupt from external ADC.*/
GPIODirModeSet(SOC_GPIO_0_REGS, 48, GPIO_DIR_INPUT);
/*
** Configure falling edge and falling edge triggers on pin 48 to generate
** an interrupt
*/
GPIOIntTypeSet(SOC_GPIO_0_REGS, 48, GPIO_INT_TYPE_FALLEDGE);
/* Enable interrupts for Bank 2.*/
GPIOBankIntEnable(SOC_GPIO_0_REGS, 2);
// Initialize DSP interrupt controller
IntDSPINTCInit();
// Enable DSP interrupts globally
IntGlobalEnable();
Next I added HWI in my .cfg file
var hwi0Params = new Hwi.Params();
hwi0Params.instance.name = "hwi0";
hwi0Params.eventId = 49;
hwi0Params.maskSetting = xdc.module("ti.sysbios.interfaces.IHwi").MaskingOption_NONE;
Program.global.hwi0 = Hwi.create(4, "&ad7606_clkpin_toggle", hwi0Params);
But after start I see that programm never enters to ISR("ad7606_clkpin_toggle" function). Should I use something else (task or myidle) for correct use HWI GPIO ?
Or ypu used onlu services of LLD EDMA for catching GPIO event?