好的,我已经查阅了STC8H8K64U的ADC、PWM和串口相关手册内容。现在我来编写一个完整的演示程序。
STC8H8K64U ADC+PWM+串口 综合演示程序
以下是一个完整的单文件C语言演示程序,功能说明如下:
功能概述:
- 使用 ADC通道0(P1.0) 采集模拟电压(如电位器分压)
- 使用 PWMA通道1(PWM1P,默认P1.0,本例切换至P6.0) 输出PWM,占空比跟随ADC值变化
- 使用 串口1(P3.0 RxD, P3.1 TxD) 以115200bps定时上报ADC值和当前PWM占空比
- 主频使用内部IRC 24MHz
/*
* STC8H8K64U ADC + PWM + 串口 综合演示程序
*
* 功能说明:
* 1. ADC通道0 (P1.0) 采集外部模拟电压
* 2. PWMA通道1 (PWM1P) 输出PWM,占空比随ADC值变化
* - PWM1P引脚选择:P6.0(通过PWMA_PS设置)
* - PWM频率 = 24MHz / (PSCR+1) / (ARR+1) = 24MHz / 1 / 1000 = 24KHz
* 3. 串口1 (P3.0 RxD, P3.1 TxD) 115200bps, 8n1
* - 定时通过串口发送ADC值和PWM占空比
* 4. 主频:内部IRC 24MHz
*
* 硬件连接:
* - P1.0 (ADC0) 接电位器中心抽头(分压输入)
* - P6.0 (PWM1P) 接示波器或LED(通过限流电阻)
* - P3.0 (RxD) / P3.1 (TxD) 接USB转串口模块
*
* 下载时选择时钟 24MHz (内部IRC)
*/
#include "stc8h.h"
#include "intrins.h"
#define MAIN_Fosc 24000000UL // 主时钟频率 24MHz
// 串口波特率重装值(定时器2作为波特率发生器,1T模式)
// 波特率 = SYSclk / 4 / (65536 - T2H_T2L)
#define UART_BRT (65536UL - (MAIN_Fosc / 4) / 115200)
// PWM参数
#define PWM_PERIOD 999 // 自动重装载值 (ARR),PWM周期 = 24MHz/1/(999+1) = 24KHz
#define PWM_PSC 0 // 预分频值 (PSCR),PWM时钟不分频
// 全局变量
unsigned int g_adc_value; // ADC转换结果(12位右对齐)
unsigned int g_pwm_duty; // PWM占空比比较值
bit g_uart_busy; // 串口发送忙标志
bit g_1ms_flag; // 1ms定时标志
unsigned int g_tick_count; // 毫秒计数器
// 函数声明
void System_Init(void);
void ADC_Init(void);
unsigned int ADC_Read(unsigned char channel);
void PWM_Init(void);
void PWM_SetDuty(unsigned int duty);
void UART1_Init(void);
void UART1_SendByte(unsigned char dat);
void UART1_SendString(unsigned char *str);
void UART1_SendNumber(unsigned int num);
void Timer0_Init(void);
void delay_ms(unsigned int ms);
/***************************** 主函数 *****************************/
void main(void)
{
unsigned int adc_val;
unsigned int duty_val;
System_Init(); // I/O口初始化
ADC_Init(); // ADC初始化
PWM_Init(); // PWM初始化
UART1_Init(); // 串口1初始化
Timer0_Init(); // 定时器0初始化(1ms基准)
EA = 1; // 开启总中断
UART1_SendString("STC8H8K64U ADC+PWM+UART Demo Start!\r\n");
while (1)
{
if (g_1ms_flag)
{
g_1ms_flag = 0;
g_tick_count++;
// 每500ms采集一次ADC并更新PWM,同时通过串口输出
if (g_tick_count >= 500)
{
g_tick_count = 0;
// 读取ADC通道0(P1.0),12位右对齐
adc_val = ADC_Read(0);
g_adc_value = adc_val;
// 将12位ADC值映射到PWM占空比(0 ~ PWM_PERIOD)
// ADC范围0~4095,映射到0~PWM_PERIOD
duty_val = (unsigned long)adc_val * PWM_PERIOD / 4095;
g_pwm_duty = duty_val;
PWM_SetDuty(duty_val);
// 通过串口发送数据
UART1_SendString("ADC:");
UART1_SendNumber(adc_val);
UART1_SendString(" PWM:");
UART1_SendNumber(duty_val);
UART1_SendString("\r\n");
}
}
}
}
/********************* 系统初始化(I/O口) *********************/
void System_Init(void)
{
P_SW2 |= 0x80; // 使能访问XFR(扩展寄存器)
// 所有I/O口设置为准双向口
P0M0 = 0x00; P0M1 = 0x00;
P1M0 = 0x00; P1M1 = 0x00;
P2M0 = 0x00; P2M1 = 0x00;
P3M0 = 0x00; P3M1 = 0x00;
P4M0 = 0x00; P4M1 = 0x00;
P5M0 = 0x00; P5M1 = 0x00;
P6M0 = 0x00; P6M1 = 0x00;
P7M0 = 0x00; P7M1 = 0x00;
// P1.0设置为高阻输入(ADC通道)
P1M0 &= ~0x01;
P1M1 |= 0x01;
}
/********************* ADC初始化 *********************/
void ADC_Init(void)
{
ADCTIM = 0x3F; // 设置ADC内部时序:CSSETUP=0, CSHOLD=01, SMPDUTY=11111
ADCCFG = 0x2F; // RESFMT=1(右对齐),SPEED=1111(最慢时钟,SYSclk/2/16)
ADC_CONTR = 0x80; // 打开ADC电源
delay_ms(2); // 等待ADC电源稳定(约1ms)
}
/********************* ADC读取函数(查询方式) *********************/
unsigned int ADC_Read(unsigned char channel)
{
unsigned int res;
ADC_CONTR = 0x80 | 0x40 | (channel & 0x0F); // 启动ADC转换,选择通道
_nop_();
_nop_();
while (!(ADC_CONTR & 0x20)); // 等待ADC_FLAG置位
ADC_CONTR &= ~0x20; // 清除ADC_FLAG
// 12位右对齐:ADC_RES[3:0]为高4位,ADC_RESL[7:0]为低8位
res = ((unsigned int)ADC_RES << 8) | ADC_RESL;
return res;
}
/********************* PWM初始化(PWMA通道1) *********************/
void PWM_Init(void)
{
// PWMA通道1输出脚选择:P6.0 (PWM1P)
PWMA_PS = 0x02; // C1PS[1:0]=10,选择P6.0/P6.1
// 配置通道1为PWM模式1输出
PWMA_CCER1 = 0x00; // 写CCMRx前必须先关闭CCxE
PWMA_CCMR1 = 0x60; // OC1M[2:0]=110(PWM模式1),OC1PE=1(预装载使能)
PWMA_CCER1 = 0x01; // CC1E=1(使能输出),CC1P=0(高电平有效)
// 设置PWM周期和预分频
PWMA_PSCRH = (unsigned char)(PWM_PSC >> 8);
PWMA_PSCRL = (unsigned char)(PWM_PSC);
PWMA_ARRH = (unsigned char)(PWM_PERIOD >> 8);
PWMA_ARRL = (unsigned char)(PWM_PERIOD);
// 初始占空比50%
PWMA_CCR1H = (unsigned char)((PWM_PERIOD / 2) >> 8);
PWMA_CCR1L = (unsigned char)(PWM_PERIOD / 2);
// 使能PWM1P输出
PWMA_ENO = 0x01; // ENO1P=1
// 使能主输出并启动计数器
PWMA_BKR = 0x80; // MOE=1(主输出使能)
PWMA_CR1 = 0x01; // CEN=1(开始计时)
}
/********************* 设置PWM占空比 *********************/
void PWM_SetDuty(unsigned int duty)
{
if (duty > PWM_PERIOD)
duty = PWM_PERIOD;
PWMA_CCR1H = (unsigned char)(duty >> 8);
PWMA_CCR1L = (unsigned char)(duty);
}
/********************* 串口1初始化(115200bps, 8n1) *********************/
void UART1_Init(void)
{
unsigned int brt;
brt = (unsigned int)UART_BRT;
// 配置串口1模式1(8位UART,可变波特率)
SCON = 0x50; // SM0=0, SM1=1(模式1), REN=1(允许接收)
// 使用定时器2作为波特率发生器
AUXR |= 0x01; // S1ST2=1,选择定时器2作为串口1波特率发生器
AUXR |= 0x04; // T2x12=1,定时器2使用1T模式
T2H = (unsigned char)(brt >> 8);
T2L = (unsigned char)(brt);
AUXR |= 0x10; // T2R=1,启动定时器2
g_uart_busy = 0;
ES = 1; // 使能串口1中断
}
/********************* 串口1发送一个字节(中断方式) *********************/
void UART1_SendByte(unsigned char dat)
{
while (g_uart_busy); // 等待上次发送完成
g_uart_busy = 1;
SBUF = dat;
}
/********************* 串口1发送字符串 *********************/
void UART1_SendString(unsigned char *str)
{
while (*str)
{
UART1_SendByte(*str++);
}
}
/********************* 串口1发送数字(十进制) *********************/
void UART1_SendNumber(unsigned int num)
{
unsigned char buf[6];
unsigned char i;
unsigned char len;
if (num == 0)
{
UART1_SendByte('0');
return;
}
len = 0;
while (num > 0)
{
buf[len++] = (unsigned char)(num % 10) + '0';
num /= 10;
}
for (i = len; i > 0; i--)
{
UART1_SendByte(buf[i - 1]);
}
}
/********************* 定时器0初始化(1ms中断) *********************/
void Timer0_Init(void)
{
// 定时器0:1T模式,16位自动重装载
// 1ms = 24000000 / 1000 = 24000 个时钟周期
// 重装值 = 65536 - 24000 = 41536 = 0xA240
AUXR |= 0x80; // T0x12=1,定时器0使用1T模式
TMOD &= 0xF0; // 定时器0模式0(16位自动重装载)
TH0 = 0xA2;
TL0 = 0x40;
ET0 = 1; // 使能定时器0中断
TR0 = 1; // 启动定时器0
}
/********************* 毫秒延时(查询方式) *********************/
void delay_ms(unsigned int ms)
{
unsigned int i, j;
for (i = 0; i < ms; i++)
{
// 24MHz下约1ms
for (j = 0; j < 6000; j++)
{
_nop_();
}
}
}
/********************* 串口1中断服务函数 *********************/
void UART1_ISR(void) interrupt 4
{
if (TI)
{
TI = 0;
g_uart_busy = 0;
}
if (RI)
{
RI = 0; // 清除接收标志(本例未使用接收)
}
}
/********************* 定时器0中断服务函数 *********************/
void Timer0_ISR(void) interrupt 1
{
g_1ms_flag = 1;
}
程序说明
| 模块 |
配置详情 |
| 系统时钟 |
内部IRC 24MHz |
| ADC |
通道0(P1.0),12位分辨率,右对齐,查询方式读取 |
| PWM |
PWMA通道1(PWM1P),引脚选择P6.0,频率 24KHz,占空比0~100% |
| 串口1 |
P3.0(RxD)/P3.1(TxD),115200bps,8n1,中断方式发送 |
| 定时器0 |
1ms中断基准,用于定时触发ADC采集和串口输出(每500ms一次) |
使用步骤
-
硬件连接:
- P1.0 接电位器中心抽头(两端分别接VCC和GND)
- P6.0 接示波器或LED(串330Ω电阻到GND)
- P3.0/P3.1 接USB转串口模块(CH340G/CP2102等)
-
下载设置:
- 打开 AiCube-ISP(STC-ISP) 下载软件
- 选择型号 STC8H8K64U
- 设置 IRC频率为24MHz
- 加载本程序生成的HEX文件,下载到芯片
-
观察结果:
- 串口助手(115200,8n1)每500ms收到类似数据:
ADC:2048 PWM:499
- 旋转电位器,ADC值和PWM占空比同步变化
- 用示波器观察P6.0,可见PWM波形占空比随电位器调节而变化
注意:如果使用P1.0作为ADC输入,而PWM默认也在P1.0输出,本程序已通过 PWMA_PS 将PWM1P切换到P6.0,避免引脚冲突。
🔍 搜索 "STC8H8K64U ADC+PWM+串口的演示程序 给一个"
📌 相关论坛讨论
- PWM做DAC完整演示程序,用内部真12位ADC来保证精度@8H8K64U
- 老刘爱捣鼓示波器,开源程序,OLED12864, ADC, PWM
- 54-DMA-ADC采样数据自动存储,STC8H8K64U实验箱演示程序, STC8H2K32U, STC8H2K12U, ST
⏱️ 查找用时 12.4s | 总回答用时 44.5s