STC实验箱4
IAP15W4K58S4
Keil uVision V5.29.0.0
PK51 Prof.Developers Kit Version:9.60.0.0


硬知识

硬件知识

       摘自《STC15系列单片机器件手册
       STC15系列部分单片机集成了3路可编程计数器阵列(CCP/PCA)模块(STC15W4K32S4系列单片机只有两路CCP/PCA),可用于软件定时器、外部脉冲的捕捉、高速脉冲输出以及脉宽调制(PWM)输出。
       下表总结了STC15系列单片机内部集成了CCP/PCA/PM功能的单片机型号:
在这里插入图片描述
       上表中√表示对应的系列有相应的功能。
在这里插入图片描述

CCP/PWM/PCA模块的结构

       STC15系列部分单片机有3路可编程计数器阵列CCP/PCA/PWM(通过AUXR1/P_SW1寄存器可以设置CCP/PCA/PWM从P1口切换到P2口切换到P3口)。
       PCA含有一个特殊的16位定时器,有3个16位的捕获/比较模块与之相连,如下图所示。
在这里插入图片描述
       每个模块可编程工作在4种模式下:上升/下降沿捕获、软件定时器、高速脉冲输出或可调制脉冲输出。
       16位PCA定时器/计数器是3个模块的公共时间基准,其结构如下图所示。
在这里插入图片描述

软件知识

       摘自《STC库函数使用参考

PCA初始化函数

PCA_Init
在这里插入图片描述
PCA_id:选择要初始化的PCA通道:在这里插入图片描述
PCA_InitTypeDef的定义见于文件”PCA.H”.

typedef struct
{
	u8 PCA_IoUse;
	u8 PCA_Clock; 
	u8 PCA_Mode; 
	u8 PCA_PWM_Wide;
	u8 PCA_Interrupt_Mode; 
	u8 PCA_Polity;
	u16 PCA_Value;
} PCA_InitTypeDef;

PCA_IoUse:选择PCA使用的IO:初始化PCA_Counter时的取值,初始化PCA0~PCA2时忽略
在这里插入图片描述
PCA_Clock:选择PCA使用的时钟:初始化PCA_Counter时的取值,初始化PCA0~PCA2时忽略在这里插入图片描述
PCA_Mode:设置PCA通道的工作模式:初始化PCA0~PCA2时的取值,初始化PCA_Counter时忽略
在这里插入图片描述
PCA_PWM_Wide:设置PCA通道工作于PWM模式时的PWM宽度:初始化PCA0~PCA2工作于PWM模式时的取值,初始化PCA_Counter时或PCA通道其它模式时忽略
在这里插入图片描述
PCA_Interrupt_Mode:中断允许或禁止:初始化PCA0~PCA2时的取值,初始化PCA_Counter时忽略在这里插入图片描述
注意:上面的参数可以做如下组合:

PCA_InitStructure.PCA_Interrupt_Mode = PCA_Fall_Active | ENABLE; //下降沿中断,允许中断。
PCA_InitStructure.PCA_Interrupt_Mode = PCA_Rise_Active | ENABLE; //上升沿中断,允许中断。
PCA_InitStructure.PCA_Interrupt_Mode = PCA_Rise_Active | PCA_Fall_Active | ENABLE; //上升沿、下降沿中断,允许中断。

如果后面使用了| DISABLE,则中断被禁止。

PCA_Polity:中断的优先级:初始化PCA_Counter时的取值,初始化PCA0~PCA2时忽略在这里插入图片描述
PCA_Value:设置PCA通道的初值。初始化PCA0~PCA2时的取值,初始化PCA_Counter时忽略

PWM更新占空比函数

UpdatePwm
在这里插入图片描述

测试程序

PWM模式产生PWM信号

PCA.c里的UpdatePwm函数默认是被注释掉的,需要解除注释:
在这里插入图片描述

main.c

#include "./Drivers/config.h"
#include "./Drivers/delay.h"

#include "./Drivers/GPIO.h"
#include "./Drivers/PCA.h"

