cancel
Showing results for 
Search instead for 
Did you mean: 

CPU,DMA UART Transfer Independently

manoritesameer
Associate II
Posted on June 05, 2014 at 22:21

Hello,

       I want to transmit 10000 samples taken by adc continuously stored in an array. Is it possible the DMA-UART can transmit this to the PC while the CPU can take the next set of samples(independently) i.e without slowing the CPU.

#fast-dma #stm-rocket-science
14 REPLIES 14
Posted on June 06, 2014 at 01:16

Yes, the real issue is if you have a significant disparity in the rate at which you are capturing data, and the rate at which you can send it via the serial port.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
jpeacock2399
Associate II
Posted on June 06, 2014 at 16:10

This is a common design pattern, what's called a ''ping pong'' buffer.  Look at the HT half transfer interrupt (the ping buffer) on the ADC DMA controller.  This signals when half the ADC samples are complete, at which point the contents can safely be sent to the serial port.  An HT and TC signals the second half (pong buffer) is filled, at which point (assuming the ADC and DMA are set to circular mode) the DMA will switch back to filling the first half of the buffer.  Transfers bounce back and forth between the two halves like a ping pong ball.

The trick is to ensure data is extracted from the buffer faster than the ADC conversion ate, otherwise you will quickly overflow and lose data.  For 10K per window and 12 bit samples (2 bytes) you need to send to the USART at >20K bytes (200Kbits for 8-1-N) per window.

If your window is 1 second, 20K samples/sec, then the USART baud rate would have to be over 200Kbit/sec, nearest common rate would be 230Kbaud.  Your external equipment must be able to accept data at that rate non-stop.

If you are using async RS-232 for the USART there is a second problem dealing with high baud rates.  Make sure your USART baud rate generator is crystal derived and exactly matches the baud rate.  Same with the external equipment, make sure there's no deviation from the baud rate (a 1 percent deviation at sustained high data rates will fail).

What happens is that, since there are no gaps between bytes in the serial stream, the RX sampling only has one bit time (the stop bit) to reset and begin sampling for the next start bit.  Any deviation in rates between sender and receiver will cause the start bit to drift lower than the sample threshold, dropping data.

And last is the slew rate for RS-232 at those speeds.  It might work if the devices are close together.

And yes it really does happen.  230Kbaud on one device is not the same as another.  Data does get lost for no apparent reason.

  Jack Peacock
manoritesameer
Associate II
Posted on June 06, 2014 at 20:14

Thanks for you valuable suggestions... I solved the problem. There is one interrupt that takes care of sampling and the DMA takes care of the transfer of the Data. Once all the samples are taken the UART-DMA is initialized in normal mode and once the transfer is complete the samples are over written with new ones.

Thanks Clive one of ur older post helped me do this. Just a thought if I have to transfer set of Data as combination of strings , 2D Array (int type) how would I do that.

Regards

Sameer    

Posted on June 06, 2014 at 20:27

Thanks Clive one of ur older post helped me do this. Just a thought if I have to transfer set of Data as combination of strings , 2D Array (int type) how would I do that.

I think you need to be specific about the form you want to send the data in. The most efficient would be to send it as a binary blob, basically a copy directly out of memory. Either in a stream, or packetize form, or using a protocol like X-Modem. This data copied into a similarly configured array on a PC application you then print or process the data there.

The word ''string'' however suggests you want it printed in an ASCII form (like a comma delimited CSV file?), which will take significantly more effort to convert and send.
Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
manoritesameer
Associate II
Posted on June 06, 2014 at 21:04

I want to transfer information like this

Aarray

10100 000 9 9000 0000

Barray

100 90 990 990 

Here is ur older post that helped me figure it out 

ur last post u have used three strings

https://my.st.com/public/STe2ecommunities/mcu/Lists/cortex_mx_stm32/Flat.aspx?RootFolder=/public/STe2ecommunities/mcu/Lists/cortex_mx_stm32/DMA%20Memory%20To%20UART5&FolderCTID=0x01200200770978C69A1141439FE559EB459D7580009C4E14902C3CDE46A77F0FFD06506F5B&currentviews=760

Thanks 

Sameer

Posted on June 07, 2014 at 00:39

