aming_ou 发表于 2025-2-15 14:00:33

用8G1K08A的按键和PWM练习

几个月前开始接触STC8G1K08A这8脚单片机,觉得挺有意思的,范例代码要做成实用的功能,还是要很多试验,才能有一些收获。
先实现了上电等待5秒,再接通1秒,然后断开进入掉电模式,仅涉及定时器和IO相关的代码
再实现了桶装水自动抽水器的定时功能,短按开始抽水,再短按,停止抽水,或者自动倒计时 n 秒后停止抽水;长按3秒开始抽水并进入设置模式,直到短按停止抽水,自动保存长按开始到短按结束之间的时间,作为下次自动倒计时时长,即倒计时时长可设置。涉及定时器、IO、按键中断,短按,长按,长长按,EEPROM读写等操作,还有就是掉电模式进入和恢复等。
最近又研究PWM,控制LED高亮、中亮、低亮、熄灭的不同显示效果,用deepseek和kimi提供的代码,由于不是适配STC8G1K08A芯片,所以无法运行,看STC8G.pdf芯片规范文件,还是很多不理解,特别是IO口不同工作模式的切换,希望大佬可以指点指点。

以下是使用STC8G1K08A单片机实现通过按键控制LED开关和亮度的代码示例。代码基于STC单片机的C语言开发环境,使用PWM信号控制LED亮度,并通过按键切换高亮、中亮、低亮和熄灭状态。
硬件连接说明
[*]P32:按键输入,连接到单片机的P32引脚。
[*]P54:PWM输出,连接到单片机的P54引脚,用于控制LED亮度,默认输出高电平(熄灭)。
[*]LED:通过电阻连接到P54引脚,另一端接电源正极,当IO口低电平时点亮。
[*]按键:一端连接到P32引脚,另一端接地。
软件设计思路
[*]PWM控制:使用STC单片机的定时器功能生成PWM信号,通过占空比控制LED亮度。
[*]按键检测:检测P32引脚的状态,判断按键是否按下。
[*]状态切换:通过按键切换LED的亮度状态(高亮、中亮、低亮、熄灭)。
[*]上电默认高亮:程序启动时,设置为高亮状态。
#include "STC8G.H"
#define MAIN_Fosc       24000000L   // 定义主时钟
#define MCU_IDLE()          PCON |= 1   /* MCU 进入 IDLE 模式 */
#define MCU_POWER_DOWN()    PCON |= 2   /* MCU 进入 睡眠 模式 */

#define u1 bit
#define u8 unsigned char
#define u16 unsigned int
#define u32 unsigned long
#define IDLE_TIME 3000000L      // 空闲计数器阈值

// 定义按键引脚
#define KEY_PIN P32// 按键连接P32

// 定义亮度档位
// 定义PWM相关参数
#define PWM_HIGH_DUTY 0xFF   // 高亮占空比(高电平时间长) = 常亮
#define PWM_MEDIUM_DUTY 0x80 // 中亮占空比
#define PWM_LOW_DUTY 0x40    // 低亮占空比
#define PWM_OFF_DUTY 0x00    // 熄灭占空比(低电平)

extern void          _nop_   (void);

// 定义状态变量
u8 led_state = 0;             // LED状态:0-高亮,1-中亮,2-低亮,3-熄灭
u1 wakeup_status = 1;      // 运行标志(0:休眠,1:活跃)
u32 idle_time = 0;            // 空闲计数器

// 延时函数, ms,要延时的ms数, 这里只支持1~255ms. 自动适应主时钟.
void delay_ms(unsigned int ms)
{
      unsigned int i;
      do{
                i = MAIN_Fosc / 10000;
                while(--i);
      }while(--ms);
}

// 初始化PWM
void PWM_Init() {
    // 配置 PCA
      CCON= 0x00;
      CL = 0x00;
      CH = 0x00;

      CCAPM2 = 0x42;         // PCA 模块2为PWM工作模式
      CR = 1;                  // 启动PCA 计数器
      
}