void GPIO_config(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;					//结构定义
	GPIO_InitStructure.Mode = GPIO_OUT_PP;					//指定IO的输入或输出方式,GPIO_PullUp,GPIO_HighZ,GPIO_OUT_OD,GPIO_OUT_PP
	GPIO_InitStructure.Pin  = GPIO_Pin_5 | GPIO_Pin_6;		//指定要初始化的IO, 或操作
	GPIO_Inilize(GPIO_P2, &GPIO_InitStructure);				//P2.5、P2.6初始化为推挽输出
}

void PCA_config(void)
{
	PCA_InitTypeDef PCA_InitStructure;

	PCA_InitStructure.PCA_Mode     = PCA_Mode_PWM;		//PCA_Mode_PWM, PCA_Mode_Capture, PCA_Mode_SoftTimer, PCA_Mode_HighPulseOutput
	PCA_InitStructure.PCA_PWM_Wide = PCA_PWM_8bit;		//PCA_PWM_8bit, PCA_PWM_7bit, PCA_PWM_6bit
	PCA_InitStructure.PCA_Interrupt_Mode = DISABLE;		//PCA_Rise_Active, PCA_Fall_Active, ENABLE, DISABLE
	PCA_InitStructure.PCA_Value    = (u16)((1 << 8) * (1 - 0.5));//对于软件定时, 为匹配比较值
	PCA_Init(PCA0,&PCA_InitStructure);

	PCA_InitStructure.PCA_Mode     = PCA_Mode_PWM;		//PCA_Mode_PWM, PCA_Mode_Capture, PCA_Mode_SoftTimer, PCA_Mode_HighPulseOutput
	PCA_InitStructure.PCA_PWM_Wide = PCA_PWM_7bit;		//PCA_PWM_8bit, PCA_PWM_7bit, PCA_PWM_6bit
	PCA_InitStructure.PCA_Interrupt_Mode = DISABLE;		//PCA_Rise_Active, PCA_Fall_Active, ENABLE, DISABLE
	PCA_InitStructure.PCA_Value    = (u16)((1 << 7) * (1 - 0.5));//对于软件定时, 为匹配比较值
	PCA_Init(PCA1,&PCA_InitStructure);

	PCA_InitStructure.PCA_Clock    = PCA_Clock_1T;		//PCA_Clock_1T, PCA_Clock_2T, PCA_Clock_4T, PCA_Clock_6T, PCA_Clock_8T, PCA_Clock_12T, PCA_Clock_Timer0_OF, PCA_Clock_ECI
	PCA_InitStructure.PCA_IoUse    = PCA_P24_P25_P26_P27;//PCA_P12_P11_P10_P37, PCA_P34_P35_P36_P37, PCA_P24_P25_P26_P27
	PCA_InitStructure.PCA_Interrupt_Mode = DISABLE;		//ENABLE, DISABLE
	PCA_InitStructure.PCA_Polity   = PolityHigh;		//优先级设置	PolityHigh,PolityLow
	PCA_Init(PCA_Counter,&PCA_InitStructure);
}

void main(void)
{
	GPIO_config();
	PCA_config();

	UpdatePwm(PCA0, (1 << 8) * (1 - 0.75));
	UpdatePwm(PCA1, (1 << 7) * (1 - 0.25));

	while(1)
	{

	}
}

实验现象

PWM频率 = PCA时钟频率 / 2^{PWM位数}



在这里插入图片描述



30MHz / 1 / 2^8 = 117187.5 Hz


在这里插入图片描述



30MHz / 1 / 2^7 = 234375 Hz



在这里插入图片描述

16位软件定时器模式模式产生PWM信号

PWM频率 = PCA时钟频率 / PWMx_DUTY

修改PCA.h中的PWMx_DUTY
在这里插入图片描述
修改PCA_Handler内的对应引脚
在这里插入图片描述

