本文将使用STC12C5A60S2配置PWM波,驱动SG90舵机。

采用的开发板包括了CH340芯片,因此下载程序只需要使用MicroUSB转USB连接线使用STC-ISP.exe软件下载程序即可。

在这里插入图片描述

1 芯片资源

芯片型号STC12C5A60S2,增强型8051 CPU,1T,单时钟/机器周期,指令代码兼容传统8051,工作电压5.5V-3.5V,工作频率范围0-35MHz,共有4个16位定时器(包括2个16位定时器T0和T1,2路PCA模块实现2个16为定时器),拥有2路PWM(PCA的PWM模式)。

2 舵机控制信号

舵机分为三根线,包括:VCC(5V)、GND和PWM。
在这里插入图片描述
PWM波的信号频率在50Hz左右,高电平占空比为2.5%-12.5%,也就是一个周期20ms,高电平占0.5-2.5ms。
在这里插入图片描述

2 STC12设置PCA的PWM模式

STC12C5A60S2系列单片机集成了两路可编程计数器阵列(PCA)模块,可用于软件定时器、外部脉冲的捕捉、高速输出以及脉宽调制(PWM)输出。
PCA可以控制2路PWM波。

相关寄存器列表:

寄存器名称功能
CCONPCA控制寄存器
CMODPCA模式寄存器
CCAPM0PCA模块模式0寄存器
CCAPM1PCA模块模式1寄存器
CLCPA基位定时器低位(PCA Base Timer Low)
CHCPA基位定时器高位(PCA Base Timer High)
CCAP0LPCA模块0捕获寄存器低位
CCAP0HPCA模块0捕获寄存器高位
CCAP1LPCA模块1捕获寄存器低位
CCAP1HPCA模块1捕获寄存器高位
PCA_PWM0PCA PWM模式辅助寄存器0
PCA_PWM1PCA PWM模式辅助寄存器1
AUXR1辅助寄存器1(能够设置输出引脚)

2.1 CCON:PCA控制寄存器

功能:控制PCA溢出标志位、控制PCA运行状态。PCA模块0和1的中断标志。

寄存器名称地址B7B6B5B4B3B2B1B0
CCON0xD8CFCR----CCF1CCF0
  • CF:PCA计数器阵列溢出标志位。PCA计数器溢出时,CF由硬件置位为1。如果CMOD.0=1(ECF=1),则CF可以用来产生中断。CF可由硬件或者软件置位(为1),但只可通过软件清零。
  • CR:PCA计数器阵列运行控制位。软件置位(为1),启动PCA计数。软件清零,关闭PCA计数。

2.2 CMOD:PCA工作模式寄存器

功能:控制空闲模式PCA计数状态、PCA计数脉冲源选项、PCA溢出中断使能。

寄存器名称地址B7B6B5B4B3B2B1B0
CMOD0xD9CIDL---CPS2CPS1CPS0ECF

CIDL和ECF在此不做介绍,主要介绍CPSn的PCA计数脉冲源选择控制位。

CPS2CPS1CPS0选择PCA时钟源输入
0000,SYSCLK/12
0011,SYSCLK/2
0102,定时器0的溢出脉冲。通过改变定时器0的分频率,能够实现SYSCLK的1-256分频。
0113,ECI/P1.2(或P4.1)脚输入的外部时钟(最大速率为SYSCLK/2)
1004,SYSCLK
1015,SYSCLK/4
1106,SYSCLK/6
1117,SYSCLK/8

但PWM频率不等于PCA频率,PWM波由于要控制占空比,因此每256个PCA周期设置为1个PWM波周期,一部分高电平、一部分低电平。PWM频率=PCA频率/256

本文主要关注定时器0溢出脉冲输入PCA。
在这里插入图片描述

2.3 CCAPM0:PCA比较/捕获寄存器

PCA模块0的比较/捕获寄存器。

寄存器名称地址B7B6B5B4B3B2B1B0
CCAPM00xDA-ECOM0CAPP0CAPN0MAT0TOG0PWM0ECCF0
  • ECOM0允许比较器功能控制位。当ECOM0=1,允许比较器功能。
  • CAPP0:正捕获控制位。当CAPP0=1时,允许上升沿捕获。
  • CAPN0:负捕获控制位。当CAPN0=1时,允许下降沿捕获。
  • MAT0:匹配控制位。当MAT0=1时,PCA计数器与模块的比较/捕获寄存器的值的匹配将置位CCON寄存器的中断标志为CCF0。
  • TOG0:翻转控制位、当TOG0=1时,工作在PCA高速输出模式,PCA计数器的值与模块的比较/捕获寄存器的值的匹配将使CCP0脚翻转。
  • PWM0:脉宽调节模式。当PWM0=1时,允许,CEX0脚用作脉宽调节输出。
  • ECCF0:使能CCF0中断。使能寄存器CCON的比较/捕获标志CCF0,用来产生中断。

