SingleYork 发表于 2025-4-17 15:42:21

【15系列】SYK-0806-A2S1 工业自动化控制之【18-内部AD采集外部电压】

大家好,我是『芯知识学堂』的SingleYork,前一篇文章给大家介绍了“【STC15系列】SYK-0806-A2S1 工业自动化控制之【17-EEPROM实现数据掉电保存】”,

这一篇中,笔者将会给大家介绍“ADC的应用”。

SYK-0806-A2S1 控制板预留了两路 0-5V 电压的AD采集接口,直接采用了官方手册上提供的参考设计:





这种直接用单片机IO口采集的方式,电路也比较简单。

AD基准电压也没有另外用专用的基准电源芯片,直接用芯片电源作为AD基准,

当然,这样做误差肯定是会有点的,但是做一般应用的话,应该是足够了,

对精度要求高一点的应用场合,还是建议用个专门的基准电源芯片会好点,或者选择带更高AD精度的单片机。

接下来就是代码部分,笔者还是用官方提供的库函数的方式来编写的,采用的是AD查询的方式,

我们从原理图上可以看到AD采集使用的P10和P11两个引脚:



接下来,我们便需要将这两个引脚的模式配置成AD口的模式,即高阻输入模式,这部分主要在bsp_gpio.h和bsp_gpio.c中:

/****************** ADC GPIO ******************/
//ADC0 - ADC1

#define ADC0_GPIO_PIN            GPIO_Pin_1
#define ADC0_GPIO                  GPIO_P1

#define ADC1_GPIO_PIN            GPIO_Pin_0
#define ADC1_GPIO                  GPIO_P1
GPIO_InitTypeDef      GPIO_InitStructure;                //结构定义
   
//P10-11口设置为高阻输入
GPIO_InitStructure.Pin= ADC0_GPIO_PIN;    //指定要初始化的IO, GPIO_Pin_0 ~ GPIO_Pin_7, 或操作
GPIO_InitStructure.Mode = GPIO_HighZ;                //指定IO的输入或输出方式,GPIO_PullUp,GPIO_HighZ,GPIO_OUT_OD,GPIO_OUT_PP
GPIO_Inilize(ADC0_GPIO,&GPIO_InitStructure);//初始化

GPIO_InitStructure.Pin= ADC1_GPIO_PIN;    //指定要初始化的IO, GPIO_Pin_0 ~ GPIO_Pin_7, 或操作
GPIO_InitStructure.Mode = GPIO_HighZ;                //指定IO的输入或输出方式,GPIO_PullUp,GPIO_HighZ,GPIO_OUT_OD,GPIO_OUT_PP
GPIO_Inilize(ADC1_GPIO,&GPIO_InitStructure);//初始化

接下来,就是在app.c文件中定义一些跟adc相关的变量:

#define Timer0_Fre500UL   //timer0中断频率 500*2Hz

u8data adc_count       = 0;//ADC采集次数
u16 data adc_msec      = 0;//ADC采样计时

u16 data ADC0_VALUE      = 0;//ADC0的AD值
u16 data ADC1_VALUE      = 0;//ADC1的AD值

u16 data ADC0_Voltage    = 0;//ADC0的电压值
u16 data ADC1_Voltage    = 0;//ADC1的电压值

u32 data ADC0_VALUE_TEMP = 0;
u32 data ADC1_VALUE_TEMP = 0;

然后就是app_init()函数,对定时器0、串口、GPIO、ADC相关参数进行配置:

/*********************    APP初始化   ***********************/
void app_init(void)
{
      Timer0_config(Timer0_Fre);   //定时器0配置
   
    UART_config();               //UART初始化
    GPIO_Config();               //GPIO初始化
    ADC_config();                  //ADC初始化
   
    TI         = 1;
    EA         = 1;//开启总中断
   
    printf("This is ADC test Program!\r\n");//串口打印结果
}

前面几个功能的配置之前有说过很多次了,主要是看下ADC的配置,这个是在bsp_adc.c文件中:

