LED數(shù)碼管或者LCD12864等顯示器在顯示溫度采樣值時(shí)檩奠,需要的是穩(wěn)定的能反映溫度實(shí)際變化的顯示效果。ADC采樣頻率快笆凌,直接采樣輸出的值往往有不小的跳變,這時(shí)候需要對(duì)其數(shù)據(jù)做一些數(shù)字濾波處理送悔。常用的方法有:平均值法爪模、多次采樣用冒泡法取中間一段數(shù)值法、平滑平均線法屋灌。冒泡法處理時(shí)間太長(zhǎng),這里主要用了平均值和平滑平均線兩種方法一起處理祠丝。
一除嘹、算法部分
平滑平均線法可解決一般均值法的欠靈敏和滯后性,同時(shí)濾除毛刺叠蝇。用此法對(duì)ADC采集的數(shù)據(jù)進(jìn)行ADC濾波后年缎,在LCD12864上顯示穩(wěn)定。
具體做法如下:
- 在一個(gè)ADC通道上采集多次单芜,獲得采樣平均值a蜕该,用a初始化含有假定100個(gè)數(shù)據(jù)的數(shù)組b[100]中所有的數(shù)據(jù)(這樣b[]數(shù)組的平均值就是a)缓溅;
- 后續(xù)每次運(yùn)行一次ADC采樣都會(huì)得到一個(gè)新的采樣平均值A(chǔ),同時(shí)把數(shù)組b的數(shù)據(jù)左移(b[0]=b[1]~~~b[98]=b[99]),然后把A的值賦值給b[99]淤齐;
- 如此每次運(yùn)行一次采樣就會(huì)平滑的更新一次數(shù)組b的平均值B袜匿,也就得到了數(shù)字濾波后的平滑平均值B,平均值B就可以拿去做顯示祭务,效果穩(wěn)定;
- 實(shí)際程序處理中是:第一次得到的采樣平均值A(chǔ)0賦值給b[0]义锥,如此順序,第100次采樣得到的平均值A(chǔ)99賦值給b[99]赂鲤,之后又重新依次更新b[0]到b[99]的值柱恤,如此循環(huán),這樣做可以減少運(yùn)算量泡孩,同時(shí)達(dá)到左移的效果
平滑平均值代碼
/**
* @brief ADC采樣所得到的溫度
* @param None
* @retval : None
*/
void ADC1_Tempera(void)
{
u32 i,k,m,n;
static u32 ValueCount;
for(i=0;i<ADC_ChannelCount;i++)
{
/*
*@平均值濾波
*/
ValueCount=0;
for(k=0;k<ADC1_Count;k++)//取中間的100次值(ADC1_Count)
{
ValueCount+=ADGetValue[k][i];
ADC_Filter[i]=(ValueCount/ADC1_Count);//求取一個(gè)ADC通道的平均值
}
if(Tflag==0)
{
for(n=0;n<SmoothNum;n++)
{
ADC_SmoothFilter[i][n]=ADC_Filter[i];//獲得第一次平滑值
}
ADC_SmoothCount[i]=ADC_Filter[i];//獲取第一次平滑平均值線
}
/*
*@平滑濾波
*/
if(Tflag==1)
{
ADC_SmoothFilter[i][Smoth_Num[i]]=ADC_Filter[i];//把數(shù)值循環(huán)放置
Smoth_Num[i]++;
if(Smoth_Num[i]==SmoothNum)
Smoth_Num[i]=0;
for(n=0;n<SmoothNum;n++)//平滑濾波
{
ADC_SmoothCount[i] += ADC_SmoothFilter[i][n];
}
ADC_SmoothCount[i]=(ADC_SmoothCount[i]/SmoothNum);//獲取平滑平均值線
}
}
for(m=0;m<3;m++)
{
TMonitor[m]=(((2361*ADC_SmoothCount[2*m])/(ADC_SmoothCount[2*m+1])-2597)+TemperaSet[m+4]);//已經(jīng)擴(kuò)大10倍的值轉(zhuǎn)化為溫度數(shù)據(jù),低溫,高溫,環(huán)境溫度 TemperaSet[m+4]為溫度補(bǔ)償系數(shù)
if(Tflag==0)
TMonitorDis[m]=TMonitor[m];
/*
*@顯示處理緩慢變化
*/
if(TMonitorDis[m]<(TMonitor[m]-1))
TMonitorDis[m] += 1;
if(TMonitorDis[m]>(TMonitor[m]+1))
TMonitorDis[m] -= 1;
}
Tflag=1;
}
代碼注解
- ADC_ChannelCount為采樣通道數(shù)仑鸥,其值為6变屁,表示ADC1有6個(gè)采樣通道。
- ADC1_Count為每一個(gè)通道在執(zhí)行一次采樣函數(shù)中需要采樣的次數(shù),此值為100摄职。
- ADGetValue[ADC1_Count][ADC_ChannelCount]數(shù)組內(nèi)存中存儲(chǔ)的是6個(gè)通道中每個(gè)通道的100個(gè)數(shù)據(jù),即每執(zhí)行一次函數(shù)void ADC1_Tempera(void)就更新一次這段內(nèi)存的所有數(shù)據(jù)蛔垢。
- 批量采樣工作過(guò)程是:定時(shí)器觸發(fā)一次后依次采樣通道(1-2-3-4-5-6)迫悠,第二次定時(shí)器觸發(fā)后依次采樣通道(1-2-3-4-5-6),如此連續(xù)轉(zhuǎn)換艺玲,循環(huán)掃描鞠抑;所以要想每個(gè)通道都采樣100次,那么就需要觸發(fā)轉(zhuǎn)換100次秒梳,這樣定義一段內(nèi)存存儲(chǔ)數(shù)據(jù)的數(shù)組就應(yīng)該是ADGetValue[100][6],這樣在i=0~99情況下100個(gè)數(shù)據(jù)ADGetValue[i][0]表示為通道1在采樣了100次后所得到的數(shù)據(jù)酪碘。
ADC通道配置
DMA模式配置要點(diǎn)
- STM32的ADC1作為DMA的外設(shè)源兴垦,其地址為16位,如果變量值不是16位那么所得到的數(shù)據(jù)會(huì)混亂滑进,但是DMA配置里是32位,所以需要強(qiáng)制轉(zhuǎn)換為32位阴汇。
vu16 ADGetValue[][];//AD采樣值,ADC_ChannelCount個(gè)通道,每個(gè)通道采樣ADC1_Count次
DMA_InitStructure.DMA_MemoryBaseAddr = (u32)&ADGetValue;//設(shè)置DMA內(nèi)存地址节槐,ADC轉(zhuǎn)換結(jié)果直接放入該地址
- 使用定時(shí)器外部觸發(fā)轉(zhuǎn)換,ADC六個(gè)通道采樣哥倔,循環(huán)采樣模式揍庄、連續(xù)轉(zhuǎn)換。
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //使用獨(dú)立模式沃测,掃描模式
ADC_InitStructure.ADC_ScanConvMode = ENABLE; //循環(huán)掃描模式
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; //連續(xù)轉(zhuǎn)換模式
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC1; //外部定時(shí)器觸發(fā)轉(zhuǎn)換
ADC配置代碼
/**
* @brief 配置采樣通道端口 使能GPIO時(shí)鐘 設(shè)置ADC采樣PA0端口信號(hào)
* @param None
* @retval : None
*/
void ADC1_GPIO_Config(void)
{
GPIO_InitTypeDef ADC_PORT;
RCC_APB2PeriphClockCmd(ADC_ClK, ENABLE);
ADC_PORT.GPIO_Pin = ADC_Pin; //ADC引腳選擇,6個(gè)通道
ADC_PORT.GPIO_Mode = GPIO_Mode_AIN; //GPIO設(shè)置為模擬輸入
GPIO_Init(ADC_CONTROL, &ADC_PORT);
}
/**
* @brief 配置ADC1的工作模式為DMA模式
* @param None
* @retval : None
*/
void ADC1_Mode_Config(void)
{
ADC_InitTypeDef ADC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); //使能ADC1時(shí)鐘
/* ADC1 configuration */
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //使用獨(dú)立模式食茎,掃描模式
ADC_InitStructure.ADC_ScanConvMode = ENABLE; //循環(huán)掃描模式
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; //連續(xù)轉(zhuǎn)換模式
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC1; //外部定時(shí)器觸發(fā)轉(zhuǎn)換
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;//數(shù)據(jù)右對(duì)齊
ADC_InitStructure.ADC_NbrOfChannel = ADC_ChannelCount; //有ADC_ChannelCount個(gè)轉(zhuǎn)換通道
ADC_Init(ADC1, &ADC_InitStructure);
/* 以下為配置通道數(shù)總共6路,分別是AD10,AD11,AD12,AD13,AD14,AD15 */
/* ADC1 regular channel10 configuration */
ADC_RegularChannelConfig(ADC1, ADC_Channel_10, 1, ADC_SampleTime_55Cycles5); //通道10,作為第1個(gè)采樣目標(biāo),采樣周期239.5個(gè)時(shí)鐘周期
/* ADC1 regular channel11 configuration */
ADC_RegularChannelConfig(ADC1, ADC_Channel_11, 2, ADC_SampleTime_55Cycles5); //通道11,作為第2個(gè)采樣目標(biāo),采樣周期55.5個(gè)時(shí)鐘周期
/* ADC1 regular channel12 configuration */
ADC_RegularChannelConfig(ADC1, ADC_Channel_12, 3, ADC_SampleTime_55Cycles5); //通道12,作為第3個(gè)采樣目標(biāo),采樣周期55.5個(gè)時(shí)鐘周期
/* ADC1 regular channel13 configuration */
ADC_RegularChannelConfig(ADC1, ADC_Channel_13, 4, ADC_SampleTime_55Cycles5); //通道13,作為第4個(gè)采樣目標(biāo),采樣周期55.5個(gè)時(shí)鐘周期
/* ADC1 regular channel14 configuration */
ADC_RegularChannelConfig(ADC1, ADC_Channel_14, 5, ADC_SampleTime_55Cycles5); //通道14,作為第5個(gè)采樣目標(biāo),采樣周期55.5個(gè)時(shí)鐘周期
/* ADC1 regular channel15 configuration */
ADC_RegularChannelConfig(ADC1, ADC_Channel_15, 6, ADC_SampleTime_55Cycles5); //通道15,作為第6個(gè)采樣目標(biāo),采樣周期55.5個(gè)時(shí)鐘周期
/* Enable ADC1 DMA */
ADC_DMACmd(ADC1, ENABLE); //使能ADC的DMA
/*使能ADC1外部觸發(fā)*/
ADC_ExternalTrigConvCmd(ADC1,ENABLE);
/* Enable ADC1 */
ADC_Cmd(ADC1, ENABLE); //使能ADC1
/* Enable ADC1 reset calibaration register */
ADC_ResetCalibration(ADC1);
/* Check the end of ADC1 reset calibration register */
while(ADC_GetResetCalibrationStatus(ADC1));
/* Start ADC1 calibaration */
ADC_StartCalibration(ADC1);
/* Check the end of ADC1 calibration */
while(ADC_GetCalibrationStatus(ADC1));
/* Start ADC1 Software Conversion */
ADC_SoftwareStartConvCmd(ADC1, ENABLE); //開(kāi)始轉(zhuǎn)換
}
/**
* @brief 配置DMA
* @param None
* @retval : None
*/
void DMA_Config(void)
{
DMA_InitTypeDef DMA_InitStructure;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); //使能DMA1時(shí)鐘
/* DMA1 channel1 configuration */
DMA_DeInit(DMA1_Channel1); //指定DMA1通道1
DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address;//設(shè)置DMA外設(shè)地址
DMA_InitStructure.DMA_MemoryBaseAddr = (u32)&ADGetValue;//設(shè)置DMA內(nèi)存地址附迷,ADC轉(zhuǎn)換結(jié)果直接放入該地址
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //外設(shè)設(shè)置為數(shù)據(jù)傳輸?shù)膩?lái)源
DMA_InitStructure.DMA_BufferSize = (ADC_ChannelCount* ADC1_Count); //DMA緩沖區(qū)設(shè)置為ADC_ChannelCount* ADC1_Count,有6個(gè)通道哎媚;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//外設(shè)地址不增加
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//內(nèi)存地址增加
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;//外設(shè)源的數(shù)據(jù)寬度半字,ADC1_DR的值16位
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;//循環(huán)模式
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(DMA1_Channel1, &DMA_InitStructure);
/* Enable DMA channel1 */
DMA_Cmd(DMA1_Channel1, ENABLE); //使能DMA通道
//DMA_ITConfig(DMA1_Channel1, DMA_IT_TC, ENABLE); //使能DMA傳輸完成中斷
}
/**
* @brief 初始化ADC1
* @param None
* @retval : None
*/
void ADC1_Init(void)
{
ADC1_GPIO_Config();
ADC1_Mode_Config();
DMA_Config();
TIM1_Mode_Config();//開(kāi)啟定時(shí)器1抄伍,產(chǎn)生PWN1外部觸發(fā)源
}