模式设置:
在这里插入图片描述
由于需要使用PWM模式输出,无需中断,所以CCAPM0=0x42。

在这里插入图片描述

2.4 CL、CH:PCA的16位计数器

CL为低8位计数器,CH为高8位计数器。
CL和CH用来保存PCA的装载值,复位值为0x00。

2.5 CCAP0L、CCAP0H:PCA捕获/比较寄存器

CCAP0L为低位字节、CCAP0H为高位字节。当PCA模块用于捕获或比较时,他们用于保存各个模块的16位捕捉计数值。当PCA模块用于PWM模式时,他们用来控制输出的占空比。 复位值均为0x00。

2.6 AUXR1:辅助寄存器引脚功能

辅助寄存器1将单片机的PCA/PWM功能从P1口设置到P4口。

寄存器名称地址B7B6B5B4B3B2B1B0
AUXR10xA2-PCA_P4SPI_P4S2_P4GF2ADRJ-DPS
  • PCA_ P4:0,缺省PCA在P1口。1,PCA/PWM从P1口切换到P4口;ECI从P1.2切换到P4.1口;PCA0/PWM0从P1.3切换到P4.2口;PCA 1/PWM1从P1.4切换到P4.3口。
  • SPI_ P4:0,缺省SPI在P1口。1,SPI从P1口切换到P4口;SPICLK从P1.7切换到P4.3口;MISO从P1.6切换到P4.2口;MOSI从P1.5切换到P4. I口;SS从P1.4切换到P4.0口
  • S2_ P4:0,缺省UART2在P1口。1,UART2从P1口切换到P4口;TxD2从P1.3切换到P4.3口;RxD2从P1.2切换到P4.2口。
  • GF2:通用标志位
  • ADRJ:0,10位A/D转换结果的高8位放在ADC_RES寄存器,低2位放在ADC_RESL寄存器。1,10位A/D转换结果的最高2位放在ADC_RES寄存器的低2位,低8位放在ADC_RESL寄存器。
  • DPS:0,使用缺省数据指针DPTR0。1,使用另一个数据指针DPTR1。

3 STC12设置定时器0

3.1 TCON:定时器/计数器控制寄存器

TCON为定时器/计数器T0、T1的控制寄存器,同时也锁存T0、T1溢出中断源和外部请求中断源。本文内只需要T0。

寄存器名称地址B7B6B5B4B3B2B1B0
TCON0x88TF1TR1TF0TR0IE1IT1IE0IT0
  • TF0:定时器/计数器T0溢出中断标志。T0被允许计数以后,从初始开始加1计数,当最高位产生溢出时,由硬件置“1”TF0,向CPU请求中断,一直保持CPU响应该中断时,才由硬件清“0”TF0。
  • TR0:定时器T0的运行控制位。该位由软件置位和清零。当GATE(TMOD.3)=0,TR0=1时就运行T0开始计数,TR0=0时禁止T0计数。当GATE(TMOD.3)=1,TR1=0且INT0输入高电平时由硬件清“0”IE1。
  • IE0:外部中断0请求源(INT0/P3.2)标志。IE0=1外部中断0向CPU请求中断,当CPU响应外部中断时,由硬件清“0”IE0(边沿触发方式)。
  • IT0:外部中断0触发方式控制位。IT0=0时,外部中断0为低电平触发方式,当INT0输入低电平时,置位IE0。采用低电平触发方式时,外部中断源(输入到INT0)必须保持低电平有效,直到该中断被CPU响应,同时在该中断服务程序执行完之前,外部中断源必须被清除(P3.2要变高),否则将产生另一次中断。当IT0=1时,则外部中断0(INT0)端口由“1”->“0”下降沿跳变,激活中断请求标志位IE1,向主机请求中断处理。

3.2 TMOD:定时器/计数器工作模式寄存器

定时和计数功能由特殊功能寄存器TMOD的控制位C/T进行选择。

