51单片机STC8G1K08输出PWM

一、测试环境

单片机型号:STC8G1K08-38I-TSSOP20,其他型号请自行测试;

IDE:Keil C51;

二、PWM功能配置

PWM输出需要用到PCA功能,相关的寄存器如下:

2.1 引脚选择

本文中,我们使用PCA0模块,将其映射到P1.1引脚,相关寄存器如下图,需要将P_SW1寄存器的B5/B4位,即CCP_S[1:0],设置为00;

2.2 计数脉冲源选择

如上图,PCA模式寄存器(CMOD)的CPS[2:0]位,选择计数脉冲源,因为蜂鸣器的驱动频率由可能是可变的,我们是想得到频率和占空比可调的PWM,选择系统时钟为输入时钟源的话显然不合适,因为时钟频率是固定的,PWM频率也就固定了;笔者使用的开发板,ECI脚没有外接时钟,因此也不能选择这个;选择定时器0的溢出脉冲正好可以满足需求,因为定时器的溢出频率是可以通过寄存器设置的,可变的;本文使用的时钟频率为22.1184MHz;

2.3 PWM频率计算

STC8G1K08的PWM有6/7/8/10四种模式,本文使用分辨率最高的10位PWM;

先看频率的计算公式:

可见频率就是时钟源的频率,除以2^n,其中n为PWM的位数;

可得
f P C A = 2 n f P W M f_{PCA}=2^n{ f_{PWM}} fPCA=2nfPWM

其中 f P C A f_{PCA} fPCA是定时器的溢出频率,定时器的溢出周期为其倒数,则
1 T = 2 n f P W M \frac{1}{T}=2^n{ f_{PWM}} T1=2nfPWM

定时器重载值的计算方法可参考51单片机定时器中断配置,从中可知,
65536 − R e l o a d f O S C = T \frac{65536-Reload}{f_{OSC}}=T fOSC65536−Reload=T

综上可得:
65536 − R e l o a d f O S C = 1 2 n f P W M \frac{65536-Reload}{f_{OSC}}=\frac{1}{2^n{ f_{PWM}}} fOSC65536−Reload=2nfPWM1
f P W M = f O S C 2 n ( 65536 − R e l o a d ) f_{PWM}=\frac{f_{OSC}}{{2^n(65536-Reload})} fPWM=2n(65536−Reload)fOSC

即定时器的重载值计算公式为:
R e l o a d = 65536 − f O S C 2 n f P W M Reload=65536-\frac{f_{OSC}}{2^n{ f_{PWM}}} Reload=65536−2nfPWMfOSC
注意:该重载值Reload是定时器的重载值,而不是PCA的寄存器EPCnH,XCCAPnH[1:0],CCAPnH[7:0]的值!

2.4 PWM占空比计算

PWM的占空比,与计数值寄存器CL、CH,和比较值寄存器EPCnL、XCCAPnL、CCAPnL的比值有关,当计数值小于比较值时,输出低电平,当计数值大于等于比较值时,输出高电平,也就是说,在 2 n 2^n 2n个计数值中,计数值在[0~cmp)区间时(比较值记为cmp),输出低电平,其余的( 2 n − c m p 2^n-cmp 2n−cmp)个为高电平,则占空比计算公式为:
D P W M 100 = 2 n − c m p 2 n \frac{D_{PWM}}{100}=\frac{2^n-cmp}{ 2^n} 100DPWM=2n2n−cmp

为方便计算,占空比的单位为1%,即DPWM=50,则实际占空比为50%;

可得比较值的计算公式如下:
c m p = ( 1 − D P W M 100 ) ∗ 2 n cmp=(1- \frac{D_{PWM}}{100})*2^n cmp=(1−100DPWM)∗2n

2.5 主要代码

芯片手册中关于10位PWM的描述如下图:

配置完成初始化后,只需要在PCA计数寄存器CH、CL溢出后,再次将重载值载入寄存器即可;虽然10位比较器的重载值及比较值是11位,但是从下面的结构框图可以看出,计数器的最高位实际是0,那么重载值和比较值的最高位也是0,否则计数值永远也比不过比较值,因此重载值和比较值实际还是10位的,与PWM的位数一致;

初始化程序:

c 复制代码
/*******************************************************************************
  * 函数名:T0_Init
  * 功  能:定时器T0初始化
  * 参  数:无
  * 返回值:无
  * 说  明:用作PWM的输入时钟源
*******************************************************************************/
void T0_Init(void)
{
	AUXR |= 0x80;//T0为1T模式
	TMOD &= 0xFC;//T0为16位自动重载
}
/*******************************************************************************
  * 函数名:PCA_Init
  * 功  能:PCA初始化
  * 参  数:无
  * 返回值:无
  * 说  明:PCA模块0的PWM模式
*******************************************************************************/
void PCA_Init(void)
{
	P_SW1 &= 0xCF;//P1.1
	CCON = 0x00;//停止PCA计数
	CMOD = 0x04;//计数源选择定时器0的溢出脉冲
	CL = 0x00;//PCA计数器
	CH = 0x00;
	CCAPM0 = 0x42;//允许PCA模块0的PWM输出
	PCA_PWM0 |= 0xC0;//10位PWM
}

PWM驱动程序:

c 复制代码
/*******************************************************************************
  * 函数名:PWM_Start
  * 功  能:开始PWM输出
  * 参  数:无
  * 返回值:无
  * 说  明:无
*******************************************************************************/
void PWM_Start(void)
{
	TF0 = 0;//清除T0溢出中断标志
	TR0 = 1;//定时器T0开始计时

	CR = 1;//启动PCA计数
}
/*******************************************************************************
  * 函数名:PWM_Stop
  * 功  能:停止PWM输出
  * 参  数:无
  * 返回值:无
  * 说  明:无
*******************************************************************************/
void PWM_Stop(void)
{
	TF0 = 0;//清除T0溢出中断标志
	TR0 = 0;//定时器T0停止计时
	ET0 = 0;//禁止定时器中断
	CR = 0;//停止PCA计数
}
/*******************************************************************************
  * 函数名:PWM_SetFreq
  * 功  能:设置PWM频率
  * 参  数:Freq:频率,1Hz~345600Hz
  * 返回值:无
  * 说  明:PCA模块0,计数源选择定时器0的溢出脉冲
			输出引脚P1.1
			PWM频率=PCA时钟源输入频率/(2^n), n=PWM模式的位数,6,7,8,10;此处n=10;
			可知PWM频率>=FOSC/(65536*2^n);当FOSC=22.1184MHz时,n=10,PWM频率>=0.33Hz,
			n=10,f=0.33Hz~21600Hz
*******************************************************************************/
void PWM_SetFreq(uint32_t Freq)
{
	uint16_t u16Reload = 0;//定时器重载值	
	u16Reload = (uint16_t)((uint32_t)65536 - (((uint32_t)FOSC >> 10)/ Freq));
	TL0 = (uint8_t)((u16Reload & 0xFF) >> 0);		//设置定时初始值
	TH0 = (uint8_t)(u16Reload >> 8);		//设置定时初始值
}
/*******************************************************************************
  * 函数名:PWM_SetDuty
  * 功  能:PWM设置占空比
  * 参  数:Duty:占空比,整数,*100
  * 返回值:无
  * 说  明:D/100=(2^n-比较值)/(2^n),则比较值=(100-D)*(2^n)/100,n=10
*******************************************************************************/
void PWM_SetDuty(uint8_t Duty)
{
	uint16_t u16Cmp = 0;//比较值
	uint8_t temp = 0;
	u16Cmp = (100 - (uint16_t)Duty) * 256 / 25;//根据公式化简
	u16Cmp &= 0x03FF;//最多10位
	temp = (u16Cmp >> 4) & 0x30;
	PCA_PWM0 = (PCA_PWM0 & 0xCF) | temp;//先写高2位,赋值给寄存器的bit5/bit4
	CCAP0H   = (uint8_t)(u16Cmp & 0xFF);//低8位	
}
/*******************************************************************************
  * 函数名:PWM_Config
  * 功  能:PWM配置
  * 参  数:Freq:频率
			Duty:占空比,*1%
  * 返回值:无
  * 说  明:无
*******************************************************************************/
void PWM_Config(uint32_t Freq, uint8_t Duty)
{
	PWM_SetFreq(Freq);
	PWM_SetDuty(Duty);
}

三、实际输出效果

设置频率为2000Hz、占空比为50%,通过以下代码设置:

c 复制代码
PWM_Config(2000, 50);

实际输出的PWM波形如下:

可看出占空比为50%,但频率实际为2.17KHz,这是因为实际计算出的定时器重载值为65526,有取整误差,将该值代入频率计算公式,得到实际频率为2160Hz,与逻辑分析仪测得的频率基本一致;

如果设置频率为800Hz,占空比为75%,实际输出的波形如下图:

可看出频率及占空比都较为准确;

四、总结

1.STC8G1K08的PCA模块输出PWM,如果想要频率及占空比均可调,需要使用定时器0的溢出脉冲作为时钟源;

2.虽然规格书上说的重载值、比较值都比PWM的位数多一位,但实际最高位都是0,所以实际操作的位数与PWM的位数一致;

3.由于计算时的取整误差,输出的频率与理论频率可能有误差;

相关推荐
传感器与混合集成电路13 小时前
210℃与175℃高温存储器选型研究:LHM256MB与LDMF4GA-H架构与可靠性对比(上)
嵌入式硬件·能源
时光找茬13 小时前
【瑞萨AI挑战赛-FPB-RA6E2】+ 从零开始:FPB-RA6E2 开箱测评与 e2 studio 环境配置
c++·单片机·边缘计算
17(无规则自律)13 小时前
【CSAPP 读书笔记】第二章:信息的表示和处理
linux·嵌入式硬件·考研·高考
@good_good_study14 小时前
FreeRTOS内存管理
单片机
Hello_Embed15 小时前
libmodbus 移植 STM32(基础篇)
笔记·stm32·单片机·学习·modbus
qq_3975623116 小时前
QT工程 , 生成别的电脑运行的exe程序
嵌入式硬件·qt
qqssss121dfd17 小时前
STM32H750XBH6的ETH模块移植LWIP
网络·stm32·嵌入式硬件
想放学的刺客19 小时前
单片机嵌入式试题(第27期)设计可移植、可配置的外设驱动框架的关键要点
c语言·stm32·单片机·嵌入式硬件·物联网
天昊吖19 小时前
stc8H启用DMA发送后 卡住【踩坑日志】
单片机