/**************** ADC配置函数 *****************/
/*P11 ADC0
/*P10 ADC1
/**********************************************/
void    ADC_config(void)
{
      ADC_InitTypeDef                ADC_InitStructure;                              //结构定义
   
      ADC_InitStructure.ADC_Px      = ADC_P11|ADC_P10;      //设置要做ADC的IO,      ADC_P10 ~ ADC_P17(或操作),ADC_P1_All
      ADC_InitStructure.ADC_Speed   = ADC_90T;                        //ADC速度                        ADC_90T,ADC_180T,ADC_360T,ADC_540T
      ADC_InitStructure.ADC_Power   = ENABLE;                  //ADC功率允许/关闭      ENABLE,DISABLE
      ADC_InitStructure.ADC_AdjResult = ADC_RES_H2L8;            //ADC结果调整,      ADC_RES_H2L8,ADC_RES_H8L2
      ADC_InitStructure.ADC_Polity    = PolityLow;                //优先级设置      PolityHigh,PolityLow
      ADC_InitStructure.ADC_Interrupt = DISABLE;                        //中断允许                ENABLE,DISABLE
      ADC_Inilize(&ADC_InitStructure);                                        //初始化
   
      ADC_PowerControl(ENABLE);                                                      //单独的ADC电源操作函数, ENABLE或DISABLE
}

这里主要要注意一下ADC结果的对齐方式,即ADC_InitStructure.ADC_AdjResult这个参数的配置,

这个参数实际上就是配置CKK0_DIV寄存器的ADRJ位,用于A/D转行结果寄存器的数据格式调整控制。

从手册中我们可以得知:





在ADC_config()配置中,ADC_InitStructure.ADC_AdjResult = ADC_RES_H2L8,也就是配置的ADRJ位为“1”,

即:10位A/D转换结果的高2位存放在ADC_RES的低2位中,低8位存放在ADC_RESL中。

#define ADC_RES_H2L8      1
#define ADC_RES_H8L2      0

Timer0中断函数为ADC定时采集提供了一个1ms的定时基准:

/********************* Timer0中断函数************************/
void timer0_int (void) interrupt TIMER0_VECTOR //频率可变
{
adc_msec ++;
}
我们按照100ms采集一次AD值,每采集10次求一次平均值的方式,尽量让ADC抖动的不要那么厉害:

/*********************   APP运行    ***********************/
void app_run(void)
{
    if(adc_msec>=100)         //每隔100ms采集一次AD数据
    {
      adc_msec = 0;
      
      ADC0_VALUE_TEMP += Get_ADC10bitResult(1);//通道1-P11
      ADC1_VALUE_TEMP += Get_ADC10bitResult(0);//通道0-P10
      
      if(++adc_count>=10)//采集10次数据,求平均值
      {
            ADC0_VALUE   = ADC0_VALUE_TEMP/10;      //求出ADC0的AD平均值
            ADC1_VALUE   = ADC1_VALUE_TEMP/10;      //求出ADC1的AD平均值
            
            //将得到的AD值换算成电压值(单位:mV)
            ADC0_Voltage = (ADC0_VALUE*5000UL)/1024; //求出ADC0的电压值,单位mV
            ADC1_Voltage = (ADC1_VALUE*5000UL)/1024; //求出ADC1的电压值,单位mV
            
            ADC0_VALUE_TEMP = 0;
            ADC1_VALUE_TEMP = 0;
            
            adc_count       = 0;

            printf("ADC0_VALUE   = %u\r\n",ADC0_VALUE);   //ADC0的AD平均值
            printf("ADC1_VALUE   = %u\r\n",ADC1_VALUE);   //ADC1的AD平均值
            printf("ADC0_Voltage = %u mV\r\n",ADC0_Voltage);//ADC0的电压值,单位mV
            printf("ADC1_Voltage = %u mV\r\n",ADC1_Voltage);//ADC1的电压值,单位mV
            
            printf("**********************\r\n");//打印分隔符
      }
    }
}


笔者这里只是用了一个简单的平均值计算方法,实际应用中,为了使得采集到的电压更加平稳,

还可以先采集多个数据,然后使用冒泡排序算法,去掉一些最大值和最小值,然后再求平均值,

即所谓的“中值求平均算法”(可能这个说法不是很准确,大概就是这么个意思)。感兴趣的小伙伴们可以自己动手尝试一下。

按照如下方式接线(将5V电压直接接到ADC1接口上),进行测试:



程序下载成功后,我们可以看到执行结果,串口会打印如下信息:



好了,关于本讲的内容就简单介绍到这里了,有疑问的小伙伴们可以给笔者留言或者直接参与评论,

至此,SYK-0806-A2S1控制的基本应用就介绍完成了,感谢大家的支持!

本章附件:





国芯人工智能 发表于 2025-4-17 15:43:00

