找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 1161|回复: 15
打印 上一主题 下一主题
收起左侧

stc8g单片机单个io口连接5个按键,用adc采集电压,会自己跳转功能?

[复制链接]
跳转到指定楼层
楼主
用的是stc8g1k08a单片机,用p5.4口接5个按键,电压分别是0.33v、1.18v、2.87v、3.79v、4.59v,想让这几个按键分别调光0、25%、50%、75%、100%,按键按下的时候可以执行此功能,但是过一会儿(有时几秒有时一分钟)就会自己跳成别的亮度,比如我按下25%,过一会儿亮度就会变成50
%或者是0。我用万用表测了p5.4口的电压,没有按键按下的时候一直是5.03v左右,想知道这是啥原因嘞,是adc采集的问题吗,还是我的程序逻辑不对嘞,电路板我只是用万能板拼接的,还是是硬件的问题,有没有大神帮我看看

部分代码:
unsigned long timer0_count = 0;
unsigned char PWM_T = 0;   //0-250占空比控制变
unsigned char current_brightness = 0;        //当前亮度
signed int    brightness = 0;
#define IT_HZ        100000        //中断频率
#define TIM0_VALUE        65536UL - (MAIN_Fosc / IT_HZ)


//设置高电平占空比
//num: 0-100%
void set_duty_cycle(unsigned char num)
{
        current_brightness = num;        if(num > 0)
        {
                light_switch_state = 1;
        }
        else
        {
                light_switch_state = 0;
        }
        PWM_T = (float)(num / 100.0)*250;
}

/********************* Timer0中断函数************************/
void timer0_int (void) interrupt TIMER0_VECTOR
{
         timer0_count++;    //每次定时器溢出加1
         
         if(timer0_count==250)   //PWM周期 100个单位
                 {
                        timer0_count=0;  //使t=0,开始新的PWM周期
                        LIGHT = 1;        //开灯
                 }
         
         if(PWM_T==timer0_count)  //按照当前占空比切换输出为高电平
                 {  
                        LIGHT = 0;        //关灯
                 }
}

//获取灯光占空比
//返回值: 0-100
int get_light_pwm(void)
{
        return PWM_T*100/250;
        // return 0;
}



#define ADC_CHANNEL 4 // 使用P5.4作为ADC输入,对应ADC通道4
#define VCC 5000      // 供电电压,单位mV

// 初始化系统
void System_Init(void)
        {
    P_SW2 |= 0x80;
    ADCTIM = 0x3f;                   //设置ADC内部时序
    P_SW2 &= 0x7f;

    ADCCFG = 0x2f;                   //设置ADC时钟为系统时钟/2/16
    // 设置ADC
    ADC_CONTR = 0x84; // 选择ADC通道4
        }

// 读取ADC值
int Read_ADC(void)
{
                int res;
        
    ADC_CONTR |= 0x40; // 开始ADC转换
    while(!(ADC_CONTR & 0x20)); // 等待转换完成
                ADC_CONTR &= ~0x20;
    res = (ADC_RES << 8) | ADC_RESL; // 返回10位ADC结果
          return res;
}

// 判断按键
void Check_Keys(int adc_value)
        {
                            //按键1
                              if(adc_value >= 20 && adc_value < 144)
                                        {
                                                        delay_ms(10);
                                                        if (adc_value >= 20 && adc_value < 144)
                                                        {
                                                                        brightness = 0;
                                                                        set_duty_cycle(brightness);
                                                        }
                                        }
                              //按键2
                                        else if (adc_value >= 144 && adc_value < 307)
                                        {
                                                        delay_ms(10);
                                                        if (adc_value >= 144 && adc_value < 307)
                                                        {
                                                                        brightness = 25;
                                                                        set_duty_cycle(brightness);
                                                        }
                                        }
                            //按键3
                                       
                                else if (adc_value >= 308 && adc_value < 588)
                                        {
                                                delay_ms(10);
                                                if (adc_value >= 308 && adc_value < 588)
                                                {
                                                                brightness = 50;
                                                                set_duty_cycle(brightness);
                                                }
                                        }
                            //按键4
                                else if (adc_value >= 589 && adc_value < 860)
                                        {
                                                delay_ms(10);
                                                if (adc_value >= 589 && adc_value < 860)
                                                {
                                                                brightness = 75;
                                                                set_duty_cycle(brightness);
                                                }
                                        }
                            //按键5
                              else   if(adc_value >=861 && adc_value <983)
                                           {
                                                 delay_ms(10);
                                                  if(adc_value >=861 && adc_value <983)
                                                {
                                                        brightness = 100;
                                                        set_duty_cycle(brightness);
                                                }
                                         }
        }

