cancel
Showing results for 
Search instead for 
Did you mean: 

Anomalous high ADC rate at stm32f103cb

kudryashovda
Associate II
Posted on March 11, 2016 at 21:02

Hello! I need an advice. My stm32f103cb chip has ADC with 0.39 us per point conversion rate on one channel. But datasheet says its max rate is 1 Msps (1us). I checked sampling rate with external oscillograph (1 Gsps Tektronics) and external signal generator (Tektronics). I sent to ADC square wave with width of 10 us ON and of 10 us OFF (0-3V) and recieve with my delphi programm also square signal with flat level ON and OFF for 26 points of each. Also I was able to view sine wave signal up to 200 kHz (0-3V) with good resolution. The code is below. The basis was USB-CDC loop example from latest stmm32f1xx perif library. On EP3 event my parser starts GetData function where firstly resets DMA to set Memory base address to start of data array and then starts ADC conversion with direct DMA to record adc data to my array [992 elements] (dma mode is normal, not cyclic). After transfer is done - all data sends to usb-com and my delphi programm recieve it and plots a graph.

The code:

// Variant with 0.39 us sampling rate - read freq up to 20o kHz 
#include ''hw_config.h''
#include ''usb_lib.h''
#include ''usb_desc.h'' //
#include ''usb_pwr.h'' //
#define ADC1_DR_Address ((uint32_t)0x4001244C)
uint32_t packet_sent=1;
uint32_t packet_receive=1;
extern
__IO uint8_t Receive_Buffer[64];
extern
__IO uint32_t Receive_length ;
uint16_t ADC_Buffer[992];
uint8_t TxBuffer[64];
uint32_t i,j,d;
GPIO_InitTypeDef GPIO_InitStructure;
ADC_InitTypeDef ADC_InitStructure;
DMA_InitTypeDef DMA_InitStructure;
void
ADC_settings(
void
); 
void
parser(
void
); 
// need to declare
void
getFirmware(
void
);
void
GetData(
void
);
void
SendError(
void
);
void
delay(uint32_t delayValue);
void
init_pwm(
void
);
int
main()
{
init_pwm();
Set_System();
Set_USBClock();
USB_Interrupts_Config();
USB_Init();
ADC_settings();
while
(1) { }
}
void
ADC_settings() {
/* ADCCLK = PCLK2/4 */
RCC_ADCCLKConfig(RCC_PCLK2_Div4); 
/* Enable DMA1 clock */
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
/* Enable ADC1 and GPIOC clock */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_GPIOA, ENABLE);
/* Configure PA.01 (ADC Channel_1) as analog input -------------------------*/
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* DMA1 channel1 configuration ----------------------------------------------*/
DMA_DeInit(DMA1_Channel1);
DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address;
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&ADC_Buffer[0];
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructure.DMA_BufferSize = 992;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(DMA1_Channel1, &DMA_InitStructure);
/* Enable DMA1 channel1 */
DMA_Cmd(DMA1_Channel1, ENABLE); 
/* ADC1 configuration ------------------------------------------------------*/
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
ADC_InitStructure.ADC_ScanConvMode = ENABLE;
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfChannel = 1;
ADC_Init(ADC1, &ADC_InitStructure);
/* ADC1 regular channel1 configuration */
//- PIN10 Mapple Mini
ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 1, ADC_SampleTime_1Cycles5);
/* Enable ADC1 DMA */
ADC_DMACmd(ADC1, ENABLE);
/* Enable ADC1 */
ADC_Cmd(ADC1, ENABLE);
/* Enable ADC1 reset calibration register */
ADC_ResetCalibration(ADC1);
/* Check the end of ADC1 reset calibration register */
while
(ADC_GetResetCalibrationStatus(ADC1));
/* Start ADC1 calibration */
ADC_StartCalibration(ADC1);
/* Check the end of ADC1 calibration */
while
(ADC_GetCalibrationStatus(ADC1)); 
}
void
parser() {
// executes at EP3 irq at incoming packet in file usb_endp.c
if
( Receive_Buffer[0] == 0xAA) {
switch
( Receive_Buffer[1] )
{
case
0xCB: 
getFirmware();
break
;
case
0x01:
GetData();
break
; 
default
:
break
;
}
} 
else
{
SendError();
}
packet_receive = 0;
}
void
getFirmware() {
TxBuffer[0] = 0xAA;
TxBuffer[1] = 0xBC; 
CDC_Send_DATA ((unsigned 
char
*)TxBuffer,2); 
}
void
SendError() {
TxBuffer[0] = 0xAA;
TxBuffer[1] = 0xEE;
CDC_Send_DATA ((unsigned 
char
*)TxBuffer,2); 
}
void
GetData() {
DMA_Cmd(DMA1_Channel1, DISABLE); 
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&ADC_Buffer[0]; 
DMA_Init(DMA1_Channel1, &DMA_InitStructure); 
DMA_Cmd(DMA1_Channel1, ENABLE);
ADC1->CR2 |= ((uint32_t)0x00500000); 
// start ADC 
while
(!DMA_GetFlagStatus(DMA1_FLAG_TC1));
ADC1->CR2 &= ((uint32_t)0xFFAFFFFF); 
// stop ADC
DMA_ClearFlag(DMA1_FLAG_TC1);
for
(j=0;j<32;j++) { 
for
(i=0;i<31;i++) {
TxBuffer[i*2] = (ADC_Buffer[i+31*j] & 0x0000FF00) >> 8;
TxBuffer[i*2+1] = (ADC_Buffer[i+31*j] & 0x000000FF); 
}
CDC_Send_DATA ((unsigned 
char
*)TxBuffer,62); 
// 63 bytes max!
delay(10000);
}
}
void
delay(uint32_t delayValue) {
for
( d = 0 ; d < delayValue ; d++) {};
}
void
init_pwm() {
GPIO_InitTypeDef pwmGPIO; 
TIM_TimeBaseInitTypeDef pwm;
TIM_OCInitTypeDef pwmOC;
// enable
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
// init gpio
pwmGPIO.GPIO_Pin = GPIO_Pin_0;
pwmGPIO.GPIO_Mode = GPIO_Mode_AF_PP;
pwmGPIO.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &pwmGPIO);
// init TIM3 ch.3 - PIN3 Mapple Mini
/* Time base configuration */
pwm.TIM_Prescaler = 36 - 1;
pwm.TIM_Period = 19; 
// 0 - also 1 tick - now 50 us ON and 50 us OFF
pwm.TIM_ClockDivision = 0;
pwm.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM3, &pwm);
// for 3600-1 and 2000 and 50 = T=1sec Pulse = 25 ms
/* PWM1 Mode configuration: Channel3 */
pwmOC.TIM_OCMode = TIM_OCMode_PWM1;
pwmOC.TIM_OutputState = TIM_OutputState_Enable; 
// ON first
pwmOC.TIM_Pulse = 10; 
// go to 50 pulses and LED OFF
pwmOC.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC3Init(TIM3, &pwmOC);
TIM_OC3PreloadConfig(TIM3, TIM_OCPreload_Enable);
TIM_ARRPreloadConfig(TIM3, ENABLE);
/* TIM3 enable counter */
TIM_Cmd(TIM3, ENABLE);
}
#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\r\n'', file, line) */
/* Infinite loop */
while
(1)
{
}
}
#endif
/**
* @}
*/
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

