本帖最后由 聪聪哥哥 于 2025-7-30 20:23 编辑
一:CH32 ADC 的基础知识分享:
ADC 模块包含 2个 12 位的逐次逼近型的模拟数字转换器,最高 14MHz 的输入时钟。支持 16 个外部通道和2个内部信号源采样源。可完成通道的单次转换、连续转换,通道间自动扫描模式、间断模式、外部触发模式、双重采样等功能。可以通过模拟看门狗功能监测通道电压是否在阈值范围内。
二: 基本特征如下所示:
2.1:12 位分辨率
2.2:支持 16 个外部通道和 2个内部信号源采样
2.3多通道的多种采样转换方式:单次、连续、扫描、触发、间断等
2.4数据对齐模式:左对齐、右对齐
2.5采样时间可按通道分别编程
2.6规则转换和注入转换均支持外部触发
2.7模拟看门狗监测通道电压,自校准功能
2.8双重模式
2.9ADC 通道输入范围:0≤V≤VoA
2.10输入增益可调,可实现小信号放大采样
三:软件配置过程:
3.1 模块上电:
3.2 配置ADC的采样时钟
3.3 配置采样的ADC通道配置
3.4 使能ADC的自校准功能
3.5 设置ADC的采样周期
3.6 读取ADC数据及其数据处理
四:软件编写如下所示
这里我是用的PA4 引脚作为ADC的输入引脚
4.1 初始化ADC基本参数
void ADC_Function_Init(void)
{
ADC_InitTypeDef ADC_InitStructure={0};
GPIO_InitTypeDef GPIO_InitStructure={0};
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE );
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE );
RCC_ADCCLKConfig(RCC_PCLK2_Div8);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(GPIOA, &GPIO_InitStructure);
ADC_DeInit(ADC1);
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
ADC_InitStructure.ADC_ScanConvMode = DISABLE;
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);
ADC_DMACmd(ADC1, ENABLE);
ADC_Cmd(ADC1, ENABLE);
ADC_BufferCmd(ADC1, DISABLE); //disable buffer
ADC_ResetCalibration(ADC1);
while(ADC_GetResetCalibrationStatus(ADC1));
ADC_StartCalibration(ADC1);
while(ADC_GetCalibrationStatus(ADC1));
Calibrattion_Val = Get_CalibrationValue(ADC1);
}
4.2 配置DAM方式采集ADC功能
void DMA_Tx_Init( DMA_Channel_TypeDef* DMA_CHx, u32 ppadr, u32 memadr, u16 bufsize)
{
DMA_InitTypeDef DMA_InitStructure={0};
RCC_AHBPeriphClockCmd( RCC_AHBPeriph_DMA1, ENABLE );
DMA_DeInit(DMA_CHx);
DMA_InitStructure.DMA_PeripheralBaseAddr = ppadr;
DMA_InitStructure.DMA_MemoryBaseAddr = memadr;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructure.DMA_BufferSize = bufsize;
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_VeryHigh;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init( DMA_CHx, &DMA_InitStructure );
}
4.3 :使能ADC的采集功能函数如下所示:
void ADC_INT(void)
{
ADC_Function_Init();
DMA_Tx_Init( DMA1_Channel1, (u32)&ADC1->RDATAR, (u32)TxBuf, 1024 );
DMA_Cmd( DMA1_Channel1, ENABLE );
ADC_RegularChannelConfig(ADC1, ADC_Channel_4, 1, ADC_SampleTime_239Cycles5 );
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
Delay_Ms(50);
ADC_SoftwareStartConvCmd(ADC1, DISABLE);
}
4.4 添加 10ms的处理函数如下所示:
//========================================================================
// 函数: void task_10ms(void)
// 描述: 10ms 任务.
// 参数: None.
// 返回: None.
// 版本: V1.0, 2025-07-09
//========================================================================
void task_10ms(void)
{
iAdcData = Get_ADC_Val(4);
}
五:实物测试如下所示:5.1 传感器实物如下所示:
上图为电阻式的 位移传感器,在工业控制中,常常用来采集位移的变化。
3.2 实物测试如图所示:
实测:使用内部ADC采集工业控制类的位移传感器,可以满足要求。稍后上传视频。
六:和大家分享几种常见的ADC数据处理方法如下所示:
6.1 :一阶互补滤波算法:
取值:k = 0-1,本次取值滤波结果 =(1-K),本次采样值需要加上上次滤波得结果数据
代码如下所示:
int firstdatadeal(int CurrValue, int lastValue, float K)
{ return K * CurrValue + (1-K) * lastValue;}
6.2: 采用中值算法滤波
主要是程序在执行的时候,连续采集N次(需要注意下这里下,这里的N必须取值奇数),程序需要按大到小或者从小到达的顺序进行排序,然后取中间数值做为有效值。
int MidValueDeal(int N)
{
int value_buf[N]; int i,j,k,temp;
for( i = 0; i < N; ++i)
{
value_buf[i] = HAL_ADC_GetValue(&hadc1);
}
for(j = 0 ; j < N-1; ++j)
{
for(k = 0; k < N-j-1; ++k)
{ //从小到大排序,冒泡法排序
if(value_buf[k] > value_buf[k+1])
{
temp = value_buf[k];
value_buf[k] = value_buf[k+1];
value_buf[k+1] = temp;
}
}
}
return value_buf[(N-1)/2];
}
可以有效的去除数据因为外界干扰引起的数据的波动,对一些大滞后系统:比如温度、液位等变化缓慢的有良好的滤波效果,上述代码可以有效消除异常数据和平稳变化的采样值效果比较好;
6.3:算术平均数滤波
连续取值N个数据,对所有的数据进行取平均值;
代码如下:
int AverValueDeal(int N)
{
int sum = 0;
unsinged short i;
for(i = 0; i < N; ++i)
{
sum += HAL_ADC_GetValue(&hadc1);
}
return sum/N;
}
这里取值时候,需要根据被控对象进行选择,N值取值过大,会导致响应过慢,数据的灵敏度过低,所以采用算术平均值滤波时候,需要我们格外的注意;
6.4:滑动滤波算法
把连续取N个采样值看成一个队列,队列的长度固定为N。每次采样到一个新数据放入队尾,并扔掉原来队首的一次数据(先进先出原则)。把队列中的N个数据进行算术平均运算,就可获得新的滤波结果;
#define N 10
int value_buf[N];
int sum=0; //数据总和
int Num=0; //当前队列中数组下标
int movedataFilter()
{
if(Num < N)
{
value_buf[Num] = HAL_ADC_GetValue(&hadc1);
sum += value_buf[Num];
Num++;
return sum/Num;
}
else
{
sum -= sum/N;
sum += HAL_ADC_GetValue(&hadc1);
return sum/N;
}
}
|