// 主函数
void main(void)
        {
                P5M0 = 0x00; P5M1 = 0x10; // 设置P5.4为高阻输入
                P3M0 = 0x08; P3M1 = 0xf7; // P3.3为推挽输出
               System_Init();
                light_init();                //使用定时器0作pwm功能
                delay_ms(5);
                EA = 1;                                
                                       
                while (1)
                        {
                               int adc_value = Read_ADC(); // 读取ADC值
                                delay_ms(10);
                                 Check_Keys(adc_value);           // 检查按键
                                set_duty_cycle(brightness);
      }
        }

我的电路图大概是这样的,估计是没连接好,干扰有点多,后面在电路上加个102电容就可以了,感谢各位大神,只是现在还有个问题,像三楼说的那样,开机时会高亮一下,我在EA=1后面加一个LIGHT=0也还是不行,请问这个怎么解决呀






分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏1 分享淘帖 顶 踩
回复

使用道具 举报

沙发
ID:161164 发表于 2024-12-31 09:20 | 只看该作者
官方的解释是你的外围电路做的不好
请按手册来画
回复

使用道具 举报

板凳
ID:192020 发表于 2024-12-31 10:50 | 只看该作者
首先Check_Keys中的delay_ms(10);没什么作用,因为adc_value在Check_Keys运行时就不会改变,加上延迟指也是不会变的。然后Read_ADC没做滤波,这两个方面处理下应该好很多
                       
回复

使用道具 举报

地板
ID:69038 发表于 2024-12-31 11:00 | 只看该作者
AD按键串,要没按键时,电压确实是高的,约=于电源电压。除非你的接法比较另类。
10bit的ADC,分5档有4个节点,平均每节点相差256个计数,为了容错,节点上下+-128个计数:
0档<=128;
1档<=384,>128;
2档<=640,>384;
3档<=896,>640;
4档>896;
基实,人眼对光的敏感度并不是线性的,所以,“0、25%、50%、75%、100%”的值不一定适合。。
在数据处理时,0档和4档不需要输出PWM,直接IO出低电平或出高电平;
ADC在采样时要有数字滤波比较好。
然后就是按键的接触电阻要稳定、分压电阻要稳定。。
===
我曾用电位器调整ADC的值,来控制PWM输出实现无级调光,好用是好用,但发现有2个缺点,
就是在某些范围内的占空比时,明显看到灯会闪,不同的频率周期,这个范围也不尽相同,
可能软件还要做个关于PWM的周期与占空比的适配。。
另一个问题是,只要配置了PWM,IO就输出高电平,然后再按PWM配置输出占空比,
表现为开机时灯会高亮一下,再按PWM显示亮度。。

=====这是一个宿醉刚起、头脑尚未完全清醒者的发言====
====以内容上仅供参考,不负任何法律或道义上的责任====
回复

使用道具 举报

5#
ID:1133081 发表于 2024-12-31 11:01 | 只看该作者
stc8g1k08a有现成的硬件PWM不用?定时器中断频率过高有可能影响ADC。ADC连续采样3次,抛弃前两次数据,以第3次数据为准或连续采样8-10次取平均值。ADC输入端加1K电阻和102电容组成低通滤波。 Check_Keys函数去掉delay_ms(10),不需要软件防抖。
回复

使用道具 举报

6#
ID:1133081 发表于 2024-12-31 11:22 | 只看该作者
不宜在主循环内定义变量
// 主函数
void main(void)
{
        int adc_value;
        P5M0 = 0x00; P5M1 = 0x10; // 设置P5.4为高阻输入
        P3M0 = 0x08; P3M1 = 0xf7; // P3.3为推挽输出
        System_Init();
        light_init();                //使用定时器0作pwm功能
        delay_ms(5);
        EA = 1;                              
       
        while (1)
        {
                adc_value = Read_ADC(); // 读取ADC值
                delay_ms(10);
                Check_Keys(adc_value);           // 检查按键
                set_duty_cycle(brightness);
        }
}
回复

使用道具 举报

7#
ID:69038 发表于 2024-12-31 13:46 | 只看该作者
WL0123 发表于 2024-12-31 11:22
不宜在主循环内定义变量
// 主函数
void main(void)


你老师说的吗?
还是你师傅教的?
或是你哪本书上看到的?

这好象说不过去啊,也不影响什么的。。
回复

使用道具 举报

8#
ID:1034262 发表于 2024-12-31 15:18 | 只看该作者
可以参考STC官方的ADC键盘电路,16个键的。
回复

使用道具 举报

9#
ID:1133081 发表于 2024-12-31 15:41 | 只看该作者
zhuls 发表于 2024-12-31 13:46
你老师说的吗?
还是你师傅教的?
或是你哪本书上看到的?

你仔细看了楼主的代码吗?
回复

使用道具 举报

