ADC的連續轉換雜訊問題
使用的晶片為STM32L152編寫程式內容大略為:
ADC轉換→ADC_DR暫存→藉由DMA搬運到自訂的陣列中 →經由USART在PC端印出
(其中ADC藉由TIM3外部觸發,每儲存N筆轉換後進入DMA中斷並匯入資料)
附上程式碼
main.c
/* Includes ------------------------------------------------------------------*/
#include "stm32l1xx.h"
#include <stdio.h>
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
#define ADC1_DR_ADDRESS ((uint32_t)0x40012458)
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
volatile uint16_t ADC_values;
volatile uint8_t bufferIndex;
int32_t a=0;
__IO extern int16_t adcInt_O;
__IO extern int16_t adcInt_S;
__IO extern int16_t storetime;
__IO extern int8_t Flag;
int16_t adcInt_Buffer_O[800];
int16_t adcInt_Buffer_S[;
/* Private function prototypes -----------------------------------------------*/
void ADC_Configuration(void);
void DMA_Configuration(void);
void USART_Configuration(void);
void RCC_Configuration(void);
void NVIC_Configuration(void);
void TIM3_Configuration(void);
int main(void)
{
Flag =0;
RCC_Configuration();
TIM3_Configuration();
ADC_Configuration();
DMA_Configuration();
USART_Configuration();
while(1)
{
if(Flag ==1) /*匯入資料*/
{
Flag = 0;
for(a=0; a<800; a++ ) /*怕資料被覆蓋*/
{
adcInt_Buffer_O=adcInt_O[(bufferIndex-1) & 0x1];
adcInt_Buffer_S=adcInt_S[(bufferIndex-1) & 0x1];
}
for(a=0 ; a<800 ; a++)
{
printf("%d\r\n", adcInt_Buffer_O);
}
}
}
}
/* ADC1 channel18 configuration using DMA1 channel1 */
void ADC_Configuration(void)
{
/*----------------- ADC1 configuration with DMA enabled --------------------*/
GPIO_InitTypeDef GPIO_InitStructure;
ADC_InitTypeDef ADC_InitStructure;
/* Enable ADC1 clock */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE);
/* Configure PB.12 (ADC Channel18) in analog mode */
GPIO_StructInit(&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOB, &GPIO_InitStructure);
/* Configure PB.13 (ADC Channel19) in analog mode */
GPIO_StructInit(&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOB, &GPIO_InitStructure);
/* ADC1 configuration */
ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;
ADC_InitStructure.ADC_ScanConvMode = ENABLE;
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
ADC_InitStructure.ADC_ExternalTrigConvEdge=ADC_ExternalTrigConvEdge_Rising;
ADC_InitStructure.ADC_ExternalTrigConv=ADC_ExternalTrigConv_T3_CC1;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfConversion = 2; /*雙通道採集*/
ADC_Init(ADC1, &ADC_InitStructure);
/* ADC1 regular channel18 configuration */
ADC_RegularChannelConfig(ADC1, ADC_Channel_18, 1, ADC_SampleTime_384Cycles);
ADC_RegularChannelConfig(ADC1, ADC_Channel_19, 2, ADC_SampleTime_384Cycles);
/* Enable the request after last transfer for DMA Circular mode */
ADC_DMARequestAfterLastTransferCmd(ADC1, ENABLE);
/* Enable ADC1 */
ADC_Cmd(ADC1, ENABLE);
/* Enable ADC1 DMA */
ADC_DMACmd(ADC1, ENABLE);
}
void DMA_Configuration(void)
{
/*------------------------ DMA1 configuration ------------------------------*/
DMA_InitTypeDef DMA_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
/* Enable DMA1 clock */
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
/* DMA1 channel1 configuration */
DMA_DeInit(DMA1_Channel1);
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructure.DMA_BufferSize = ARRAYSIZE;
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)ADC1_DR_ADDRESS;
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)ADC_values;
DMA_Init(DMA1_Channel1, &DMA_InitStructure);
DMA_ITConfig(DMA1_Channel1, DMA_IT_TC, ENABLE);
/* Enable DMA1 channel1 */
DMA_Cmd(DMA1_Channel1, ENABLE);
/*Enable DMA1 channel IRQ Channel */
NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
void USART_Configuration(void)
{
USART_InitTypeDef USART_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource2, GPIO_AF_USART2);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource3, GPIO_AF_USART2);
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_40MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
GPIO_Init(GPIOA, &GPIO_InitStructure);
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_RTS;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(USART2, &USART_InitStructure);
USART_Cmd(USART2, ENABLE);
}
void RCC_Configuration(void)
{
/* Enable HSI Clock */
RCC_HSICmd(ENABLE);
/*!< Wait till HSI is ready */
while (RCC_GetFlagStatus(RCC_FLAG_HSIRDY) == RESET);
/* Set HSI as sys clock*/
RCC_SYSCLKConfig(RCC_SYSCLKSource_HSI);
/* Set MSI clock range to ~4.194MHz*/
RCC_MSIRangeConfig(RCC_MSIRange_6);
/* Enable the GPIOs clocks */
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA | RCC_AHBPeriph_GPIOB | RCC_AHBPeriph_GPIOC| RCC_AHBPeriph_GPIOD| RCC_AHBPeriph_GPIOE| RCC_AHBPeriph_GPIOH, ENABLE);
/* Enable comparator, LCD and PWR mngt clocks */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_COMP | RCC_APB1Periph_LCD | RCC_APB1Periph_PWR,ENABLE);
/* Enable ADC & SYSCFG clocks */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_SYSCFG , ENABLE);
/* Allow access to the RTC */
PWR_RTCAccessCmd(ENABLE);
/* Reset RTC Backup Domain */
RCC_RTCResetCmd(ENABLE);
RCC_RTCResetCmd(DISABLE);
/* LSE Enable */
RCC_LSEConfig(RCC_LSE_ON);
/* Wait until LSE is ready */
while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET);
/* RTC Clock Source Selection */
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);
/* Enable the RTC */
RCC_RTCCLKCmd(ENABLE);
/*Disable HSE*/
RCC_HSEConfig(RCC_HSE_OFF);
if(RCC_GetFlagStatus(RCC_FLAG_HSERDY) != RESET )
{
/* Stay in infinite loop if HSE is not disabled*/
while(1);
}
}
void TIM3_Configuration(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
/*PWM Output pin (PC6) for Timer 3, Channel 1 */
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOC, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_40MHz;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOC, &GPIO_InitStructure);
GPIO_PinAFConfig(GPIOC, GPIO_PinSource6, GPIO_AF_TIM3);
//#define SAMPLESPERSEC 4096
//#define PRESCALER SystemCoreClock / 1600000
//#define PERIOD 1600000 / SAMPLESPERSEC
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3 , ENABLE);
/* Time base configuration */
/*((1 + TIM Prescaler)/16M)*(1 + TIM Period)*/
TIM_TimeBaseStructure.TIM_Prescaler = (SystemCoreClock / 2000000) - 1;
TIM_TimeBaseStructure.TIM_Period = 1000 - 1;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
/* TIM PWM1 Mode configuration */
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = 5;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC1Init(TIM3, &TIM_OCInitStructure);
TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable);
TIM_SelectOutputTrigger(TIM3,TIM_TRGOSource_OC1);
TIM_ARRPreloadConfig(TIM3, ENABLE);
TIM_Cmd(TIM3, ENABLE);
}
int fputc(int ch, FILE *f)
{
USART_SendData(USART2, (unsigned char) ch);
while (USART_GetFlagStatus(USART2, USART_FLAG_TC) == RESET)
{
}
return ch;
}
stm32l1xx_it.c
extern volatile uint8_t bufferIndex;
int Flag;
extern int16_t a;
extern volatile uint16_t ADC_values;
__IO int16_t adcInt_O[1000][2];
__IO int16_t adcInt_S[1000][2];
__IO int16_t storetime;
__IO int16_t myindex=0;
void DMA1_Channel1_IRQHandler(void)
{
if(DMA_GetITStatus(DMA1_IT_TC1)==SET)
{
DMA_ClearITPendingBit(DMA1_IT_TC1);
for(myindex=0 ; myindex<1600; myindex+=2)
{
adcInt_O = ADC_values;
adcInt_S = ADC_values;
storetime++;
if (storetime == 800)
{
storetime=0;
bufferIndex = (bufferIndex+1) & 0x1;
Flag = 1;
}
}
}
}
已以上程式為例
每1ms進行一次ADC轉換,每次轉換輪尋2個通道並經由DMA存入陣列,故800ms後陣列存入1600筆資料並進入DMA中斷
DMA中斷程式中將資料存入緩衝陣列(怕被覆蓋),全部存完後設置旗標(Flag=1)
Main函示判斷(Flag)旗標為貞開始進行Printf動作
實驗結果
第一輪資料轉換(只抓一個通道來觀察,故為800筆資料)
https://www.stmcu.org.cn/module/forum/forum.php?mod=image&aid=400827&size=300x300&key=7a16112e5e6286f2&nocache=yes&type=fixnone
第二輪資料轉換
https://www.stmcu.org.cn/module/forum/forum.php?mod=image&aid=400828&size=300x300&key=ff722c8be62524b8&nocache=yes&type=fixnone
N輪轉換
https://www.stmcu.org.cn/module/forum/forum.php?mod=image&aid=400829&size=300x300&key=0cf126db0f60f2c5&nocache=yes&type=fixnone
看起來是前段的資料被覆蓋了,但實際運算個城市的執行時間後卻又是足夠的
目前僅發現更改包率可以減少出問題的資料數
台湾人?还有现在ST这种外设配置,请使用Cube。。。妥妥的。。。 台湾人?还有现在ST这种外设配置,请使用Cube。。。妥妥的。。。 本帖最后由 wenyangzeng 于 2017-10-5 17:45 编辑
STM32L152主频只有30MHZ,楼主的ADC采样周期又设置达384Cycles之长。加之需要进行串口通信,可能超时了。降低ADC采样个数试看看吧!还有DMA只收集2次ADC数据很不划算,为何不对2通道尽量采样多次后一次性处理数据呢?
wenyangzeng 发表于 2017-10-5 13:26
STM32L152主频只有30MHZ,楼主的ADC采样周期又设置达384Cycles之长。加之需要进行串口通信,可能超时了。降 ...
你好!
我的程式
DMA_InitStructure.DMA_BufferSize = ARRAYSIZE;//ARRAYSIZE = 1600
此程式在雙通道採集到1600筆資料後才進DMA中斷處理資料
樓主的对2通道尽量采样多次是這個意思嗎?
小豚豚 发表于 2017-10-17 15:59
你好!
我的程式
是的,如果RAM足够,DMA应该一次足够多数据后才传送到串口。 HSI Clock这就决定了你的ADC有很高的相位噪声。
至于数据堆在一起,明显是你的绘图软件的问题,把前后数据画了堆在一起。
kylongmu 发表于 2017-10-18 00:10
HSI Clock这就决定了你的ADC有很高的相位噪声。
至于数据堆在一起,明显是你的绘图软件的问题,把前后数据 ...
數據的產生方式為使用Putty將資料輸出到excel程式中並繪圖
數據異常的點發生在:
第一次ADC採集1600筆資料>經DMA>儲存至自己的Buffer陣列>輸出資料 (第一資料皆正常)
第二次ADC採集1600筆資料(感覺第二次的ADC轉換資料前幾筆會有附蓋問題)>經DMA>儲存至自己的Buffer陣列>輸出資料(因此輸出的資料也跟著不正確)
页:
[1]