寄存器名称地址B7B6B5B4B3B2B1B0
TMOD0x89GATEC/TM1M0GATEC/TM1M0

B7-B4为定时器1的控制位,B3-B0为定时器0的控制位。

  • GATE:置1时只有在INT0脚为高电平及TR0控制位置1时才可打开定时器/计数器0。
  • C/T:定时器/计数器控制位,清零则用作定时器(从内部系统时钟输入),置1用作计数器(从T0/P3.4脚输入)。
  • M1、M0:定时器/计数器模式选择。
M1M0功能
0013位定时器/计数器,兼容8048定时模式,TL0只用低5位参与分频,TH0整个8位全用。
0116位定时器/计数器,TL0、TH0全用
108位自动重装载定时器,当溢出时将TH0存放的值自动重装入TL0
11定时器0此时作为双8位定时器/计数器。TL0作为一个8位定时器/计数器,通过标准定时器0的控制位控制。YH0仅作为一个8位定时器,由定时器1的控制位控制。

3.3 TL0、TH0

此寄存器用于控制PWM的占空比。

3.4 AUXR:辅助寄存器

寄存器名称地址B7B6B5B4B3B2B1B0
AUXR0x8ET0x12T1x12UART_M0x6BRTRS2SMODBRTx12EXTRAMS1BRS

3.5 WAKE_CLKO:时钟输出和掉电唤醒寄存器

寄存器名称地址B7B6B5B4B3B2B1B0
WAKE_CLKO0x8FPCAWAKEUPRXD_PIN_IET1_PIN_IET0_PIN_IELVD_WAKEBRTCLKOT1CLKOT0CLKO
  • PCAWAKEUP:在掉电模式下,是否允许PCA上升沿/下降沿中断唤醒powerdown。0,禁止;1,允许。
  • RXD_PIN_IE:掉电模式下,允许P3.0(RXD)下降沿置RI,也能使RXD唤醒powerdown。0,禁止;1,允许。
  • T0_PIN_IE:掉电模式下,允许T0/P3.4脚下降沿置T0中断标志,也能使T0脚唤醒powerdown。0,禁止;1,允许。
  • LVD_WAKE:掉电模式下,是否允许EX_LVD/P4.6低压检测中断唤醒CPU。0,禁止;1,允许。
  • BRTCLKO:是否允许将P1.0脚配置为独立波特率发生器(BRT)的时钟输出CLKOUT2。1,允许;0,不允许。
  • T0CLKO:是否允许将P3.4/T0脚配置为定时器T0的时钟输出CLKOUT0。1,允许;0,不允许。

4 定时器T0与PWM配置思路

芯片晶振频率为11.0592MHz,输入芯片内得到SYSCLK=11059200Hz。

由于PWM波需要50Hz,PCA将一个周期时长固定划分为256个部分,因此PWM波频率=PCA输入频率/256
所以PCA输入频率为50*256=12800Hz。

CMOD寄存器中可以设置PCA的输入模式,输入模式分为8种,可以对SYSCLK脉冲、T0溢出脉冲进行分频。

在这里插入图片描述

由于SYSCLK/12=11059200/12=921600Hz,远大于PCA输入频率12800Hz,输入模式:0、1、3、4、5、6、7都无法完成分频。因此需要采用输入模式2:定时器0计数溢出脉冲,在1T情况下,分频能够达到SYSCLK/num,num可以为1~256。

芯片有1T模式和12T模式,12T模式是12个周期执行一条指令,兼容8051。

定时器0如果设置为1T模式,采用最大的分频数256,能够输入到PCA的最小频率为SYSCLK/256=11059200/256=43200Hz,也无法满足12800Hz的要求。

因此必须采用12T模式,先将SYSCLK进行12分频,得到频率SYSCLK/12=11059200/12=92600Hz,然后再在定时器0中得到分频。

在这里插入图片描述

如图所示,定时器T0采用12T模式(T0x12=0或AUXR.7=0)、定时器(C/T-=0或TMOD.2=0)、模式2:8为自动重载定时器(M1/M0=1/0或TMOD.1/0=1/0)、关闭外部输入(GATE=0或TMOD.3=0)、打开定时器运行控制位(TR0=1或TCON.5=1)。