10#
ID:161164 发表于 2024-12-31 17:40 | 只看该作者
1. adc_value 在Check_Keys()函数内是不变的,if()delay()if的多余的,不要死抄按键消抖
2. 主循环的set_duty_cycle(brightness)也很多余,Check_Keys()函数内有按键已经运行一次了,不用在主循环不断运行
3. 可以试试中值滤波
  1. #define ARRAY_SIZE 10

  2. // 中值滤波函数
  3. unsigned char median_filter() {
  4.     unsigned char sorted[ARRAY_SIZE];
  5.     unsigned char i, j, k;
  6.        
  7.     for (i = 0; i < ARRAY_SIZE; i++) {
  8.                 sorted[i] = Read_ADC(); // 读取ADC值
  9.     }
  10.         // 冒泡排序 sorted 数组
  11.         for (j = 0; j < ARRAY_SIZE - 1; j++) {
  12.                 for (k = 0; k < ARRAY_SIZE - j - 1; k++) {
  13.                         if (sorted[k] > sorted[k + 1]) {
  14.                                 // 交换
  15.                                 unsigned char temp = sorted[k];
  16.                                 sorted[k] = sorted[k + 1];
  17.                                 sorted[k + 1] = temp;
  18.                         }
  19.                 }
  20.         }
  21.         // 找到中值并赋值给输出
  22.    return sorted[ARRAY_SIZE / 2];
  23. }

  24. // 主函数
  25. void main(void)
  26. {
  27.         P5M0 = 0x00;
  28.         P5M1 = 0x10; // 设置P5.4为高阻输入
  29.         P3M0 = 0x08;
  30.         P3M1 = 0xf7; // P3.3为推挽输出
  31.         System_Init();
  32.         light_init();                //使用定时器0作pwm功能
  33.         delay_ms(5);
  34.         EA = 1;

  35.         while (1)
  36.         {
  37.                 int adc_value = median_filter();
  38.                 Check_Keys(adc_value);           // 检查按键
  39.         }
  40. }
复制代码
回复

使用道具 举报

11#
ID:1109793 发表于 2024-12-31 19:29 | 只看该作者
WL0123 发表于 2024-12-31 15:41
你仔细看了楼主的代码吗?

之前好像是要先声明才能使用的,但是现在的编译器是可以这样写的。
回复

使用道具 举报

12#
ID:1109793 发表于 2024-12-31 19:37 | 只看该作者
的确里面的延时没有任何意义
  1. //按键1
  2.   if(adc_value > 20 && adc_value < 144)
  3.     {
  4.                 set_duty_cycle(0);
  5.                 return;
  6.                 }
  7. //按键2
  8.         if (adc_value > 144 && adc_value < 307)
  9.                 {
  10.                 set_duty_cycle(25);
  11.                 return;
  12.                 }
复制代码

这样看起来简洁一些
回复

使用道具 举报

13#
ID:1076567 发表于 2025-1-6 11:20 | 只看该作者
zhuls 发表于 2024-12-31 11:00
AD按键串,要没按键时,电压确实是高的,约=于电源电压。除非你的接法比较另类。
10bit的ADC,分5档有4个 ...

确实在开机时会高亮一下,大神有没有什么解决办法,我试过在开机的时候让IO口直接等于0,还是不行
回复

使用道具 举报

14#
ID:1076567 发表于 2025-1-6 11:24 | 只看该作者
WL0123 发表于 2024-12-31 11:01
stc8g1k08a有现成的硬件PWM不用?定时器中断频率过高有可能影响ADC。ADC连续采样3次,抛弃前两次数据,以第 ...

谢谢大神,加上电容确实解决了,只是不加软件防抖的话有时按键会失灵,估计是按键也有点问题,加上软件防抖的话会有其它什么问题吗
回复

使用道具 举报

15#
ID:1076567 发表于 2025-1-6 11:29 | 只看该作者
coody_sz 发表于 2024-12-31 15:18
可以参考STC官方的ADC键盘电路,16个键的。

官方的电路是在官网看吗 我只看到一个触摸按键的演示视频,还是是在论坛里面搜索呀
回复

使用道具 举报

16#
ID:1076567 发表于 2025-1-6 11:40 | 只看该作者
lkc8210 发表于 2024-12-31 17:40
1. adc_value 在Check_Keys()函数内是不变的,if()delay()if的多余的,不要死抄按键消抖
2. 主循环的set_d ...

后面在电路中加了个电容解决了,只是不加按键消抖的话按键有时有点不灵敏,可能按键用久了也不太好用,加上消抖的话会有什么其他问题吗,之前经常这样用这个中值滤波我试了,通电的时候会自己亮,而且按键也完全不起作用了,是不是跟我的电路不太匹配呀,我把我的电路贴到上面了
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表