#stm32f103cb #adc #adc #stm32f103cb
6 REPLIES 6
kudryashovda
Associate II
Posted on March 12, 2016 at 10:34

Uploaded some photos to my previous post.

A little comments. I started TIM3 timer inside code to generate square pulses with 5 us width:

....
pwm.TIM_Prescaler = 36 - 1;
pwm.TIM_Period = 19; 
....
pwmOC.TIM_Pulse = 10;

and connected TIM output via wire to adc input. My home DSO shows 5 us pulses and in my Delphi programm I see 13 points per pulse = 5 / 13 = 0.38 us resolution 0690X000006034VQAQ.jpg 0690X0000060382QAA.jpg 0690X0000060387QAA.jpg
Posted on March 12, 2016 at 13:24

I'm pretty sure it is not operating at 0.39 us, looking at the configuration the 18 MHz ADC clock should deliver 0.777 us. The 1us (1MHz) would come if clocking at 14 MHz, the supposed max rate of the F1 ADC.

To measure sample rate, I'd generally prefer to do 1000 samples with circular DMA, and toggle a GPIO in the DMA TC, and scope that.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
kudryashovda
Associate II
Posted on March 12, 2016 at 18:39

Thanks clive to reply!

Here is my test with toggle pin after cyclic DMA transfer of 1000 bytes fron ADC 0690X00000602rvQAA.jpg The time of one transfer if about 400 us so 1 sampling is 400 us / 1000 = 0.4 us. The code is here:

