桂林y 发表于 2025-4-21 15:12:30

求高手帮忙AI8H 12位ADC转换电压

我的ADC能正常读取到,使用的是unsigned int ,我的参考电压是2.5V读取出来的ADC数值*2500会超限,使用unsigned long数据就会变成0,求教怎么进行数据计算,unsigned int V,adc 使用V= (adc*2500/4096);无法实现计算#include "drive_ADC.h"

#define BUBBLE_SORT   //设置使用冒泡排序,去掉最高值、最低值,求中间平均值(不需要的话屏蔽此行)
#define SUM_LENGTH      16      /* 平均值采样次数 最大值16(不需要的话可将定义值改为 1 )*/
#ifdef BUBBLE_SORT//使用冒泡排序,去掉最高值、最低值,求中间平均值

int voladc1,voladc2,voladc3,voladc4;
u16 ADC_Buffer;
#endif
//void ADC_Isr() interrupt 5
//{
//    ADC_CONTR &= ~0x20; //清中断标志
//    voladc1=(ADC_RES << 8) | ADC_RESL;//adc读取
//    ADC_CONTR |= 0x4a; //继续 AD 转换
//}

//符号                地址adc电源启动转换 转换结束 PWM触发   通道选择
//ADC_CONTR BCH ADC_POWER ADC_START ADC_FLAG ADC_EPWMT ADC_CHS
//                              1                        1                        1                1                0000
void initADC(void)
{
    P_SW2 |= 0x80; //使能访问 XFR,没有冲突不用关闭
    ADCTIM = 0x3f; //设置 ADC 内部时序
    ADCCFG = 0x20; //设置 ADC 时钟为系统时钟/2/1
    //ADCCFG = 0x2f; //设置 ADC 时钟为系统时钟/2/16
    ADC_CONTR = 0x80; //使能 ADC 模块
    //EADC = 1; //使能 ADC 中断
    EA = 1;
    //ADC_CONTR |= 0x40; //启动 AD 转换
}

#ifdef BUBBLE_SORT//使用冒泡排序
//========================================================================
// 函数: void DataSwap(u16* data1, u16* data2)
// 描述: 数据交换函数。
// 参数: data1,data2 要交换的数据.
// 返回: none.
// 版本: VER1.0
// 日期: 2021-9-27
// 备注:
//========================================================================
void DataSwap(u16* data1, u16* data2)
{
      u16 temp;
      temp = *data1;
      *data1 = *data2;
      *data2 = temp;
}

//========================================================================
// 函数: void BubbleSort(u16* pDataArry, u8 DataNum)
// 描述: 冒泡排序函数。
// 参数: pDataArry需要排序的数组,DataNum需要排序的数据个数.
// 返回: none.
// 版本: VER1.0
// 日期: 2021-9-27
// 备注:
//========================================================================
void BubbleSort(u16* pDataArry, u8 DataNum)
{
      bit flag;
      u8 i,j;
      for(i=0;i<DataNum-1;i++)
      {
                flag = 0;
                for(j=0;j<DataNum-i-1;j++)
                {
                        if(pDataArry > pDataArry)
                        {
                              flag = 1;
                              DataSwap(&pDataArry, &pDataArry);
                        }
                }
                if(!flag)//上一轮比较中不存在数据交换,则退出排序
                {
                        break;
                }
      }
}
#endif

//========================================================================
// 函数: u16 Get_ADC12bitResult(u8 channel))      //channel = 0~15
// 描述: 查询法读一次ADC结果.
// 参数: channel: 选择要转换的ADC, 0~15.
// 返回: 12位ADC结果.
// 版本: V1.0, 2016-4-28
//========================================================================
u16      Get_ADC12bitResult(u8 channel)      //channel = 0~15
{
      ADC_RES = 0;
      ADC_RESL = 0;

    ADC_CONTR = (ADC_CONTR & 0xF0) | 0x40 | channel;    //启动 AD 转换
    _nop_();
    _nop_();
    _nop_();
    while((ADC_CONTR & 0x20) == 0);   //等待ADC结束
    ADC_CONTR &= ~0x20;   //清除ADC结束标志
    return (((u16)ADC_RES << 8) | ADC_RESL);
}

/***********************************
查询方式做一次ADC, chn为通道号, chn=0~7对应P1.0~P1.7, chn=8~14对应P0.0~P0.6, chn=15对应BandGap电压.
***********************************/
u32 ADC_convert(u8 chn)
{
      u16      j ;
      //u32 h;
      u8      k;                //平均值滤波时使用

      Get_ADC12bitResult(chn);                //参数i=0~15,查询方式做一次ADC, 切换通道后第一次转换结果丢弃. 避免采样电容的残存电压影响.
      Get_ADC12bitResult(chn);                //参数i=0~15,查询方式做一次ADC, 切换通道后第二次转换结果丢弃. 避免采样电容的残存电压影响.

#ifdef BUBBLE_SORT//使用冒泡排序,去掉最高值、最低值,求中间平均值

      for(k=0; k<16; k++)      ADC_Buffer = Get_ADC12bitResult(chn);
      BubbleSort(ADC_Buffer,16);//冒泡排序
      for(k=4, j=0; k<12; k++) j += ADC_Buffer;//取中间8个数据
      j = j / 8;                // 求平均

#else   //采样累加,求平均值(不需要的话可将 SUM_LENGTH 定义值改为 1 )

      for(k=0, j=0; k<SUM_LENGTH; k++)      j += Get_ADC12bitResult(chn);      // 采样累加和 参数0~15,查询方式做一次ADC, 返回值就是结果
      j = j / SUM_LENGTH;                // 求平均

#endif
      h= (j*2500/4096);

//debug("ADCV%02bd=%04u",chn,g);
debug("ADC%02bd=%u",chn,h);
return j;
}