[DEAD LINK /public/STe2ecommunities/mcu/Lists/cortex_mx_stm32/Flat.aspx?RootFolder=/public/STe2ecommunities/mcu/Lists/cortex_mx_stm32/transmitting%20array%20of%20integers%20over%20UART%20using%20DMA&FolderCTID=0x01200200770978C69A1141439FE559EB459D7580009C4E14902C3CDE46A77F0FFD06506F5B&TopicsView=https://my.st.com/public/STe2ecommunities/mcu/Lists/cortex_mx_stm32/AllItems.aspx&currentviews=1]Cross posting

Ok, but understand that converting to ASCII decimal is not computationally efficient and burns a lot of bandwidth. A 16-bit integer sent as two 8-bit binary bytes, vs 2-6 ASCII characters.

Unless there is a human readability issue with the transfer, I'd DMA blocks of binary bytes.

If you must convert to ASCII and use DMA, then perhaps do 100 at a time, doing at itoa() equivalent into a buffer of 600 byte max capacity, when generated light off a DMA transfer for the bytes creates, and have the DMA TC interrupt set up to craft the next block (or have a chain of blocks ready to go).

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
manoritesameer
Associate II
Posted on June 07, 2014 at 08:42

thanks clive,

        I have no problem transferring data as binary form, but I dont know how would I do that. Can u refer me to some example.

As I told you before I have an adc that takes samples and stores the value in a buffer, all the values are in the form of decimal number. I want the DMA to transfer this data over UART. 

Thanks 

manoritesameer
Associate II
Posted on June 07, 2014 at 08:43

thanks clive,

        I have no problem transferring data as binary form, but I dont know how would I do that. Can u refer me to some example.

As I told you before I have an adc that takes samples and stores the value in a buffer, all the values are in the form of decimal number. I want the DMA to transfer this data over UART. 

Thanks 

Posted on June 07, 2014 at 14:38

I'm not sure you fully grasp the 16-bit binary in a machine readable form, and a decimal number in a human readable form. The former is just a block of memory &array, sizeof(array) which can be handed to the DMA controller. You should examine the memory representation of your data, and how that could be passed from machine to machine without going through a human centric decimal ASCII representation.

Here's a quick blind demo of sending decimal ASCII formatted strings with DMA, the transfer length is variable.

// STM32 USART6 DMA TX DECIMAL (Tx PC.6, Rx PC.7) STM32F4DIS-BB - sourcer32@gmail.com
#include ''stm32f4_discovery.h''
#include <
string.h
>
/**************************************************************************/
void RCC_Configuration(void)
{
/* --------------------------- System Clocks Configuration -----------------*/
/* USART6 clock enable */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART6, ENABLE);
/* GPIOC clock enable */
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);
/* DMA2 clock enable */
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);
}
/**************************************************************************/
void GPIO_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
/*-------------------------- GPIO Configuration ----------------------------*/
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7; // PC.6 USART6_TX, PC.7 USART6_RX
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_Init(GPIOC, &GPIO_InitStructure);
/* Connect USART pins to AF */
GPIO_PinAFConfig(GPIOC, GPIO_PinSource6, GPIO_AF_USART6);
GPIO_PinAFConfig(GPIOC, GPIO_PinSource7, GPIO_AF_USART6);
}
/**************************************************************************/
void USART6_Configuration(void)
{
USART_InitTypeDef USART_InitStructure;
/* USARTx configuration ------------------------------------------------------*/
/* USARTx configured as follow:
- BaudRate = 115200 baud
- Word Length = 8 Bits
- One Stop Bit
- No parity
- Hardware flow control disabled (RTS and CTS signals)
- Receive and transmit enabled
*/
USART_InitStructure.USART_BaudRate = 115200;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(USART6, &USART_InitStructure);
USART_Cmd(USART6, ENABLE);
/* Enable the USART Tx DMA request */
USART_DMACmd(USART6, USART_DMAReq_Tx, ENABLE);
}
/**************************************************************************/
// 16-bit to ASCII Decimal, no division/modulus, all 32-bit int math
int rapid16u(char *p, int index, unsigned int value) // sourcer32@gmail.com
{
int a;
int out = 0;
p += index;
if (value >= 10000) // Tens of Thousands
{
a = 0x30; // ASCII '0'
if (value >= 40000) { value -= 40000; a += 4; } // Start with 40000 as 16-bit can't be 80000
if (value >= 20000) { value -= 20000; a += 2; }
if (value >= 10000) { value -= 10000; a += 1; }
p[out++] = (char)a;
}
if (out || (value >= 1000)) // Thousands
{
a = 0x30; // ASCII '0'
if (value >= 8000) { value -= 8000; a += 8; }
if (value >= 4000) { value -= 4000; a += 4; }
if (value >= 2000) { value -= 2000; a += 2; }
if (value >= 1000) { value -= 1000; a += 1; }
p[out++] = (char)a;
}
if (out || (value >= 100)) // Hundreds
{
a = 0x30; // ASCII '0'
if (value >= 800) { value -= 800; a += 8; }
if (value >= 400) { value -= 400; a += 4; }
if (value >= 200) { value -= 200; a += 2; }
if (value >= 100) { value -= 100; a += 1; }
p[out++] = (char)a;
}
if (out || (value >= 10)) // Tens
{
a = 0x30; // ASCII '0'
if (value >= 80) { value -= 80; a += 8; }
if (value >= 40) { value -= 40; a += 4; }
if (value >= 20) { value -= 20; a += 2; }
if (value >= 10) { value -= 10; a += 1; }
p[out++] = (char)a;
}
// Units
a = 0x30; // ASCII '0'
if (value >= 8) { value -= 8; a += 8; }
if (value >= 4) { value -= 4; a += 4; }
if (value >= 2) { value -= 2; a += 2; }
if (value >= 1) { value -= 1; a += 1; }
p[out++] = (char)a;
return(index + out);
}
/**************************************************************************/
int rapid16i(char *p, int index, int value)
{
if (value < 
0
) // Negative?
{
p[index++] = '-';
value = -value;
}
return(rapid16u(p, index, (unsigned int)value));
}
/**************************************************************************/
uint16_t 
Counter
= 
0
;
char String[64]; // enough to hold 10 16-bit integers in ASCII, spaces and CR/LF
void DMA_Configuration(void)
{
DMA_InitTypeDef DMA_InitStructure;
int 
Index
= 
0
;
int Count;
// USART6_TX DMA Channel 5, DMA2, Stream7 (alt Stream6 potential clash SDIO)
for(
Count
=
0
; Count<10; Count++) // integers in line
{
if (Count)
String[Index++] = ' '; // Separate numbers with spaces
Index
= 
rapid16u
(String, Index, (unsigned int)Counter++); // Convert to ASCII decimal
}
String[Index++] = '
'; // Add a CR & LF for the line
String[Index++] = '
';
DMA_DeInit(DMA2_Stream7);
DMA_InitStructure.DMA_Channel
= 
DMA_Channel_5
;
DMA_InitStructure.DMA_DIR
= 
DMA_DIR_MemoryToPeripheral
; // Transmit
DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)String;
DMA_InitStructure.DMA_BufferSize = (uint16_t)Index; // Length as constructed
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&USART6->DR;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Enable;
DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
DMA_Init(DMA2_Stream7, &DMA_InitStructure);
/* Enable DMA Stream Transfer Complete interrupt */
DMA_ITConfig(DMA2_Stream7, DMA_IT_TC, ENABLE);
/* Enable the DMA TX Stream */
DMA_Cmd(DMA2_Stream7, ENABLE);
}
/**************************************************************************/
void DMA2_Stream7_IRQHandler(void) // USART6_TX
{
/* Test on DMA Stream Transfer Complete interrupt */
if (DMA_GetITStatus(DMA2_Stream7, DMA_IT_TCIF7))
{
/* Clear DMA Stream Transfer Complete interrupt pending bit */
DMA_ClearITPendingBit(DMA2_Stream7, DMA_IT_TCIF7);
DMA_Configuration(); // Light off next string
}
}
/**************************************************************************/
void NVIC_Configuration(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
/* Configure the Priority Group to 2 bits */
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
/* Enable the USART6 TX DMA Interrupt */
NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream7_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
/**************************************************************************/
int main(void)
{
RCC_Configuration();
NVIC_Configuration();
GPIO_Configuration();
USART6_Configuration();
DMA_Configuration();
while(1); // Don't want to exit
}
/**************************************************************************/
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t* file, uint32_t line)
{
/* User can add his own implementation to report the file name and line number,
ex: printf(''Wrong parameters value: file %s on line %d

'', file, line) */
while (1)
{}
}
#endif
/**************************************************************************/

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..