- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Hi,
I have a test project were i use the ADC SAR Seq available in the 4200M, i need to read three inputs, i decided to transfer the results of the conversions from the ADC to memory with DMA.
Each ADC channel have a specific register to save the conversion result, so as i have 3 inputs i needed three DMA channels to transfer the result of each input to memory, then trigger an interrupt on one DMA channel and do the conversion from counts to voltage, like so:
This is the main loop:
#include "project.h"
#include <stdio.h>
#include <stdbool.h>
volatile bool data_ready = false;
enum {
ADC_CHANNEL_0 = 0,
ADC_CHANNEL_1 = 1,
ADC_CHANNEL_2 = 2,
};
enum {
FORM_FEED_CMD = 0x0C,
};
struct adc_result {
char string[20];
float32 volts;
int16_t counts;
} adc_result;
CY_ISR_PROTO(DMA_Handler);
int main(void)
{
struct adc_result ADC_Data[ADC_SEQUENCED_CHANNELS_NUM];
size_t string_size = sizeof(ADC_Data[ADC_CHANNEL_0]) / sizeof(char);
DMA_Chn0_SetInterruptCallback(&DMA_Handler);
CyIntEnable(CYDMA_INTR_NUMBER);
CyGlobalIntEnable;
UART_Start();
UART_PutString("Test ADC -> DMA -> memory\r\n");
PWM_Start();
ADC_Start();
DMA_Chn0_Start((void*) ADC_SAR_CHAN0_RESULT_PTR, &ADC_Data[ADC_CHANNEL_0].counts);
DMA_Chn1_Start((void*) ADC_SAR_CHAN1_RESULT_PTR, &ADC_Data[ADC_CHANNEL_1].counts);
DMA_Chn2_Start((void*) ADC_SAR_CHAN2_RESULT_PTR, &ADC_Data[ADC_CHANNEL_2].counts);
ADC_StartConvert();
while (1) {
if ( true == data_ready ) {
UART_PutChar(FORM_FEED_CMD);
for (uint8_t channel = 0; channel < ADC_SEQUENCED_CHANNELS_NUM; channel++) {
ADC_Data[channel].volts = ADC_CountsTo_Volts(channel, ADC_Data[channel].counts);
snprintf(ADC_Data[channel].string, string_size, "CH %d: %.3f Volts\r\n", channel, ADC_Data[channel].volts);
UART_PutString(ADC_Data[channel].string);
}
data_ready = false;
}
}
}
CY_ISR(DMA_Handler)
{
data_ready = true;
CyDmaClearInterruptSource(DMA_Chn0_CHANNEL_MASK);
}
/* [] END OF FILE */
The test project seems to work but is it the most efficient way to implement it?
May be a waste of DMA channels but i will try to port this project to the 4L family and send the ADC conversion results to the PC via USB, so i need to keep the infinite loop of the main function almost empty for USB transactions i guess (haven't did a USB project before ).
Any advice or feedback?
The project is attached, i used Creator 4.1
Carlos
PD: The new forum seems great
Solved! Go to Solution.
- Labels:
-
PSoC 4 Architecture
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Hi,
You could use a single DMA channel. The ADC results are sequentially placed in the memory. If you create an array with all the results, you can configure your DMA to increment the source and destination addresses after every transfer. Here is the summary:
- Source Address = SAR_CHAN_RESULT0;
- Destination Address = &MyResults[0]; // Declared as uint32 MyResults[3];
Regards,
Rodolfo Lossio
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Hey,
Using 3 channels out of the 32 available channels does not look like much of a wastage. As each channel has only 2 TD, this looks like a more elegant solution. Do check the example codes available on Creator before using USB, File -> Code Examples.
Best Regards,
VSRS
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Hi,
You could use a single DMA channel. The ADC results are sequentially placed in the memory. If you create an array with all the results, you can configure your DMA to increment the source and destination addresses after every transfer. Here is the summary:
- Source Address = SAR_CHAN_RESULT0;
- Destination Address = &MyResults[0]; // Declared as uint32 MyResults[3];
Regards,
Rodolfo Lossio
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Hi,
Thanks for the reply, i will try that and upload the project when i got it working.
I guess the destination address can be declared as uint16_t and configure the DMA to transfer from Word -> HalfWord.
Carlos
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Hi Carlos,
Yes, you can. Note that the the most significant bits from the SAR_CHAN_RESULT contains some useful information (maybe not for your application). It can tell:
- If the result is valid
- If the result is in range (based on the ADC limits defined in the component customizer)
- If the result is saturated
If you transfer from Word -> Halfword, you would loose that information.
Regards,
Rodolfo Lossio
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
rlos wrote:
Hi Carlos,
Yes, you can. Note that the the most significant bits from the SAR_CHAN_RESULT contains some useful information (maybe not for your application). It can tell:
- If the result is valid
- If the result is in range (based on the ADC limits defined in the component customizer)
- If the result is saturated
If you transfer from Word -> Halfword, you would loose that information.
Regards,
Rodolfo Lossio
Hi,
Actually i didn't knew that, i will need to take that in mind. I will update the project taking that "flags" to check for valid conversions.
Here's what i have so far:
The DMA Configuration:
And the main.c:
#include "project.h"
#include <stdio.h>
#include <stdbool.h>
volatile bool data_ready = false;
enum {
FORM_FEED_CMD = 0x0C,
};
struct adc_result {
char string[20];
float32 volts[ADC_SEQUENCED_CHANNELS_NUM];
int16_t counts[ADC_SEQUENCED_CHANNELS_NUM];
} adc_result;
CY_ISR_PROTO(DMA_Handler);
int main(void)
{
struct adc_result ADC_Data;
size_t string_size = sizeof(ADC_Data.string) / sizeof(char);
DMA_ADC_SetInterruptCallback(&DMA_Handler);
CyIntEnable(CYDMA_INTR_NUMBER);
CyGlobalIntEnable;
UART_Start();
UART_PutString("Test ADC -> DMA -> memory\r\n");
PWM_Start();
ADC_Start();
DMA_ADC_Start((void*) ADC_SAR_CHAN0_RESULT_PTR, &ADC_Data.counts);
ADC_StartConvert();
while (1) {
if ( true == data_ready ) {
UART_PutChar(FORM_FEED_CMD);
for (uint8_t channel = 0; channel < ADC_SEQUENCED_CHANNELS_NUM; channel++) {
ADC_Data.volts[channel] = ADC_CountsTo_Volts(channel, ADC_Data.counts[channel]);
snprintf(ADC_Data.string, string_size, "CH %d: %.3f Volts\r\n", channel, ADC_Data.volts[channel]);
UART_PutString(ADC_Data.string);
}
data_ready = false;
}
}
}
CY_ISR(DMA_Handler)
{
data_ready = true;
CyDmaClearInterruptSource(DMA_ADC_CHANNEL_MASK);
}
The project is attached, will update it with the "latest" info about the ADC conversion flags
Regards
Carlos
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
rlos wrote:
Hi Carlos,
Yes, you can. Note that the the most significant bits from the SAR_CHAN_RESULT contains some useful information (maybe not for your application). It can tell:
- If the result is valid
- If the result is in range (based on the ADC limits defined in the component customizer)
- If the result is saturated
If you transfer from Word -> Halfword, you would loose that information.
Regards,
Rodolfo Lossio
Just found the documentation about that on the ADC SAR Seq Datasheet
Just one more question, i was saving the ADC conversion results in a signed 16bits variable (int16_t), if i wanted to include the flag bits should i use a signed or unsigned 32bits variable? i think an unsigned 32bit variable should be the right one, but i'm not sure.
Regards
Carlos