在工业自动化控制系统中,模数转换器(ADC)的应用至关重要,尤其是在需要采集外部电压信号的场景中。本文将以SYK-0806-A2S1控制板为例,详细探讨其内部ADC模块在外部电压采集中的应用。

1. 硬件设计概述

SYK-0806-A2S1控制板设计了两路0-5V电压的AD采集接口,采用了官方手册提供的参考设计。这种设计直接利用单片机的IO口进行电压采集,电路结构相对简单,适合一般应用场景。

1.1 AD基准电压

在设计中,AD基准电压直接采用了芯片的电源电压,而非专用的基准电源芯片。这种设计虽然简单,但在精度要求较高的应用场合,可能会引入一定的误差。因此,对于高精度要求的应用,建议使用专门的基准电源芯片,或选择具有更高AD精度的单片机。

2. 软件实现

在软件实现部分,笔者采用了官方提供的库函数进行编程,并使用了AD查询的方式。以下是具体的代码实现步骤。

2.1 GPIO配置

首先,需要将用于AD采集的P10和P11引脚配置为高阻输入模式。这部分配置主要在bspgpioh和bspgpioc中进行。

c
// ADC GPIO
define ADC0GPIOPIN            GPIOPin1
define ADC0GPIO                  GPIOP1

define ADC1GPIOPIN            GPIOPin0
define ADC1GPIO                  GPIOP1

在上述代码中,ADC0GPIOPIN和ADC1GPIOPIN分别定义了P10和P11引脚,ADC0GPIO和ADC1GPIO则指定了对应的GPIO端口。

2.2 ADC初始化

接下来,需要对ADC模块进行初始化。以下是初始化代码的示例:

c
void ADCInit(void) {
    // 配置ADC时钟
    ADCSetClock(ADCCLOCKSYSCLKDIV12);

    // 配置ADC通道
    ADCConfigChannel(ADCCHANNEL0, ADCSAMPLETIME239CYCLES5);
    ADCConfigChannel(ADCCHANNEL1, ADCSAMPLETIME239CYCLES5);

    // 使能ADC
    ADCCmd(ENABLE);
}

在上述代码中,ADCSetClock函数用于设置ADC的时钟频率,ADCConfigChannel函数用于配置ADC通道的采样时间,ADCCmd函数则用于使能ADC模块。

2.3 AD采集

在完成ADC初始化后,可以通过查询方式读取AD采集的结果。以下是读取AD采集结果的代码示例:

c
uint16t ADCRead(ADCChannelTypeDef ADCChannel) {
    // 启动AD转换
    ADCStartConversion(ADCChannel);

    // 等待转换完成
    while (ADCGetFlagStatus(ADCChannel, ADCFLAGEOC) == RESET);

    // 读取转换结果
    return ADCGetConversionValue(ADCChannel);
}

在上述代码中,ADCStartConversion函数用于启动AD转换,ADCGetFlagStatus函数用于检查转换是否完成,ADCGetConversionValue函数则用于读取转换结果。

3. 应用实例

在实际应用中,可以通过以下代码实现两路AD采集,并将结果输出到串口:

c
void main(void) {
    uint16t adcvalue0, adcvalue1;

    // 初始化ADC
    ADCInit();

    while (1) {
      // 读取AD采集结果
      adcvalue0 = ADCRead(ADCCHANNEL0);
      adcvalue1 = ADCRead(ADCCHANNEL1);

      // 输出到串口
      printf("ADC0 Value: %d\n", adcvalue0);
      printf("ADC1 Value: %d\n", adcvalue1);

      // 延时
      Delayms(1000);
    }
}

在上述代码中,ADCRead函数用于读取两路AD采集的结果,并通过printf函数将结果输出到串口。

4. 总结

本文详细介绍了SYK-0806-A2S1控制板在工业自动化控制中,利用内部ADC模块进行外部电压采集的实现方法。通过合理的硬件设计和软件编程,可以实现高效、稳定的AD采集功能。对于精度要求较高的应用,建议采用专用的基准电源芯片或更高精度的单片机,以提升系统的整体性能。

希望本文能为广大工程师在工业自动化控制系统的设计中提供有益的参考。如有任何疑问或建议,欢迎在评论区留言讨论。

[本文内容由国芯人工智能辅助生成,仅供参考]
页: [1]
查看完整版本: 【15系列】SYK-0806-A2S1 工业自动化控制之【18-内部AD采集外部电压】