好的,所有信息已收集完毕。现在我来编写完整的演示程序。
以下是针对 STC8A8K64D4-45I-LQFP64 的完整演示程序,包含三个功能模块:
完整演示程序(C语言,适用于 Keil C51)
/*************************************************************
* 文件名: main.c
* 芯片: STC8A8K64D4-45I-LQFP64
* 功能:
* 1. 串口下发指令设置软件PWM渐变亮度,实现平滑呼吸灯效果
* 2. ADC采集电压,根据电压区间自动分段调节PWM输出占空比
* 3. 检测外部高低电平持续时间,判断长按键/短按键并串口上报
*
* 硬件连接:
* - PWM输出: P2.0 (增强型PWM通道0)
* - ADC输入: P1.0/ADC0 (电位器分压)
* - 按键输入: P3.2 (外部按键,低电平有效)
* - 串口1: P3.0(RxD), P3.1(TxD), 波特率115200, 8N1
*
* 串口指令(以\r\n结尾):
* "PWM=xxx" — 设置PWM占空比(0~1000)
* "BREATH" — 启动呼吸灯模式
* "STOP" — 停止呼吸灯,恢复手动模式
*
* 串口上报:
* "KEY_SHORT" — 短按键(按下<1秒)
* "KEY_LONG" — 长按键(按下>=1秒)
* "ADC=xxx, PWM=xxx" — ADC电压区间调节时上报
*************************************************************/
#include "reg51.h"
#include "intrins.h"
/******************** 系统时钟定义 ********************/
#define FOSC 11059200UL // 系统时钟 11.0592MHz
#define BAUD 115200UL // 串口波特率
/******************** 特殊功能寄存器声明 ********************/
/* I/O口模式 */
sfr P0M1 = 0x93;
sfr P0M0 = 0x94;
sfr P1M1 = 0x91;
sfr P1M0 = 0x92;
sfr P2M1 = 0x95;
sfr P2M0 = 0x96;
sfr P3M1 = 0xB1;
sfr P3M0 = 0xB2;
sfr P4M1 = 0xB3;
sfr P4M0 = 0xB4;
sfr P5M1 = 0xC9;
sfr P5M0 = 0xCA;
/* 串口1 */
sfr SCON = 0x98;
sfr SBUF = 0x99;
sfr PCON = 0x87;
sfr AUXR = 0x8E;
sfr SADDR = 0xA9;
sfr SADEN = 0xB9;
/* 定时器 */
sfr TCON = 0x88;
sfr TMOD = 0x89;
sfr TL0 = 0x8A;
sfr TH0 = 0x8C;
sfr TL1 = 0x8B;
sfr TH1 = 0x8D;
sfr T2H = 0xD6;
sfr T2L = 0xD7;
sfr T4T3M = 0xD1;
sfr T3H = 0xD4;
sfr T3L = 0xD5;
sfr T4H = 0xD2;
sfr T4L = 0xD3;
sfr INTCLKO = 0x8F;
/* ADC */
sfr ADC_CONTR = 0xBC;
sfr ADC_RES = 0xBD;
sfr ADC_RESL = 0xBE;
sfr ADCCFG = 0xDE;
/* 扩展寄存器访问 */
sfr P_SW2 = 0xBA;
sfr P_SW1 = 0xA2;
/* 中断 */
sfr IE = 0xA8;
sfr IP = 0xB8;
sfr IPH = 0xB7;
sfr IE2 = 0xAF;
sfr IP2 = 0xB5;
sfr IPH2 = 0xB6;
/* I/O口中断 */
#define P3INTE *(unsigned char volatile xdata *)0xFD03
#define P3INTF *(unsigned char volatile xdata *)0xFD13
#define P3IM0 *(unsigned char volatile xdata *)0xFD23
#define P3IM1 *(unsigned char volatile xdata *)0xFD33
#define P3WKUE *(unsigned char volatile xdata *)0xFD43
/* 增强型PWM寄存器 (需先置位EAXFR) */
#define PWMSET *(unsigned char volatile xdata *)0xFF00
#define PWMCFG *(unsigned char volatile xdata *)0xFF06
#define PWMCH *(unsigned char volatile xdata *)0xFF00
#define PWMCL *(unsigned char volatile xdata *)0xFF01
#define PWMCKS *(unsigned char volatile xdata *)0xFF02
#define PWMIF *(unsigned char volatile xdata *)0xFF05
#define PWMFDCR *(unsigned char volatile xdata *)0xFF06
#define PWM0T1H *(unsigned char volatile xdata *)0xFF10
#define PWM0T1L *(unsigned char volatile xdata *)0xFF11
#define PWM0T2H *(unsigned char volatile xdata *)0xFF12
#define PWM0T2L *(unsigned char volatile xdata *)0xFF13
#define PWM0CR *(unsigned char volatile xdata *)0xFF14
#define PWM0HLD *(unsigned char volatile xdata *)0xFF15
/* 定时器2预分频 */
#define TM2PS *(unsigned char volatile xdata *)0xFEA2
/******************** 位定义 ********************/
/* 串口1 */
sbit RI = SCON^0;
sbit TI = SCON^1;
sbit REN = SCON^4;
sbit SM1 = SCON^6;
sbit SM0 = SCON^7;
/* 定时器0 */
sbit TR0 = TCON^4;
sbit TF0 = TCON^5;
sbit ET0 = IE^1;
/* 定时器1 */
sbit TR1 = TCON^6;
sbit TF1 = TCON^7;
/* 总中断 */
sbit EA = IE^7;
sbit ES = IE^4;
/* ADC */
sbit ADC_POWER = ADC_CONTR^7;
sbit ADC_START = ADC_CONTR^6;
sbit ADC_FLAG = ADC_CONTR^5;
/* 按键引脚 */
sbit KEY = P3^2;
/******************** 全局变量 ********************/
volatile unsigned char g_uart_rx_buf[16]; // 串口接收缓冲区
volatile unsigned char g_uart_rx_cnt = 0; // 接收计数
volatile unsigned char g_uart_rx_ok = 0; // 一帧接收完成标志
volatile unsigned int g_pwm_duty = 500; // 当前PWM占空比(0~1000)
volatile unsigned char g_breath_en = 0; // 呼吸灯使能
volatile unsigned int g_breath_step = 0; // 呼吸灯步进
volatile unsigned char g_breath_dir = 0; // 呼吸灯方向(0:渐亮,1:渐暗)
volatile unsigned char g_key_pressed = 0; // 按键按下标志
volatile unsigned int g_key_timer = 0; // 按键按下计时(ms)
volatile unsigned char g_key_reported = 0; // 按键状态已上报标志
volatile unsigned int g_sys_tick = 0; // 系统滴答(ms)
/******************** 函数声明 ********************/
void delay_ms(unsigned int ms);
void UART1_Init(void);
void UART1_SendByte(unsigned char dat);
void UART1_SendString(unsigned char *str);
void UART1_ISR(void) interrupt 4;
void Timer0_Init(void);
void Timer0_ISR(void) interrupt 1;
void ADC_Init(void);
unsigned int ADC_Read(unsigned char channel);
void PWM_Init(void);
void PWM_SetDuty(unsigned int duty);
void Breath_Process(void);
void ADC_AutoPWM_Process(void);
void Key_Scan_Process(void);
void Cmd_Parse(void);
/******************** 主函数 ********************/
void main(void)
{
unsigned char i;
/* ---- 配置I/O口 ---- */
P0M0 = 0x00; P0M1 = 0x00; // P0: 准双向口
P1M0 = 0x00; P1M1 = 0x00; // P1: 准双向口(ADC输入用高阻,在ADC初始化中设置)
P2M0 = 0x00; P2M1 = 0x00; // P2: 准双向口(PWM输出由硬件控制)
P3M0 = 0x00; P3M1 = 0x00; // P3: 准双向口
/* ---- 初始化各模块 ---- */
Timer0_Init(); // 定时器0: 1ms系统滴答
UART1_Init(); // 串口1: 115200,8N1
ADC_Init(); // ADC初始化
PWM_Init(); // 增强型PWM初始化
/* 设置P1.0为高阻输入(ADC通道) */
P1M0 &= ~0x01;
P1M1 |= 0x01;
/* 使能P3.2低电平中断(用于按键检测) */
P3IM1 |= 0x04; // P3.2: IM1=1
P3IM0 &= ~0x04; // P3.2: IM0=0 => 低电平中断
P3INTE |= 0x04; // 使能P3.2中断
P3WKUE |= 0x04; // 使能唤醒(可选)
EA = 1; // 开总中断
/* 串口输出启动信息 */
UART1_SendString("STC8A8K64D4 Demo Start\r\n");
UART1_SendString("Commands: PWM=xxx, BREATH, STOP\r\n");
/* ---- 主循环 ---- */
while(1)
{
/* 1. 串口指令处理 */
if(g_uart_rx_ok)
{
Cmd_Parse();
g_uart_rx_ok = 0;
g_uart_rx_cnt = 0;
}
/* 2. 呼吸灯处理 */
if(g_breath_en)
{
Breath_Process();
}
/* 3. ADC自动调节PWM */
ADC_AutoPWM_Process();
/* 4. 按键检测(由定时器中断辅助计时) */
Key_Scan_Process();
/* 简单延时,降低主循环频率 */
delay_ms(5);
}
}
/******************** 延时函数 ********************/
void delay_ms(unsigned int ms)
{
unsigned int i, j;
for(i = 0; i < ms; i++)
for(j = 0; j < 1350; j++); // 约1ms @11.0592MHz
}
/******************** 串口1初始化 ********************/
void UART1_Init(void)
{
/* 使用定时器1作为波特率发生器,模式2(8位自动重载) */
/* 波特率 = 2^SMOD / 32 * (T1溢出率) */
/* T1溢出率 = FOSC / 12 / (256 - TH1) (12T模式) */
/* 115200 = 2^0 / 32 * 11059200 / 12 / (256 - TH1) */
/* TH1 = 256 - 11059200/12/32/115200 = 256 - 2 = 254 = 0xFE */
SCON = 0x50; // 模式1(8位UART), REN=1
TMOD &= 0x0F; // 定时器1模式0(16位自动重载) 先清高4位
TMOD |= 0x20; // 定时器1模式2(8位自动重载)
AUXR &= ~0x40; // T1x12=0, 12T模式
AUXR |= 0x01; // S1ST2=0, 使用定时器1作为波特率发生器
TH1 = 0xFE; // 重载值
TL1 = 0xFE;
TR1 = 1; // 启动定时器1
ES = 1; // 使能串口1中断
}
/******************** 串口1发送单字节 ********************/
void UART1_SendByte(unsigned char dat)
{
SBUF = dat;
while(!TI);
TI = 0;
}
/******************** 串口1发送字符串 ********************/
void UART1_SendString(unsigned char *str)
{
while(*str)
{
UART1_SendByte(*str++);
}
}
/******************** 串口1发送数字(十进制) ********************/
void UART1_SendNum(unsigned int num)
{
unsigned char buf[6];
unsigned char i = 0;
if(num == 0)
{
UART1_SendByte('0');
return;
}
while(num > 0)
{
buf[i++] = (num % 10) + '0';
num /= 10;
}
while(i > 0)
{
UART1_SendByte(buf[--i]);
}
}
/******************** 串口1中断服务 ********************/
void UART1_ISR(void) interrupt 4
{
unsigned char dat;
if(RI)
{
RI = 0;
dat = SBUF;
/* 收到回车(0x0D)或换行(0x0A)表示一帧结束 */
if((dat == '\r') || (dat == '\n'))
{
if(g_uart_rx_cnt > 0)
{
g_uart_rx_buf[g_uart_rx_cnt] = '\0';
g_uart_rx_ok = 1;
}
}
else
{
if(g_uart_rx_cnt < sizeof(g_uart_rx_buf) - 1)
{
g_uart_rx_buf[g_uart_rx_cnt++] = dat;
}
}
}
if(TI)
{
TI = 0;
}
}
/******************** 定时器0初始化(1ms滴答) ********************/
void Timer0_Init(void)
{
/* 1T模式, 16位自动重载 */
/* 定时周期 = (65536 - [TH0,TL0]) / FOSC */
/* 1ms = (65536 - reload) / 11059200 */
/* reload = 65536 - 11059200 / 1000 = 65536 - 11059 = 54477 = 0xD4CD */
AUXR |= 0x80; // T0x12=1, 1T模式
TMOD &= 0xF0; // 定时器0模式0(16位自动重载)
TMOD |= 0x00;
TH0 = 0xD4; // 重载值高8位
TL0 = 0xCD; // 重载值低8位
TR0 = 1; // 启动定时器0
ET0 = 1; // 使能定时器0中断
}
/******************** 定时器0中断服务(1ms) ********************/
void Timer0_ISR(void) interrupt 1
{
g_sys_tick++;
/* 按键计时 */
if(g_key_pressed)
{
g_key_timer++;
}
}
/******************** ADC初始化 ********************/
void ADC_Init(void)
{
ADCCFG = 0x20; // RESFMT=1(右对齐), SPEED=0000(最快)
ADC_CONTR = 0x80; // ADC_POWER=1, 开启ADC电源
delay_ms(1); // 等待ADC电源稳定
}
/******************** ADC读取(单次转换) ********************/
unsigned int ADC_Read(unsigned char channel)
{
unsigned int result;
ADC_CONTR &= 0xF0; // 清通道选择
ADC_CONTR |= (channel & 0x0F); // 设置通道
ADC_START = 1; // 启动转换
_nop_(); _nop_();
while(!ADC_FLAG); // 等待转换完成
ADC_FLAG = 0; // 清标志
result = ((unsigned int)ADC_RES << 8) | ADC_RESL;
return result; // 10位结果, 0~1023
}
/******************** 增强型PWM初始化 ********************/
void PWM_Init(void)
{
P_SW2 |= 0x80; // EAXFR=1, 允许访问扩展RAM区
/* 配置PWM时钟: 使用系统时钟, 预分频=0 => SYSclk/1 */
PWMCKS = 0x00; // SELT2=0, PWM_PS=0 => 时钟 = SYSclk/1 = 11.0592MHz
/* 设置PWM周期: 计数器从0计数到1000, 频率 = 11.0592MHz / 1000 ≈ 11KHz */
PWMCH = 0x03; // 周期高字节 (1000 = 0x03E8)
PWMCL = 0xE8;
/* 设置PWM通道0输出脚为P2.0 */
PWM0CR = 0x80; // ENC0O=1(使能输出), C0_S[1:0]=00(P2.0), C0INI=0(初始低)
/* 设置初始占空比 */
PWM0T1H = 0x00; // T1 = 0 (电平翻转点1)
PWM0T1L = 0x00;
PWM0T2H = (unsigned char)(g_pwm_duty >> 8); // T2 = 占空比
PWM0T2L = (unsigned char)(g_pwm_duty);
/* 使能PWM */
PWMSET = 0x40; // PWMRST=1, 复位PWM计数器
PWMSET = 0x01; // ENPWM=1, 使能PWM
PWMCFG = 0x01; // PWMCEN=1, 允许PWM计数
P_SW2 &= ~0x80; // EAXFR=0
}
/******************** 设置PWM占空比 ********************/
void PWM_SetDuty(unsigned int duty)
{
if(duty > 1000) duty = 1000;
g_pwm_duty = duty;
P_SW2 |= 0x80;
PWM0T2H = (unsigned char)(duty >> 8);
PWM0T2L = (unsigned char)(duty);
P_SW2 &= ~0x80;
}
/******************** 呼吸灯处理 ********************/
void Breath_Process(void)
{
static unsigned int breath_cnt = 0;
breath_cnt++;
if(breath_cnt < 10) return; // 每10ms调整一次
breath_cnt = 0;
if(g_breath_dir == 0) // 渐亮
{
g_breath_step += 5;
if(g_breath_step >= 1000)
{
g_breath_step = 1000;
g_breath_dir = 1; // 切换为渐暗
}
}
else // 渐暗
{
if(g_breath_step >= 5)
g_breath_step -= 5;
else
g_breath_step = 0;
if(g_breath_step == 0)
{
g_breath_dir = 0; // 切换为渐亮
}
}
PWM_SetDuty(g_breath_step);
}
/******************** ADC自动调节PWM ********************/
void ADC_AutoPWM_Process(void)
{
static unsigned int adc_timer = 0;
unsigned int adc_val;
unsigned int new_duty;
static unsigned int last_duty = 0xFFFF;
adc_timer++;
if(adc_timer < 50) return; // 每250ms采集一次
adc_timer = 0;
/* 如果呼吸灯模式开启,不进行ADC自动调节 */
if(g_breath_en) return;
/* 读取ADC通道0(P1.0) */
adc_val = ADC_Read(0);
/* 根据电压区间分段调节PWM占空比 */
/* ADC 10位: 0~1023, 对应 0V~VCC(假设3.3V或5V) */
if(adc_val < 200) // 0 ~ ~1V
new_duty = 100;
else if(adc_val < 400) // ~1V ~ ~2V
new_duty = 300;
else if(adc_val < 600) // ~2V ~ ~3V
new_duty = 500;
else if(adc_val < 800) // ~3V ~ ~4V
new_duty = 700;
else // > ~4V
new_duty = 900;
if(new_duty != last_duty)
{
last_duty = new_duty;
PWM_SetDuty(new_duty);
/* 串口上报 */
UART1_SendString("ADC=");
UART1_SendNum(adc_val);
UART1_SendString(", PWM=");
UART1_SendNum(new_duty);
UART1_SendString("\r\n");
}
}
/******************** 按键检测处理 ********************/
void Key_Scan_Process(void)
{
/* 按键状态由P3.2低电平中断触发记录 */
/* 在定时器中断中累计按下时间 */
if(g_key_pressed)
{
/* 检测按键是否释放(P3.2变高) */
if(KEY) // 高电平 => 释放
{
if(g_key_timer >= 1000) // >= 1秒 => 长按键
{
if(!g_key_reported)
{
UART1_SendString("KEY_LONG\r\n");
g_key_reported = 1;
}
}
else if(g_key_timer >= 20) // >= 20ms 消抖 => 短按键
{
if(!g_key_reported)
{
UART1_SendString("KEY_SHORT\r\n");
g_key_reported = 1;
}
}
/* 复位按键状态 */
g_key_pressed = 0;
g_key_timer = 0;
g_key_reported = 0;
}
}
}
/******************** P3口中断服务(按键检测) ********************/
void P3_ISR(void) interrupt 40
{
unsigned char intf;
intf = P3INTF; // 读取中断标志
P3INTF = 0; // 清中断标志
if(intf & 0x04) // P3.2中断
{
if(!KEY) // 低电平 => 按下
{
if(!g_key_pressed)
{
g_key_pressed = 1;
g_key_timer = 0;
g_key_reported = 0;
}
}
}
}
/******************** 串口指令解析 ********************/
void Cmd_Parse(void)
{
unsigned char *cmd = (unsigned char *)g_uart_rx_buf;
unsigned int val = 0;
unsigned char i = 0;
/* 指令: PWM=xxx */
if((cmd[0] == 'P' || cmd[0] == 'p') &&
(cmd[1] == 'W' || cmd[1] == 'w') &&
(cmd[2] == 'M' || cmd[2] == 'm') &&
cmd[3] == '=')
{
i = 4;
while((cmd[i] >= '0') && (cmd[i] <= '9'))
{
val = val * 10 + (cmd[i] - '0');
i++;
}
if(val > 1000) val = 1000;
g_breath_en = 0; // 停止呼吸灯
PWM_SetDuty(val);
UART1_SendString("PWM set to ");
UART1_SendNum(val);
UART1_SendString("\r\n");
return;
}
/* 指令: BREATH */
if((cmd[0] == 'B' || cmd[0] == 'b') &&
(cmd[1] == 'R' || cmd[1] == 'r') &&
(cmd[2] == 'E' || cmd[2] == 'e') &&
(cmd[3] == 'A' || cmd[3] == 'a') &&
(cmd[4] == 'T' || cmd[4] == 't') &&
(cmd[5] == 'H' || cmd[5] == 'h'))
{
g_breath_en = 1;
g_breath_step = 0;
g_breath_dir = 0; // 从渐亮开始
UART1_SendString("Breath mode ON\r\n");
return;
}
/* 指令: STOP */
if((cmd[0] == 'S' || cmd[0] == 's') &&
(cmd[1] == 'T' || cmd[1] == 't') &&
(cmd[2] == 'O' || cmd[2] == 'o') &&
(cmd[3] == 'P' || cmd[3] == 'p'))
{
g_breath_en = 0;
UART1_SendString("Breath mode OFF\r\n");
return;
}
/* 未知指令 */
UART1_SendString("Unknown cmd: ");
UART1_SendString(cmd);
UART1_SendString("\r\n");
}
程序说明
功能1:串口下发指令设置软件PWM渐变亮度(呼吸灯)
- 串口指令:发送
BREATH 启动呼吸灯,STOP 停止,PWM=xxx 手动设置占空比(0~1000)
- 呼吸灯实现:在定时器中断驱动的
Breath_Process() 中,每10ms步进5个单位,从0→1000(渐亮)再1000→0(渐暗),循环往复
- PWM硬件:使用增强型PWM通道0,输出到P2.0,频率约11KHz,占空比通过
PWM0T2 寄存器控制
功能2:ADC采集电压,分段调节PWM
- ADC通道:P1.0/ADC0,10位分辨率
- 分段逻辑:每250ms采集一次,将0~1023分为5个区间,分别对应占空比100/300/500/700/900
- 串口上报:每次占空比变化时上报
ADC=xxx, PWM=xxx
- 互斥:呼吸灯模式开启时,ADC自动调节暂停
功能3:检测外部高低电平持续时间,判断长/短按键
- 按键引脚:P3.2,低电平有效
- 检测方式:使用 P3口低电平中断(手册中说明低电平中断在正常模式下可正常使用),配合定时器0的1ms滴答计时
- 判断逻辑:
- 按下时间 ≥ 1000ms → 上报
KEY_LONG
- 按下时间 ≥ 20ms(消抖)且 < 1000ms → 上报
KEY_SHORT
- 串口上报:按键释放时立即上报
引脚分配(LQFP64)
| 功能 |
引脚 |
说明 |
| PWM输出 |
P2.0 |
增强型PWM通道0,推挽输出 |
| ADC输入 |
P1.0 |
ADC通道0,接电位器分压 |
| 按键输入 |
P3.2 |
低电平中断检测,外部上拉 |
| 串口TxD |
P3.1 |
串口1发送 |
| 串口RxD |
P3.0 |
串口1接收 |
使用说明
- 用 STC-ISP 下载软件 将程序下载到 STC8A8K64D4-45I-LQFP64
- 串口助手设置:115200, 8, N, 1,以
\r\n 结尾发送指令
- 测试指令:
- 发送
BREATH → 观察P2.0上LED呼吸效果
- 发送
STOP → 停止呼吸
- 发送
PWM=500 → 固定50%占空比
- 调节P1.0电位器 → 自动分段调节亮度
- 按下P3.2按键 → 串口收到
KEY_SHORT 或 KEY_LONG
信息来源:以上寄存器配置参考自《STC8A8K64D4系列数据手册》中"精度可达15位的增强型PWM"章节、"ADC模数转换"章节、"串口通信"章节、"普通I/O口均可中断"章节及"定时器/计数器"章节。
🔍 搜索 "STC8A8K64D4 PWM呼吸灯 ADC采集 串口通信"
⏱️ 查找用时 17.6s | 总回答用时 79.4s