DYyanfa 2024-03-30 15:25 采纳率: 31.3%
浏览 7

stc8g1k08单片机ADC转换电压百分比输出异常

stc8g1k08单片机,百分比battery_percent始终是3.3v电压对应的百分比,不是2.8v对应百分比,找了很久都没找到原因,通过串口打印v_bat的值,是正确的,就是百分比计算那一步出现问题。


#define  _ADC_C
#include "ADC.h"
#include "config.h"
#include "Uart.h"
#include "string.h"
#include "Battery.h"

#define ADC_RES_MAX 1024  // ADC采样最大值
#define V_REF (3.3)        // 参考电压为3.3V
#define V_BAT_MAX (2.8)     // 电池最大电压为2.8V
#define V_BAT_MIN (1.8)     // 电池最小电压为1.8V
#define V_BAT_WARN (2.1)    // 电池电量过低警告阈值为2.1V

unsigned int ADC_Value;  // 定义全局变量,用于存储采样结果
unsigned char cnt = 0;  //
unsigned int AdcBuff[] = {0,0,0,0,0,0,0,0,0,0}; //实时采集的adc值
unsigned int CpBuff[] = {0,0,0,0,0,0,0,0,0,0};
extern unsigned int cnt5s;
extern unsigned int inserttime;

void ADC_Init() 
{
    P_SW2 |= 0x80;
    ADCTIM = 0x2A; //设置 ADC 内部时序
    P_SW2 &= 0x7f;
    ADCCFG = 0x20; //设置 ADC 时钟为系统时钟/2,右对齐
    ADC_CONTR = 0x80; //使能 ADC 模块
}

void ADC_ISR() interrupt 5 
{
    ADC_Value = (unsigned int)(ADC_RES << 8) + (unsigned int)ADC_RESL;
    if(cnt < 10)
    {
        AdcBuff[cnt] = ADC_Value;
        cnt++;
    }
    ADC_CONTR &= ~0x20; //中断标志位清0
    if(cnt == 10)
    {
        cnt = 0;
    }
}

void Delay1ms(void)    
{
    unsigned char data i, j;

    _nop_();
    _nop_();
    _nop_();
    i = 11;
    j = 190;
    do
    {
        while (--j);
    } while (--i);
}

