打印
[单片机芯片]

【CH32F207VCT6】开发例程+ 05 使用工业级传感器的采集

[复制链接]
308|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本帖最后由 聪聪哥哥 于 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;      
    }
}

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

79

主题

215

帖子

1

粉丝