|
|
View previous topic :: View next topic |
Author |
Message |
guy
Joined: 21 Oct 2005 Posts: 291
|
Update - code for compiler version 5.026, help??? |
Posted: Tue Aug 19, 2014 3:16 am |
|
|
problem: I'm using DMA with compiler v. 5.026 and PIC24FJ64GA308.
Good news: I solved a lot of issues (see below quick DMA guide...)
Bad news: The DMA interrupt routine only gets called once after the first DMA transaction. Even turning off the DMA module, clearing interrupts etc. doesn't help. Anyone has any ideas?
As for the part which does work:The documentation is not very clear so here is my version, which works well except the interrupt
Code: | disable_interrupts(GLOBAL);
enable_interrupts( INT_TBE );
setup_dma(1,DMA_TRIGGER_TBE, DMA_BYTE);
dma_start(1, DMA_ONE_SHOT|DMA_FORCE_NOW|DMA_INC_SOURCE_ADDR,0x224,&txBuffer[0],cnt);
|
* cnt *doesn't* have to be decreased. It does represent actual number of trasnfers/characters sent.
* 0x224 is the destination address (U1TXREG). Adjust for other UARTs/peripherals.
* #BANK_DMA is not necessary for this compiler version and PIC. During startup the limit registers (DMAH & DMAL) get set to full address range, for better or for worse...
* Tip: DMA also seems to work without enabling any interrupts! If you *do* enable interrupts make sure to have a handler, even if it is empty. Otherwise the debugger will stop execution due to an unhandled interrupt. Example:
Code: | #INT_TBE
void tbe_isr() {
}
|
If you poll dma_status you will see 0x100 (DMA_REQUEST_INITIATED) immediately after dma_start. Then it reads 0, later 0x10 (DMA_HALF_DONE) and at the end 0x30 (DMA_HALF_DONE|DMA_DONE). If you plan on polling I suggest to poll for DMA_DONE bit:
Code: | while(!(dma_status()&DMA_DONE)) ; |
Thanks for the guiding me in the right direction |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19255
|
|
Posted: Tue Aug 19, 2014 9:08 am |
|
|
DMA_ONE_SHOT, specifically disables the DMA channel when the transfer completes. It sets DMACNT to zero, so no further transfer will occur. The DMA_REPEATED option (or'ed in), tells it to reload the address, so it can be triggered again. You set or clear the CHEN bit to stop/start the transaction.
When using the DMA, the DMA controller receives the interrupt. The normal enable affects whether the CPU responds _as well_. You wouldn't normally want this, since the buffer will have already been written by the DMA controller. |
|
|
guy
Joined: 21 Oct 2005 Posts: 291
|
Solved |
Posted: Tue Aug 19, 2014 9:28 am |
|
|
Quote: | DMA_ONE_SHOT, specifically disables the DMA channel when the transfer completes. It sets DMACNT to zero, so no further transfer will occur. The DMA_REPEATED option (or'ed in), tells it to reload the address, so it can be triggered again. You set or clear the CHEN bit to stop/start the transaction. |
So DMA_ONE_SHOT is in fact what I need. I want to initiate a new, different-size transfer only when I have more data to send. By the way, I saw that CHEN is much more reliable than dma_status which I mentioned before.
Quote: | When using the DMA, the DMA controller receives the interrupt. The normal enable affects whether the CPU responds _as well_. You wouldn't normally want this, since the buffer will have already been written by the DMA controller. |
Correct, of course. I only mentioned that it's not required since Microchip says you should enable the interrupt (5.2 Typical Setup).
Found it! The DMA interrupt routine (not the trigger) was called only after the first transfer because the interrupt flags DONEIF, HALFIF don't get cleared (not even with clear_interrupt).
To fix this we need to clear them manually inside the interrupt routine or before the next transfer. Here is the complete code:
Code: | #include <24FJ64GA308.h>
#fuses XT,PR_PLL,BROWNOUT,WDT128,WDT,WPOSTS10,ICSP1
// 8MHz XT+4xPLL, WDT 2 sec nominal
#use delay(CLOCK=32000000) // 8MHz*4PLL = 32MHz
#PIN_SELECT U1RX =PIN_B9 // from 232 conv from 5V to 3.3V
#PIN_SELECT U1TX =PIN_B8 // to 232 open drain!!
#use RS232(STREAM=R232, BAUD=9600, UART1, ERRORS) // VR-500 RS-485 bus
char buf[10];
/////////////////////////////
#INT_DMA0
void dma_isr() {
// called when DMA is complete
#WORD DMAINT0 = 0x38A // DMA0 interrupt register
fputc('*',R232); // also UART1
DMAINT0=0; // clear interrupt flags, shut down DMA for now
}
/////////////////////////////
void DMAhandler() {
#define U1TXREG 0x224 // uart1 transmit register address
byte cnt;
strcpy (buf, "Hi There");
cnt=8; // 8 characters
setup_dma(0,DMA_TRIGGER_TBE, DMA_BYTE);
// start DMA of 8 characters from buf to UART1 (AKA R232) :
dma_start(0, DMA_ONE_SHOT|DMA_FORCE_NOW|DMA_INC_SOURCE_ADDR,U1TXREG,&buf[0],cnt);
}
//////////////////////////////
void main(void) {
enable_interrupts(INT_DMA0);
enable_interrupts(GLOBAL);
DMAhandler();
delay_ms(100);
DMAhandler();
delay_ms(100);
DMAhandler();
delay_ms(100);
// output is Hi There*Hi There*Hi There*
while(1) ;
}
|
Thanks everyone. |
|
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|