国芯人工智能 发表于 2025-4-21 15:18:00

在STC8H系列单片机中,12位ADC的转换结果范围为0到4095,对应输入电压的0到参考电压(Vref)。根据你的描述,参考电压为25V,因此ADC的每个LSB(最低有效位)对应的电压为:

\[ \text{LSB} = \frac{25V}{4096} \approx 0.0061V \]

你提到使用unsigned int类型存储ADC转换结果时,当ADC值为2500时会出现超限问题,而使用unsigned long类型时数据会变成0。这可能是由于数据类型转换或计算过程中出现了问题。以下是一些可能的原因及解决方案:

1. 数据类型选择
unsigned int在大多数8位单片机中通常是16位,范围为0到65535。对于12位ADC的转换结果(0到4095),unsigned int是足够的。然而,在进行计算时,如果中间结果超过了unsigned int的范围,可能会导致溢出或错误。

2. 计算顺序
在C语言中,整数除法的结果会被截断为整数。例如,2500 / 4096的结果是0,因为2500小于4096。为了避免这种情况,可以先进行乘法运算,再进行除法运算。例如:

c
unsigned int V = (adc2500) / 4096;

这样可以确保在除法之前,adc2500的结果足够大,避免被截断为0。

3. 使用浮点数
如果需要进行更精确的计算,可以使用浮点数。例如:

c
float V = (float)adc25.0 / 4096.0;

这样可以避免整数除法带来的精度损失。

4. 代码示例
以下是一个完整的代码示例,展示了如何正确计算ADC转换后的电压值:

c
include "driveADCh"

define BUBBLESORT   // 设置使用冒泡排序,去掉最高值、最低值,求中间平均值(不需要的话屏蔽此行)
define SUMLENGTH 16   // 平均值采样次数 最大值16(不需要的话可将定义值改为 1 )

ifdef BUBBLESORT// 使用冒泡排序,去掉最高值、最低值,求中间平均值
int voladc1, voladc2, voladc3, voladc4;
u16 ADCBuffer;
endif

void ADCIsr() interrupt 5
{
    ADCCONTR &= ~0x20; // 清中断标志
    voladc1 = (ADCRESarr)
            {
                u16 temp = arr;
                arr = arr;
                arr = temp;
            }
      }
    }
}

u16 CalculateAverage(u16 arr[], int n)
{
    BubbleSort(arr, n);
    u16 sum = 0;
    for (int i = 1; i < n-1; i++) // 去掉最高值和最低值
    {
      sum += arr;
    }
    return sum / (n-2);
}
endif

在main函数中,你可以使用CalculateAverage函数来计算平均值:

c
ifdef BUBBLESORT
    u16 adcvalues;
    // 填充adcvalues数组
    u16 averageadc = CalculateAverage(adcvalues, SUMLENGTH);
    unsigned int averagevoltage = CalculateVoltage(averageadc);
endif

总结
通过合理选择数据类型、调整计算顺序以及使用浮点数,可以避免ADC转换结果计算中的问题。此外,冒泡排序和平均值计算可以帮助你获得更稳定的ADC读数。希望这些建议能帮助你解决当前的问题。

[本文内容由国芯人工智能辅助生成,仅供参考]

DebugLab 发表于 2025-4-21 15:39:55


等号前后数据类型必须匹配

unsigned int V;

unsigned int adc;

V=(unsigned int) ((unsigned long)adc*2500UL/4096UL);


haiyang201 发表于 2025-4-21 16:07:34

                j = (uint)((ulong)j * 119 / Bandgap);        //计算外部电压, Bandgap为1.19V, 测电压分辨率0.01V
我一般这样计算

18344371880 发表于 2025-4-21 17:26:41

不是4096,是4096UL就可以了

桂林y 发表于 2025-4-22 11:45:43

DebugLab 发表于 2025-4-21 15:39
等号前后数据类型必须匹配

unsigned int V;


感谢,用了你的方法,终于可以了

桂林y 发表于 2025-4-22 11:46:41

国芯人工智能 发表于 2025-4-21 15:18
在STC8H系列单片机中,12位ADC的转换结果范围为0到4095,对应输入电压的0到参考电压(Vref)。根据你的描述 ...

非常感谢,问题解决了

桂林y 发表于 2025-4-22 11:46:56

18344371880 发表于 2025-4-21 17:26
不是4096,是4096UL就可以了

非常感谢

桂林y 发表于 2025-4-22 11:48:00

haiyang201 发表于 2025-4-21 16:07
j = (uint)((ulong)j * 119 / Bandgap);        //计算外部电压, Bandgap为1.19V, 测电压分辨率0.01V
我一般这 ...

非常感谢,用了你这种写法终于正常了
页: [1]
查看完整版本: 求高手帮忙AI8H 12位ADC转换电压