//========================================================================
// 函数: void	PCA_Handler (void) interrupt PCA_VECTOR
// 描述: PCA中断处理程序.
// 参数: None
// 返回: none.
// 版本: V1.0, 2012-11-22
//========================================================================
void	PCA_Handler (void) interrupt PCA_VECTOR
{
	if(CCF0)		//PCA模块0中断
	{
		CCF0 = 0;		//清PCA模块0中断标志
		if(P25)	CCAP0_tmp += PCA_Timer0;	//输出为高电平,则给影射寄存器装载高电平时间长度
		else	CCAP0_tmp += PWM0_low;	//输出为低电平,则给影射寄存器装载低电平时间长度
		CCAP0L = (u8)CCAP0_tmp;			//将影射寄存器写入捕获寄存器,先写CCAP0L
		CCAP0H = (u8)(CCAP0_tmp >> 8);	//后写CCAP0H
	}

	if(CCF1)	//PCA模块1中断
	{
		CCF1 = 0;		//清PCA模块1中断标志
		if(P26)	CCAP1_tmp += PCA_Timer1;	//输出为高电平,则给影射寄存器装载高电平时间长度
		else	CCAP1_tmp += PWM1_low;	//输出为低电平,则给影射寄存器装载低电平时间长度
		CCAP1L = (u8)CCAP1_tmp;			//将影射寄存器写入捕获寄存器,先写CCAP0L
		CCAP1H = (u8)(CCAP1_tmp >> 8);	//后写CCAP0H
	}

	if(CCF2)	//PCA模块2中断
	{
		CCF2 = 0;		//清PCA模块1中断标志
		if(P27)	CCAP2_tmp += PCA_Timer2;	//输出为高电平,则给影射寄存器装载高电平时间长度
		else	CCAP2_tmp += PWM2_low;	//输出为低电平,则给影射寄存器装载低电平时间长度
		CCAP2L = (u8)CCAP2_tmp;			//将影射寄存器写入捕获寄存器,先写CCAP0L
		CCAP2H = (u8)(CCAP2_tmp >> 8);	//后写CCAP0H
	}

	if(CF)	//PCA溢出中断
	{
		CF = 0;			//清PCA溢出中断标志
	}

}

main.c

#include "./Drivers/config.h"
#include "./Drivers/delay.h"

#include "./Drivers/GPIO.h"
#include "./Drivers/PCA.h"

void GPIO_config(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;					//结构定义
	GPIO_InitStructure.Mode = GPIO_OUT_PP;					//指定IO的输入或输出方式,GPIO_PullUp,GPIO_HighZ,GPIO_OUT_OD,GPIO_OUT_PP
	GPIO_InitStructure.Pin  = GPIO_Pin_5 | GPIO_Pin_6;		//指定要初始化的IO, 或操作
	GPIO_Inilize(GPIO_P2, &GPIO_InitStructure);				//P2.5、P2.6初始化为推挽输出
}

void PCA_config(void)
{
	PCA_InitTypeDef PCA_InitStructure;

	PCA_InitStructure.PCA_Mode     = PCA_Mode_HighPulseOutput;	//PCA_Mode_PWM, PCA_Mode_Capture, PCA_Mode_SoftTimer, PCA_Mode_HighPulseOutput
	PCA_InitStructure.PCA_PWM_Wide = 0;							//PCA_PWM_8bit, PCA_PWM_7bit, PCA_PWM_6bit
	PCA_InitStructure.PCA_Interrupt_Mode = ENABLE;				//PCA_Rise_Active, PCA_Fall_Active, ENABLE, DISABLE
	PCA_InitStructure.PCA_Value    = 65535;						//对于软件定时, 为匹配比较值
	PCA_Init(PCA0,&PCA_InitStructure);

	PCA_InitStructure.PCA_Mode     = PCA_Mode_HighPulseOutput;	//PCA_Mode_PWM, PCA_Mode_Capture, PCA_Mode_SoftTimer, PCA_Mode_HighPulseOutput
	PCA_InitStructure.PCA_PWM_Wide = 0;							//PCA_PWM_8bit, PCA_PWM_7bit, PCA_PWM_6bit
	PCA_InitStructure.PCA_Interrupt_Mode = ENABLE;				//PCA_Rise_Active, PCA_Fall_Active, ENABLE, DISABLE
	PCA_InitStructure.PCA_Value    = 65535;						//对于软件定时, 为匹配比较值
	PCA_Init(PCA1,&PCA_InitStructure);

	PCA_InitStructure.PCA_Clock    = PCA_Clock_12T;				//PCA_Clock_1T, PCA_Clock_2T, PCA_Clock_4T, PCA_Clock_6T, PCA_Clock_8T, PCA_Clock_12T, PCA_Clock_Timer0_OF, PCA_Clock_ECI
	PCA_InitStructure.PCA_IoUse    = PCA_P24_P25_P26_P27;		//PCA_P12_P11_P10_P37, PCA_P34_P35_P36_P37, PCA_P24_P25_P26_P27
	PCA_InitStructure.PCA_Interrupt_Mode = DISABLE;				//ENABLE, DISABLE
	PCA_InitStructure.PCA_Polity   = PolityHigh;				//优先级设置	PolityHigh,PolityLow
	PCA_Init(PCA_Counter, &PCA_InitStructure);
}