// 设置PWM占空比,输出8位的PWM
void Set_PWM_Duty(u8 duty) {
    if (duty == 0xFF){ // 固定输出低电平,常亮
                PCA_PWM2 &= 0x3F;
                CCAP2H    = 0xFF;
      }else if (duty == 0x00){ // 固定输出高电平,熄灭
                PCA_PWM2 &= 0xC0;
                CCAP2H    = 0x00;
      }else{ // 输出8位的PWM
                PCA_PWM2 &=0x00;
                CCAP2L = duty;   // 设置占空比(低字节)
                CCAP2H = duty;   // 设置占空比(高字节)
      }
}

// 按键检测
u8 Key_Scan() {
    if (KEY_PIN == 0) {// 检测按键是否按下
      delay_ms(20);// 消抖
      if (KEY_PIN == 0) {
            while (KEY_PIN == 0);// 等待按键释放
            return 1;// 返回按键按下标志
      }
    }
    return 0;// 无按键按下
}

// 初始化I/O端口
void InitPorts()
{
      // 初始化为准双向
    P3M0 = 0x00; P3M1 = 0x00;
    P5M0 = 0x00; P5M1 = 0x00;
}

// 外部中断0初始化
void Int0_Init(void) {
    IT0 = 1;         // 使能INT0下降沿中断
    EX0 = 1;         // 使能INT0中断0
    EA = 1;          // 使能中断总开关
}

// 掉电模式
void PowerDownMode(void) {
    wakeup_status = 0;
    _nop_();
    _nop_();
    MCU_POWER_DOWN();// MCU进入掉电模式(STC8G系列)
    _nop_();
    _nop_();
    _nop_();
    _nop_();
}

// 从掉电模式唤醒
void WakeUpFromPowerDown(void) {
    PCON &= 0xFD; // 清除掉电标志
}

// 外部中断0服务程序(按键唤醒)
void INT0_ISR(void) interrupt 0 {
    if (!wakeup_status){       // 如果在休眠状态,就执行唤醒操作
      wakeup_status = 1;
      WakeUpFromPowerDown(); // 从掉电模式唤醒
    }
}

// 主函数
void main() {
      InitPorts(); // 初始化端口
      Int0_Init(); // 初始化外部中断0
    PWM_Init();// 初始化PWM
    // 上电默认高亮
    Set_PWM_Duty(PWM_HIGH_DUTY);
      led_state = 0;
    while (1) {
                idle_time++;
      if (Key_Scan()) {// 检测按键
            led_state++;// 切换LED状态
                        idle_time = 0; //重置空闲计数器
            if (led_state > 3) {
                led_state = 0;// 循环回到高亮状态
            }
            // 根据状态设置PWM占空比或普通输出高电平
            switch (led_state) {
                case 0:// 高亮
                  Set_PWM_Duty(PWM_HIGH_DUTY);
                  break;
                case 1:// 中亮
                  Set_PWM_Duty(PWM_MEDIUM_DUTY);
                  break;
                case 2:// 低亮
                  Set_PWM_Duty(PWM_LOW_DUTY);
                  break;
                case 3:// 熄灭
                  Set_PWM_Duty(PWM_OFF_DUTY);
                  break;
            }
      }else{
                        if (led_state == 3 && idle_time >= IDLE_TIME){ // 熄灭时,进入掉电省电模式
                              PowerDownMode();
                        }
                }
    }
}

阿白凌坤 发表于 2025-4-18 11:22:58

CCAPnL
CCAPnH    这两个寄存器是干啥用的,看数据手册写的我没有完全理解,可能是我的理解能力有问题。能否给讲解一下谢谢!

aming_ou 发表于 2025-4-19 11:05:39

阿白凌坤 发表于 2025-4-18 11:22
CCAPnL
CCAPnH    这两个寄存器是干啥用的,看数据手册写的我没有完全理解,可能是我的理解能力有问题。能 ...

理论上这是内部使用的,我们只要设置好CL和CH初始值,然后修改对应PWM,
CCAPnL中的n是指第几个PWM,例如代码中使用模块2,所以后续代码就设置CCAP2L和CCAP2H
页: [1]
查看完整版本: 用8G1K08A的按键和PWM练习