I get the impression that if I want to use DMA with MQX I'll have to go way off piste. Is this assumption correct? If so what is the best way to go about it, presumably there is no docs/an's giving examples?
What I'm trying to do is fire off the ADC from the PDB, then get the ADC to generate a DMA request on completion. I'm fairly sure none of the MQX ADC drivers will support this, so what is the suggested route?
PS on a Kenetis uP if that has any bearing.
Solved! Go to Solution.
Hi Bill,
MQX is extremely flexible and as the system designer you can literally "to-it-your-way". So no right or wrong answer. Just trying to implement the coding as you like.
The option #1 you mention is fine. Not certain what you mean by outside any MQX command but will guess you mean accessing the peripheral registers directly. You might also mean that the interrupt(s) will operate outside MQX as well. This is OK too.
Option #2 is what I recommend starting with and then if the MQX system cannot achieve the timing you need, then implementing direct interrupts (sometimes called Gorilla interrupts) will prempt the MQX RTOS while you service the DMA interrupt.
It all comes down to managing the system how you want.
Regards,
David
Hi Billnd,
Please have a look at following appnote (it does have ZIP file with code too).
AN4590 Using DMA to Emulate ADC Flexible Scan Mode on Kinetis K Series
The appnotes can be found under the product pages of the device by clicking the Documentation tab.
Regards,
David
David,
Thanks for your response. I think I may have phrased my question badly, indeed reading it back it does not ask the question that I'm looking to answer. So, I'll give it another go...
I'm planning to use the ADC in a fashion that is not fully supported by MQX, ie linking it to the DMA system. As far as I see it there are two different routes I can take....
1. Use MQX for as much of the ADC work as possible, ie calibration, config (as much as MQX supports) and PDB triggers. But then, outside of any MQX command, alter the ADC to fire off the DMA.
2. Side step MQX completely for all the ADC work, and set it up without using any MQX commands.
My gut feeling is that option 2 is likely to cause least hassle, but option 1 may offer some unknown benefits. Thoughts?
Although in this example I'm being specific about the ADC, I'm really after formulating a general principle regarding using MQX. Should I be aiming to use MQX for as much as possible, or is it better to only use it for things that it supports 100%?
Cheers in advance.
Bill
Hi Bill,
MQX is extremely flexible and as the system designer you can literally "to-it-your-way". So no right or wrong answer. Just trying to implement the coding as you like.
The option #1 you mention is fine. Not certain what you mean by outside any MQX command but will guess you mean accessing the peripheral registers directly. You might also mean that the interrupt(s) will operate outside MQX as well. This is OK too.
Option #2 is what I recommend starting with and then if the MQX system cannot achieve the timing you need, then implementing direct interrupts (sometimes called Gorilla interrupts) will prempt the MQX RTOS while you service the DMA interrupt.
It all comes down to managing the system how you want.
Regards,
David
HI DAVID
I am trying to configure UART-DMA yet I cannot do so far.
Could you help me to resolve my problem? I am not sure concerning to interruption value.
In intention to help for better understanding of my problem I divide this message into three sesions.
(A) my Check list.
(B) References
(C) My code.
Thanks for your help in advance.
(A)
1.- UART3 configuration to 9600 bps using TTYD
Uart_serie3 = fopen("ttyd:", BSP_DEFAULT_IO_OPEN_MODE);
if (Uart_serie3==NULL)
{
printf("ERROR ");
}
else{
write(Uart_serie3,(pointer)"Ready 3",7);
fflush(Uart_serie3);
}
2.- Interrupt-function asociation.
_int_install_isr(32, dam0_isr,buffer_TUART3.buffer);
interrupcion 32 es del DMA
I tried using following configuration but does not work
_int_install_isr(INT_UART3_RX_TX, dam0_isr,buffer_TUART3.buffer);
3.- I made the TCD configuration followin intructions on AN2998-1.pdf page 10 table 4.20, as well as two references from towergeeks
4.- To activate the configuration I did the following.
configure_TCD();
Enable_channel_0_1(); transmission reception channels ON
DMA_on(1); CHANNEL 1 ON
(B) REFERENCES
https://community.freescale.com/thread/117204
https://community.freescale.com/message/106758#106758
AN2998-1.pdf PAGE 10 TABLE 4.20 transfer control descriptors
Document Number: K60P100M100SF2RM Rev. 6, Nov 2011
(C)
#include "main.h"
//BUFFERS for Tx and Rx in UART3 to be used by DMA
struct _uart_buf
{
int index;
char buffer[64];
} buffer_RUART3,buffer_TUART3;
//pointer to UART 5 and 3
MQX_FILE_PTR Uart_serie5=NULL;
MQX_FILE_PTR Uart_serie3=NULL;
/* function for UART-DMA interruption*/
void dam0_isr(pointer user_DMAisr_ptr)
{
static unsigned char cnt=0;
DMA_INT = 0x1; // clear dma int flag
cnt++;
memset(buffer_TUART3.buffer,cnt,64);
DMA_TCD0_SADDR = (unsigned long)&buffer_TUART3.buffer[0];
DMA_ERQ |= (1 << 0);//
DMA_TCD1_DADDR = (unsigned long)&buffer_RUART3.buffer[0];
DMA_ERQ |= (1 << 1);//
}
/* turn on channels DMAmux 0 and 1 Tx and Rx for UART3*/
void Enable_channel_0_1(void)
{
/* Configure DMAMux for Channel 0 */
DMAMUX_CHCFG0 = 9; //receive UART3_TX
DMAMUX_CHCFG0 |= DMAMUX_CHCFG_ENBL_MASK;//Enable DMA channel
DMA_ERQ |= (1 << 0);// inicia START
/* Configure DMAMux for Channel 1 */
DMAMUX_CHCFG1 = 8; // transmitte UART3_TX
DMAMUX_CHCFG1 |= DMAMUX_CHCFG_ENBL_MASK;//Enable DMA channel
DMA_ERQ |= (1 << 1);// inicia START
}
/* TCD configuration based on AN2998-1.pdf page 10 table 4.20 transfer control descriptors
* and the Document Number: K60P100M100SF2RM Rev. 6, Nov 2011 */
void configure_TCD (void)
{
DMAMUX_CHCFG0 = 0; //channel 0 turn off
/* Configure DMA Channel 0 TCD */
DMA_TCD0_SADDR = ((uint_32)&UART3_D);/* data UART3_D, 0x4006D007 UART3 address*/
DMA_TCD0_ATTR = (DMA_ATTR_SMOD(0x0) /* Source Modulo, feature disabled */
| DMA_ATTR_SSIZE(0x0) /* Source Size = 0x0 -> 8-bit transfers */
| DMA_ATTR_DMOD(0x0)); /* Destination Modulo, feature disabled */
// | DMA_ATTR_DSIZE=(0x0)); /* Destination Size = 0x2 -> 32-bit transfers */
DMA_TCD0_SOFF = 0; /* Source addr offset = 0x0, el incremento lo dejo a cero porque la direccion es fija */
DMA_TCD0_NBYTES_MLNO = 0x4; /* Transfer 4 bytes per channel activation */
DMA_TCD0_SLAST = DMA_SLAST(0x0); /* Do not adjust SADDR upon channel completion */
DMA_TCD0_DADDR = ((uint_32)&buffer_RUART3.buffer[0]); /* Destination Address = BUFFER DE RECEPCION */
DMA_TCD1_CITER_ELINKNO = 0;
DMA_TCD1_CITER_ELINKNO = 0x1; /* Current Iter Count -> 1 "NBYTES" transfer */
DMA_TCD1_DOFF = 0x1; /* Destination addr offset = 0x1, AQUI SI INCREMENTO PARA QUE LO ALMACENE EN EL BUFFER DE RECEPCION */
DMA_TCD1_DLASTSGA = 0x0; /* Do not adjust DADDR upon channel completion */
DMA_TCD1_BITER_ELINKNO = 0;
DMA_TCD1_BITER_ELINKNO = 0x1; /* TCD Beginning Minor Loop Link, Major Loop Count (Channel Linking Disabled) */
DMA_TCD1_CSR = 0;
DMA_TCD1_CSR |= DMA_CSR_INTMAJOR_MASK; /* Enable an interrupt when major iteration count completes.*/
DMA_TCD1_CSR |= DMA_CSR_DREQ_MASK; /* If this bit is set when the channel completes a major loop, the eDMA clears the
corresponding DMAERQ, disabling the transfer request.*/
DMA_TCD1_CSR |= 1 << 255; // ACTIVA EL START
NVICISER0 |= 1 << 0 ;// enable interrupt NVICISERn = 1 << m, where n = 0/32, m = 0% 32 dma interrupt 32
NVICICPR0 |= 1 << 0;
DMAMUX_CHCFG1 = 0;
/* Configure DMA Channel 1 TCD */
DMA_TCD1_SADDR = ((uint_32)&buffer_TUART3.buffer[0]);/* Source Addr = address of command var */
DMA_TCD1_ATTR = (DMA_ATTR_SMOD(0x0) /* Source Modulo, feature disabled */
| DMA_ATTR_SSIZE(0x0) /* Source Size = 0x0 -> 8-bit transfers */
| DMA_ATTR_DMOD(0x0)); /* Destination Modulo, feature disabled */
// | DMA_ATTR_DSIZE=(0x2)); /* Destination Size = 0x2 -> 32-bit transfers */
DMA_TCD1_SOFF = 1; /* Source addr offset = 0x1, increment */
DMA_TCD1_NBYTES_MLNO = 0x4; /* Transfer 4 bytes per channel activation */
DMA_TCD1_SLAST = 0x0; /* Do not adjust SADDR upon channel completion */
DMA_TCD1_DADDR = ((uint_32)&UART3_D);/* Dest Addr = UART3_D Command Word Register 0x4006D007*/
DMA_TCD1_CITER_ELINKNO = 0;
DMA_TCD1_CITER_ELINKNO = 0x1; /* Current Iter Count -> 1 "NBYTES" transfer */
DMA_TCD1_DOFF = 0x1; /* Destination addr offset = 0x1, increment */
DMA_TCD1_DLASTSGA = 0x0; /* Do not adjust DADDR upon channel completion */
DMA_TCD1_BITER_ELINKNO = 0;
DMA_TCD1_BITER_ELINKNO = 0x1; /* TCD Beginning Minor Loop Link, Major Loop Count (Channel Linking Disabled) */
DMA_TCD1_CSR = 0;
DMA_TCD1_CSR |= DMA_CSR_INTMAJOR_MASK;
DMA_TCD1_CSR |= DMA_CSR_DREQ_MASK;
DMA_TCD1_CSR |= 1 << 255;
NVICISER0 |= 1 << 0 ;// enable interrupt NVICISERn = 1 << m, where n = 0/32, m = 0% 32 dma interrupt 32
NVICICPR0 |= 1 << 0;
}
void DMA_off(int DMA_CHn)
{
DMA_ERQ &= ~(1 << DMA_CHn); /* stop; The ERQ register provides a bit map for the 16 implemented channels to enable the request signal for each channel. */
}
void DMA_on(int DMA_CHn)
{
DMA_ERQ |= (1 << DMA_CHn); /* start The ERQ register provides a bit map for the 16 implemented channels to enable the request signal for each channel. */
}
/* Parametros tomados de recomendaciones de twr geeks
* https://community.freescale.com/thread/117204
* https://community.freescale.com/message/106758#106758 */
void UART_config()
{
/* no configurar la UART en modo interrupcion !!!! */
UART3_C2 &= ~(UART_C2_TE_MASK | UART_C2_RE_MASK);
UART3_C5 &= ~(UART_C5_TDMAS_MASK | UART_C5_RDMAS_MASK);
UART3_C5 |= UART_C5_TDMAS_MASK;
UART3_C5 |= UART_C5_RDMAS_MASK;
UART3_C2 = (1 << 7) | (1 << 5) | (1 << 2) ;// allow income, interrupt, it allows the receiver to
UART3_C5 = (1 << 7) | (1 << 5) ;// allow the recipient, hair interrupt generates a DMA request
UART3_C1 |= 1 << 7 ;// use loopback mode
UART3_C2 |= UART_C2_TE_MASK; //| UART_C2_RE_MASK);
SIM_SCGC6 |= SIM_SCGC6_DMAMUX_MASK;
SIM_SCGC7 |= SIM_SCGC7_DMA_MASK;
// Set FIFO water marks (not used in default 'legacy' port configuration)
UART3_TWFIFO = UART_TWFIFO_TXWATER(0);
UART3_RWFIFO = UART_RWFIFO_RXWATER(1);
}
void DMA_config()
{
configure_TCD();
Enable_channel_0_1();
}
TASK_TEMPLATE_STRUCT MQX_template_list[] =
{
/* Task number, Entry point, Stack, Pri, String, Auto? */
{MAIN_TASK, Main_task, 2000, 9, "main", MQX_AUTO_START_TASK},
{0, 0, 0, 0, 0, 0, }
};
/*TASK*-----------------------------------------------------------------
*
* Function Name : Main_task
* Comments :
* This task initializes MFS and starts SHELL.
*
*END------------------------------------------------------------------*/
void Main_task(uint_32 initial_data)
{
short int i;
//MY_ISR_STRUCT_PTR isr_ptr=NULL;
rtcs_init();
Uart_serie3 = fopen("ttyd:", BSP_DEFAULT_IO_OPEN_MODE);
if (Uart_serie3==NULL)
{
printf("ERROR no se pudo abrir el dispositivo serial 3");
}
else{
write(Uart_serie3,(pointer)"Ready 3",7);
fflush(Uart_serie3);
}
Uart_serie5 = fopen("ittyf:", BSP_DEFAULT_IO_OPEN_MODE);
if (Uart_serie5==NULL)
{
printf("ERROR no se pudo abrir el dispositivo serial 5");
}
else{
write(Uart_serie5,(pointer)"Ready 5",7);
fflush(Uart_serie5);
}
_int_install_isr(32, dam0_isr,buffer_TUART3.buffer);
UART_config();
DMA_config();
DMA_on(1);
for (i = 0; i <64; i ++)
{
buffer_TUART3.buffer[i] = 'A';
}
buffer_TUART3.index = 1;
buffer_RUART3.index = 0;
UART3_C2 |= 1 << 3; /* Activa la recepcion de mensajes */
fflush(Uart_serie3);
for (;;)
{
_time_delay_ticks(1000);
for (i = 0; i <64; i ++)
{
write(Uart_serie3,(pointer)&buffer_TUART3.buffer[i],1);
}
write(Uart_serie3,"\r\n",2);
}
fflush(Uart_serie3);
close(Uart_serie3);
fflush(Uart_serie5);
close(Uart_serie5);
DMA_off(0);
DMA_off(1);
_mqx_exit(0);
}
Hi,
Should start to see a bit more DMA support in a few MQX drivers in future releases. Keep an eye out for the releases towards the second half of the year. In the meantime, as David mentioned, it is very flexible and allows for DMA support to be added and customized.
Thanks,
MacL