void main(void)
{
	GPIO_config();
	PCA_config();

	PWMn_Update(PCA0, (u16)(PWM0_DUTY * 0.75));
	PWMn_Update(PCA1, (u16)(PWM1_DUTY * 0.25));
	EA = 1;

	while(1)
	{

	}
}

实验现象


30MHz / 12 / 50000 = 50 Hz


在这里插入图片描述
在这里插入图片描述

捕获模式测量周期长度

修改PCA.c内的PCA_Handler
在这里插入图片描述

//========================================================================
// 函数: void	PCA_Handler (void) interrupt PCA_VECTOR
// 描述: PCA中断处理程序.
// 参数: None
// 返回: none.
// 版本: V1.0, 2012-11-22
//========================================================================
void	PCA_Handler (void) interrupt PCA_VECTOR
{
	if(CCF0)		//PCA模块0中断
	{
		CCF0 = 0;		//清PCA模块0中断标志
		if(P25)	CCAP0_tmp += PCA_Timer0;	//输出为高电平,则给影射寄存器装载高电平时间长度
		else	CCAP0_tmp += PWM0_low;	//输出为低电平,则给影射寄存器装载低电平时间长度
		CCAP0L = (u8)CCAP0_tmp;			//将影射寄存器写入捕获寄存器,先写CCAP0L
		CCAP0H = (u8)(CCAP0_tmp >> 8);	//后写CCAP0H
	}

	if(CCF1)	//PCA模块1中断
	{
		CCF1 = 0;		//清PCA模块1中断标志
		if(PCA1_mode >= PCA_Mode_SoftTimer)		//PCA_Mode_SoftTimer and PCA_Mode_HighPulseOutput
		{
			CCAP1_tmp += PCA_Timer1;
			CCAP1L = (u8)CCAP1_tmp;			//将影射寄存器写入捕获寄存器,先写CCAP0L
			CCAP1H = (u8)(CCAP1_tmp >> 8);	//后写CCAP0H
		}
		else if(PCA1_mode == PCA_Mode_Capture)
		{
			CCAP1_tmp = CCAP1H;	//读CCAP1H
			CCAP1_tmp = (CCAP1_tmp << 8) + CCAP1L;
			B_Capture1 = 1;
		}
	}

	if(CCF2)	//PCA模块2中断
	{
		CCF2 = 0;		//清PCA模块1中断标志
		if(P27)	CCAP2_tmp += PCA_Timer2;	//输出为高电平,则给影射寄存器装载高电平时间长度
		else	CCAP2_tmp += PWM2_low;	//输出为低电平,则给影射寄存器装载低电平时间长度
		CCAP2L = (u8)CCAP2_tmp;			//将影射寄存器写入捕获寄存器,先写CCAP0L
		CCAP2H = (u8)(CCAP2_tmp >> 8);	//后写CCAP0H
	}

	if(CF)	//PCA溢出中断
	{
		CF = 0;			//清PCA溢出中断标志
	}

}