// test for 1000 ADC sampling via DMA, after TC toggle Pin 
#include ''hw_config.h''
#include ''usb_lib.h''
#include ''usb_desc.h'' 
#include ''usb_pwr.h'' 
#define ADC1_DR_Address ((uint32_t)0x4001244C)
uint32_t packet_sent=1;
uint32_t packet_receive=1;
extern
__IO uint8_t Receive_Buffer[64];
extern
__IO uint32_t Receive_length ;
uint16_t ADC_Buffer[1000];
GPIO_InitTypeDef GPIO_InitStructure;
ADC_InitTypeDef ADC_InitStructure;
DMA_InitTypeDef DMA_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
void
ADC_DMA_GPIO_NVIC_settings(
void
); 
void
TogglePin(
void
);
void
parser(
void
); 
// need to declare
int
main()
{
Set_System();
Set_USBClock();
USB_Interrupts_Config();
USB_Init();
ADC_DMA_GPIO_NVIC_settings();
while
(1) {}
}
void
DMA1_Channel1_IRQHandler(
void
)
{ 
if
(DMA_GetITStatus(DMA1_IT_TC1)) 
//Test on DMA1 Channel1 Transfer Complete interrupt
{
TogglePin();
DMA_ClearITPendingBit(DMA1_IT_GL1);
}
}
void
TogglePin(
void
) {
if
(!GPIO_ReadOutputDataBit(GPIOA, GPIO_Pin_7)) {
GPIO_SetBits(GPIOA, GPIO_Pin_7); }
else
{
GPIO_ResetBits(GPIOA, GPIO_Pin_7); }
}
void
ADC_DMA_GPIO_NVIC_settings(
void
) {
/* ADCCLK = PCLK2/4 */
RCC_ADCCLKConfig(RCC_PCLK2_Div4); 
/* Enable DMA1 clock */
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
/* Enable ADC1 and GPIOA clock */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_GPIOA, ENABLE);
/* Configure PA.01 (ADC Channel_1) as analog input -------------------------*/
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* Configure PA.07 as GPIO output of maple mini clone D4 as a test pin for DSO -*/
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* DMA1 channel1 configuration ----------------------------------------------*/
DMA_DeInit(DMA1_Channel1);
DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address;
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&ADC_Buffer[0];
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructure.DMA_BufferSize = 1000;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(DMA1_Channel1, &DMA_InitStructure);
// Enable DMA1 Channel Transfer Complete interrupt
DMA_ITConfig(DMA1_Channel1, DMA_IT_TC, ENABLE);
//Enable DMA1 channel IRQ Channel */
NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure); 
/* Enable DMA1 channel1 */
DMA_Cmd(DMA1_Channel1, ENABLE); 
/* ADC1 configuration ------------------------------------------------------*/
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
ADC_InitStructure.ADC_ScanConvMode = ENABLE;
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfChannel = 1;
ADC_Init(ADC1, &ADC_InitStructure);
/* ADC1 regular channel1 configuration */
//- PIN10 Mapple Mini
ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 1, ADC_SampleTime_1Cycles5);
/* Enable ADC1 DMA */
ADC_DMACmd(ADC1, ENABLE);
/* Enable ADC1 */
ADC_Cmd(ADC1, ENABLE);
ADC_ResetCalibration(ADC1);
while
(ADC_GetResetCalibrationStatus(ADC1));
ADC_StartCalibration(ADC1);
while
(ADC_GetCalibrationStatus(ADC1)); 
ADC1->CR2 |= ((uint32_t)0x00500000); 
// start ADC
}
void
parser() {}

One interesting thing I found - if to comment these calls:

Set_System();
Set_USBClock();
USB_Interrupts_Config();
USB_Init();

then the results is: 0690X0000060388QAA.jpg 800 us / 1000 samples = 0.8 us that is close to 1Msps. So USB settings change something to increase ADC? Or maybe here is something that I missed?
Posted on March 12, 2016 at 21:38

I think you'd want to sanity check the speed at which the processor and buses are running.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
TDK
Guru
Posted on March 12, 2016 at 22:44

What are you doing inside 

Set_USBClock that is changing the clocks?  I'd look at the clock settings after you're done all of the intiailziation and verify they are as expected.  Clearly something is amiss if you comment those out and the behavior changes.

If you feel a post has answered your question, please click "Accept as Solution".
kudryashovda
Associate II
Posted on March 13, 2016 at 11:26

These usb functions are come with CDC USB example for stm32f1xx perif lib. And they all must be enabled. If anyone of them to comment then ADC DMA transfer will be ''Normal''.

Set_System() only setups usb disconect pin andEXTI_Line18 Set_USBClock() has two commands:

/* Select USBCLK source */
RCC_USBCLKConfig(RCC_USBCLKSource_PLLCLK_1Div5); 
// div 1.5
/* Enable the USB clock */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USB, ENABLE);

Clock tree for f103 shows that usb block (prescaler) is connected to PLL directly

USB_Interrupts_Config

()enables the USB interruptandthe USB Wake-up interrupt USB_Init() makes a magic. Withcommand pProperty->Init() it executesVirtual_Com_Port_init() where after PowerOn() command my DMA transter executes two times faster. This function sets a number of interrups that out of my mind:

RESULT PowerOn(
void
)
{
uint16_t wRegVal;
/*** cable plugged-in ? ***/
USB_Cable_Config(ENABLE);
/*** CNTR_PWDN = 0 ***/
wRegVal = CNTR_FRES;
_SetCNTR(wRegVal);
/*** CNTR_FRES = 0 ***/
wInterrupt_Mask = 0;
_SetCNTR(wInterrupt_Mask);
/*** Clear pending interrupts ***/
_SetISTR(0);
/*** Set interrupt mask ***/
wInterrupt_Mask = CNTR_RESETM | CNTR_SUSPM | CNTR_WKUPM;
_SetCNTR(wInterrupt_Mask);
return
USB_SUCCESS;
}

After this function goes USB_SIL_Init(); that perform basic device initialization operations. If it comment then ''fast'' ADC DMA will be only 5 seconds, after it windows shows ''Unrecognized usb device'' and ADC DMA transfer goes ''Normal''