TL0和TH0都是8位寄存器,两个都通过软件设置相同的值,当TL0捕获到左侧传来的脉冲,从设置好的值到255逐渐递增,然后溢出,这就是定时器的分频过程。计时器T0的溢出脉冲为PCA提供输入信号,同时TH0也会将存储的值传递给TL0,以便进行下一次分频。

上文已经得出输入定时器频率为921600Hz,如果想通过分频得到PCA所需的12800Hz频率,需要分进行921600/12800=72分频。
因此设置定时器0的两个寄存器TL0和TH0为256-72=184。

小结:
1、晶振频率11059200Hz。
2、定时器0采用12T预先分频,得到11059200/12=921600Hz。
3、定时器0设置TL0=TH0=256-72=184,为72分频,得到定时器0溢出频率921600/72=12800Hz。
4、定时器0溢出频率输入到PCA内,PWM波频率为12800/256=50Hz。
5、修改PWM波占空比,设置CCP0L=CCP0H=n(0<n<256),n越大,占空比Duty越大,从0%-100%。

代码如下:

#include "reg51.h"
#include "intrins.h"

/*Declare SFR associated with the PCA */
sfr CCON        =   0xD8;           //PCA control register
sbit CCF0       =   CCON^0;         //PCA module-0 interrupt flag
sbit CCF1       =   CCON^1;         //PCA module-1 interrupt flag
sbit CR         =   CCON^6;         //PCA timer run control bit
sbit CF         =   CCON^7;         //PCA timer overflow flag
sfr CMOD        =   0xD9;           //PCA mode register
sfr CL          =   0xE9;           //PCA base timer LOW
sfr CH          =   0xF9;           //PCA base timer HIGH
sfr CCAPM0      =   0xDA;           //PCA module-0 mode register
sfr CCAP0L      =   0xEA;           //PCA module-0 capture register LOW
sfr CCAP0H      =   0xFA;           //PCA module-0 capture register HIGH
sfr CCAPM1      =   0xDB;           //PCA module-1 mode register
sfr CCAP1L      =   0xEB;           //PCA module-1 capture register LOW
sfr CCAP1H      =   0xFB;           //PCA module-1 capture register HIGH
sfr PCAPWM0     =   0xf2;
sfr PCAPWM1     =   0xf3;

sfr AUXR = 0x8E;
sfr WAKE_CLKO = 0x8F;

void Timer0_Init(int i);
void PWM_Init(int i ,int j);
void PWM_Config(float duty);
void delay_ms(int num);

void main()
{
	// 初始化定时器0,确定溢出频率
	Timer0_Init(50);
	// 初始化PWM外设
	PWM_Init(0x04,0x42);
	while(1)
	{
		// 设置占空比
		PWM_Config(12.5);
		delay_ms(256);
		// 设置占空比
		PWM_Config(10);
		delay_ms(256);
		// 设置占空比
		PWM_Config(7.5);
		delay_ms(256);
		// 设置占空比
		PWM_Config(5);
		delay_ms(256);
		// 设置占空比
		PWM_Config(2.5);
		delay_ms(256);
		
	}
}


// i为PWM波频率
void Timer0_Init(int i)
{
	int j = 0;
	// int k = 0;
	// k = i * 256; 	// 定时器0输出的溢出脉冲,输入到PCA的频率
	j = (int)(3600/i);	// 定时器0分频数(11059200/12 /i /256 = 3600/i)
	EA = 0;				// 禁止中断
	TMOD |= 0x02;		// 配置为八位自动重装载
	AUXR |= 0x00;		// 12T模式,SYSCLK/12=11059200/12=921600Hz
	TL0 = 256-j;
	TH0 = 256-j;		// 原来是256-j
	TR0 = 1;				// 启动计数器计数
	TF0 = 0;				// 清空溢出标志位
	
	WAKE_CLKO = 0x01;
}

void PWM_Init(int i,int j)
{
	CL = 0x00;
	CH = 0x00;
	CMOD = i;
	CCAPM0 = j;		// 使用比较功能,脉宽调节模式
	CR = 1;		// 启动PCA计数器
}

void PWM_Config(float duty) // 范围为2.5-12.5
{
	int width = 0;
	width = (int)(duty*2.56);//-1+0.5;
	// duty写的是12.5,实际是12.5%,需要duty/100*256
	CCAP0H = 256-width;
	CCAP0L = CCAP0H;
}

void delay_ms(int num)
{
	int i,j;
	for(i=0; i<num;i++){
		for(j=0; j<2560;j++);
	}
}
Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