main.c

  1. #include "./Drivers/config.h"
    #include "./Drivers/delay.h"
    
    #include "./Drivers/GPIO.h"
    #include "./Drivers/PCA.h"
    
    #include "./Drivers/soft_UART.h"
    #include <stdio.h>
    
    char putchar(char Char)
    {
    	TxSend(Char);
        return Char;
    }
    
    void GPIO_config(void)
    {
    	GPIO_InitTypeDef GPIO_InitStructure;					//结构定义
    	GPIO_InitStructure.Mode = GPIO_OUT_PP;					//指定IO的输入或输出方式,GPIO_PullUp,GPIO_HighZ,GPIO_OUT_OD,GPIO_OUT_PP
    	GPIO_InitStructure.Pin  = GPIO_Pin_5;					//指定要初始化的IO, 或操作
    	GPIO_Inilize(GPIO_P2, &GPIO_InitStructure);				//P2.5初始化为推挽输出
    }
    
    void PCA_config(void)
    {
    	PCA_InitTypeDef PCA_InitStructure;
    
    	PCA_InitStructure.PCA_Mode     = PCA_Mode_HighPulseOutput;	//PCA_Mode_PWM, PCA_Mode_Capture, PCA_Mode_SoftTimer, PCA_Mode_HighPulseOutput
    	PCA_InitStructure.PCA_PWM_Wide = 0;							//PCA_PWM_8bit, PCA_PWM_7bit, PCA_PWM_6bit
    	PCA_InitStructure.PCA_Interrupt_Mode = ENABLE;				//PCA_Rise_Active, PCA_Fall_Active, ENABLE, DISABLE
    	PCA_InitStructure.PCA_Value    = 65535;						//对于软件定时, 为匹配比较值
    	PCA_Init(PCA0,&PCA_InitStructure);
    
    	PCA_InitStructure.PCA_Mode     = PCA_Mode_Capture;	//PCA_Mode_PWM, PCA_Mode_Capture, PCA_Mode_SoftTimer, PCA_Mode_HighPulseOutput
    	PCA_InitStructure.PCA_PWM_Wide = 0;					//PCA_PWM_8bit, PCA_PWM_7bit, PCA_PWM_6bit
    	PCA_InitStructure.PCA_Interrupt_Mode = PCA_Fall_Active | ENABLE;	//(PCA_Rise_Active, PCA_Fall_Active) or (ENABLE, DISABLE)
    	PCA_InitStructure.PCA_Value    = 0;					//对于捕捉, 这个值没意义
    	PCA_Init(PCA1,&PCA_InitStructure);
    
    	PCA_InitStructure.PCA_Clock    = PCA_Clock_12T;				//PCA_Clock_1T, PCA_Clock_2T, PCA_Clock_4T, PCA_Clock_6T, PCA_Clock_8T, PCA_Clock_12T, PCA_Clock_Timer0_OF, PCA_Clock_ECI
    	PCA_InitStructure.PCA_IoUse    = PCA_P24_P25_P26_P27;		//PCA_P12_P11_P10_P37, PCA_P34_P35_P36_P37, PCA_P24_P25_P26_P27
    	PCA_InitStructure.PCA_Interrupt_Mode = DISABLE;				//ENABLE, DISABLE
    	PCA_InitStructure.PCA_Polity   = PolityHigh;				//优先级设置	PolityHigh,PolityLow
    	PCA_Init(PCA_Counter, &PCA_InitStructure);
    }
    
    u16	Cap_time;	//上一次捕捉时间
    
    void main(void)
    {
    	u16 j;
    	Cap_time = 0;
    
    	GPIO_config();
    	PCA_config();
    
    	PWMn_Update(PCA0, (u16)(PWM0_DUTY * 0.75));
    
    	EA = 1;
    
    	while(1)
    	{
    		if(B_Capture1)
    		{
    			B_Capture1 = 0;
    			j = CCAP1_tmp - Cap_time;	//计算时间差
    			Cap_time = CCAP1_tmp;
    			printf("%d us\r\n", (int)(j * 12. / 30));
    		}
    		delay_ms(1);
    	}
    }
    
    

实验现象

在这里插入图片描述

在这里插入图片描述