void ADC_BATTERY() 
{
    static float up_percent = 100.0; //用于保存上一次的百分比值
    unsigned int i=0,j=0,t=0;
//    unsigned char battery_percent = 0;  // 定义电量百分比变量
    float adc_res,battery_percent,ave;
    float v_bat;
    ADC_CONTR &= 0xf0;    //底四位清零
    ADC_CONTR |= 0x0C;    //置通道12
    ADC_CONTR |= 0x40;  // 启动ADC转换
    Delay1ms();

    for(i=0;i<10;i++)  //将AdcBuff值存到CpBuff,防止中断导致buffer值变化
    {
        CpBuff[i] = AdcBuff[i];
    }
    i = 0;
    for(j=0;j<9;j++)    //将数组由小到大排序
        for(i=0;i<9-j;i++)
            if(CpBuff[i]>CpBuff[i+1])
            {
                t=CpBuff[i];
                CpBuff[i] = CpBuff[i+1];
                CpBuff[i+1] = t;
            }
    ave = ((CpBuff[3] + CpBuff[4] + CpBuff[5] + CpBuff[6]) / 4); //adc范围658-1024
    if(ave>600 && ave <= 1024)
    {
        adc_res = (float)ave * V_REF / ADC_RES_MAX;// 计算ADC被转换通道的输入电压,采集电压范围2.12-3.3
        v_bat = adc_res * V_BAT_MAX / V_REF;// 计算电池电压值,电池电压范围1.8-2.8
        if(v_bat > 2.8)  
            v_bat = 2.8;
        if(v_bat < 1.8)
            v_bat = 1.8;
        battery_percent =((v_bat - 1.8) * 100) ;// 计算电池电量百分比
        UartSend(battery_percent);
  • 写回答

1条回答 默认 最新

  • 关注

    下午好🌅🌅🌅
    本答案参考ChatGPT-3.5

    问题分析:

    1. 原始ADC值的采集存在问题。在ADC_ISR中,当cnt < 10时,将ADC值存储到AdcBuff数组中。但是,在ADC_BATTERY函数中,将AdcBuff数组的值复制到CpBuff数组中进行排序,这可能导致在ADC中断过程中AdcBuff数组的值发生变化。

    解决方案:

    1. 修改ADC_ISR函数:将ADC值存储到AdcBuff数组时,使用atomic操作,以确保在拷贝AdcBuff数组之前,ADC值不会被修改。
    void ADC_ISR() interrupt 5 {
        EA = 0; // 关闭总中断
        ADC_Value = (unsigned int)(ADC_RES << 8) + (unsigned int)ADC_RESL;
        if(cnt < 10)
        {
            EA = 0; // 关闭总中断
            AdcBuff[cnt] = ADC_Value;
            cnt++;
            EA = 1; // 打开总中断
        }
        ADC_CONTR &= ~0x20; //中断标志位清0
        if(cnt == 10)
        {
            cnt = 0;
        }
    }
    
    1. 修改ADC_BATTERY函数:在计算电池电量百分比时,考虑使用排序后的CpBuff数组的中间4个值的平均值。
    void ADC_BATTERY() {
        static float up_percent = 100.0; //用于保存上一次的百分比值
        unsigned int i=0,j=0,t=0;
        float adc_res, battery_percent, ave;
        float v_bat;
        ADC_CONTR &= 0xf0;    //底四位清零
        ADC_CONTR |= 0x0C;    //置通道12
        ADC_CONTR |= 0x40;  // 启动ADC转换
        Delay1ms();
    
        EA = 0; // 关闭总中断
        for(i=0;i<10;i++)  //将AdcBuff值存到CpBuff,防止中断导致buffer值变化
        {
            CpBuff[i] = AdcBuff[i];
        }
        EA = 1; // 打开总中断
    
        for(j=0;j<9;j++)    //将数组由小到大排序
            for(i=0;i<9-j;i++)
                if(CpBuff[i]>CpBuff[i+1])
                {
                    t=CpBuff[i];
                    CpBuff[i] = CpBuff[i+1];
                    CpBuff[i+1] = t;
                }
        ave = (CpBuff[3] + CpBuff[4] + CpBuff[5] + CpBuff[6]) / 4; //取中间4个值的平均值
        if(ave>600 && ave <= 1024)
        {
            adc_res = (float)ave * V_REF / ADC_RES_MAX;// 计算ADC被转换通道的输入电压,采集电压范围2.12-3.3
            v_bat = adc_res * V_BAT_MAX / V_REF;// 计算电池电压值,电池电压范围1.8-2.8
            if(v_bat > 2.8)  
                v_bat = 2.8;
            if(v_bat < 1.8)
                v_bat = 1.8;
            battery_percent = (v_bat - 1.8) * 100 / (V_BAT_MAX - V_BAT_MIN);// 计算电池电量百分比
            UartSend(battery_percent);
        }
    }
    
    1. 注意:代码中使用的V_BAT_MAX和V_BAT_MIN值应与电池的实际最大电压和最小电压相对应。

    总结:

    通过对ADC_ISR和ADC_BATTERY函数的修改,可以修复百分比battery_percent始终是3.3v电压对应的百分比,不是2.8v对应百分比的问题。在ADC_ISR中使用atomic操作确保ADC值的准确性,并且在ADC_BATTERY函数中使用中间4个值的平均值计算电池电量百分比。

    评论 编辑记录

报告相同问题?

问题事件

  • 创建了问题 3月30日

悬赏问题

  • ¥15 iE 浏览器不能访问 oracle secure global server, google 浏览器可以
  • ¥50 Namuru2007_Baseband_Verilog代码疑点
  • ¥15 复杂公式求最值问题,含有多个符号常量
  • ¥15 Qt移植后在开发板上无法运行
  • ¥15 关于STID337-32D15YB开发板LINUX调试
  • ¥15 有没有熟悉自动化点胶设备,有个加密狗,想复制一份软件到另个设备
  • ¥15 mysql 部分截取字符串
  • ¥20 只有一个C盘,磁盘管理分区
  • ¥15 PVE8.2版本ceph的监控项状态stopped不能销毁
  • ¥15 如何加快视频观看速度?