STC12C5A60S2单片机总结及应用指南

STC12C5A60S2单片机的总结

文章目录

  • STC12C5A60S2单片机的总结
  • 引言:
  • 1.独立串口
  • 1.1 相关寄存器
  • 1.2 实验程序
  • 2.内置看门狗
  • 2.1 看门狗寄存器
  • 2.2 实验程序:
  • 3. EEPROM
  • 3.1 相关的寄存器
  • 3.2 实验程序
  • 4.内置ADC
  • 4.1 相关的寄存器
  • 4.2 实验程序
  • 5. PCA定时器做软件定时的功能
  • 5.1 相关的寄存器
  • 5.2 实验程序
  • 6. PCA定时器做PWM输出的功能(一路输出)
  • 6.1 相关寄存器
  • 6.2 实验程序
  • 7. PCA定时器做PWM输出的功能(两路输出)
  • 8. PCA定时器用于扩展外部中断
  • 9. PCA定时器用于输入捕获
  • 10. PCA定时器用于生成高速脉冲
  • 引言:

    STC12C5A60S2系列主要性能:

  • 高速: 1个时钟/机器周期,增型8051内核,速度比普通8051快6~12倍
  • 宽电压: 5.5~4.0V, 2.1~3.6V( STC12LE5A60S2系列)
  • 增加第二复位功能脚/P4.6(高可靠复位, 可调整复位门槛电压,频率<12MHz时,无需此功能)
  • 增加外部掉电检测电路/P4.6,可在掉电时,及时将数据保存进EEPROM,正常工作时
    无需操作EEPROM
  • 低功耗设计:空闲模式(可由任意一个中断唤醒)
  • 低功耗设计:掉电模式(可由外部中断唤醒),可支持下降沿/上升沿和远程唤醒
  • 支持掉电唤醒的管脚: P3.2/INT0, P3.3/INT1, T0/P3.4, T1/P3.5, RxD/P3.0, P1.3/CCP0(或
    P4.2/CCP0), P1.4/CCP1 (或P4.3/CCP1), EX_LVD/P4.6
  • 工作频率: 0~35MHz,相当于普通8051: 0~420MHz
  • 时钟:外部晶体或内部R/C振荡器可选,在ISP下载编程用户程序时设置
  • 8/16/32/40/48/56/60/62K字节片内Flash程序存储器,擦写次数10万次以上
    1280字节片内RAM数据存储器
  • 大容量片内EEPROM功能,擦写次数10万次以上
  • ISP/IAP,在系统可编程/在应用可编程,无需编程器/仿真器
  • 8通道,10位高速ADC,速度可达25万次/秒, 2路PWM还可当2路D/A使用
  • 2通道捕获/比较单元(CCP/PCA/PWM),—也可用来再实现2个定时器或2个外部中断(支持上升沿/下降沿中断)
  • 2个16位定时器(兼容普通8051定时器T0/T1), 2路PCA可再实现2个定时器。可编程时钟输出功能(T0在P3.4输出时钟, T1在P3.5输出时钟,BRT在P1.0输出时钟
  • 硬件看门狗(WDT)
  • 独立波特率发生器
  • SPI高速同步串行通信接口
  • 双串口,全双工异步串行口(UART),兼容普通8051串口,分时复用可当3组使用
  • 先进的指令集结构,兼容普通8051指令集, 有硬件乘法/除法指令
  • 通用I/O口(36/40/44个),复位后为: 准双向口/弱上拉(普通8051传统I/O口)
    可设置成四种模式:准双向口/弱上拉,强推挽/强上拉,仅为输入/高阻,开漏
    每个I/O口驱动能力均可达到20mA,但建议整个芯片不要超过120mA
  • 1.独立串口

    STC12C5A60S2系列单片机具有2个采用UART(Universal Asychronous Receiver/Transmitter)工作方式的全双工串行通信接口(串口1和串口2) 。串行口1的两个缓冲器共用的地址码是99H;串行口2的两个缓冲器共用的地址码是9BH。串行口1的两个缓冲器统称串行通信特殊功能寄存器SBUF;串行口2的两个缓冲器统称串行通信特殊功能寄存器S2BUF。

    串行口1对应的硬件部分是TxD/P3.1和RxD/P3.0引脚,串行口2 对应的硬件部分是TxD2和RxD2。通过设置特殊功能寄存器AUXR1中的S2_P4/AUXR1.4位,串行口2(UART2)功能可以在P1口和P4口之间任意切换。当串行口2功能在P1口实现时, 对应的管脚是P1.2/RxD2和P1.3/TxD2。当串行口2功能在P4口实现时, 对应的管脚是P4.2/RxD2和P4.3/ TxD2。

    1.1 相关寄存器

    SCON寄存器:

    辅助寄存器AUXR:

    独立波特率发生器寄存器BRT(地址为9CH,复位值为00H)用于保存重装时间常数

    1.2 实验程序
    #include"stc12c5a60s2.h"
    typedef unsigned int uint;
    typedef unsigned char uchar;
    
    uchar a,flag;
    
    sbit led1=P2^0;
    
    void ck_init()
    {
    	SCON=0X50;
    	BRT=0Xfd;   //一定要11.0592M晶振,BRT的值即为用定时器做波特率发生器方式的定时器匹配值; 9600波特率
    	AUXR=0x11;
    	ES=1;
    	EA=1;
    }
    void UART_SendData(uchar dat)
    {
    	SBUF=dat;      
    	while(TI==0);  
    	TI=0;	
    }
    void main()
    {
    	ck_init();
    	while(1)
    	{
    		if(flag==1)
    		{
    			ES=0;		//关串口中断
    			flag=0;
    			SBUF=a;
    			UART_SendData(a+0x30);
    			ES=1;
    		}
    	}
    }
    
    void serial() interrupt 4
    {
    	flag=1;
    	if(SBUF==0x01)
    	{
    		led1=0;	
    	}
    	else led1=1;
    
    	a=SBUF;
    	RI=0;
    }
    
    2.内置看门狗

    “看门狗”其实是一个寄存器,每隔一定时间我们需要把寄存器的指定位清零(即所谓的“喂狗”),如果在设置的时间范围内没有“喂狗”,则单片机会自动软复位。“看门狗”的设计是为了防止单片机程序跑飞,确保系统的工作正常。

    2.1 看门狗寄存器


    PS2、PS1、PS0位设置看门狗的超时时间:

    2.2 实验程序:
    #include"stc12c5a60s2.h"
    typedef unsigned int uint;
    typedef unsigned char uchar;
    
    #define wdt_reset_counts 0x3d
    
    sbit p20=P2^0;
    
    void delayms(uint t)
    {
    	uint x,y;
    	for(x=t;x>0;x--)
    	for(y=920;y>0;y--);
    }
    void main()
    {
    	WDT_CONTR=wdt_reset_counts;
    	p20=0;
    	delayms(500);
    	p20=1;	  
    	while(1)
    	{
    		delayms(5000);   // 延时超过看门狗超时时间单片机会复位,p2.0口led不停闪烁
    		WDT_CONTR=wdt_reset_counts;
    	}
    }
    
    3. EEPROM

    STC12C5A60S2系列单片机内部集成了的EEPROM是与程序空间是分开的,利用ISP/IAP技术可将内部Data Flash当EEPROM,擦写次数在10万次以上。EEPROM可分为若干个扇区,每个扇区包含512字节。使用时,建议同一次修改的数据放在同一个扇区,不是同一次修改的数据放在不同的扇区,不一定要用满。数据存储器的擦除操作是按扇区进行的。

    3.1 相关的寄存器

    底层寄存器的操作可以自行看单片机的官方手册,对于使用12单片机的EEPROM,只需要会调用EEPROM模块的API即可。

    STC12C5A60S2单片机的EEPROM资源(每个扇区512字节):

    3.2 实验程序
    #include"stc12c5a60s2.h"
    typedef unsigned int uint;
    typedef unsigned char uchar;
    
    #include"lcd.h"
    
    #define	EEP_address	0x0000
    uchar code ASCII[] = {'0','1','2','3','4','5','6','7','8','9'};
    uchar a,ge,shi;
    uchar	TestCnt;
    
    uchar zf[]="num:";
    uchar shu;
    
    void	EEPROM_read_n(uint EE_address,uchar *DataAddress,uchar lenth);
    void 	EEPROM_SectorErase(uint EE_address);
    void 	EEPROM_write_n(uint EE_address,uchar *DataAddress,uchar lenth);
    
    void delayms(uint t)
    {
    	uint x,y;
    	for(x=t;x>0;x--)
    	for(y=920;y>0;y--);
    }
    
    void main()
    {
    	LcdInit();
    	delayms(5);
    	EEPROM_read_n(EEP_address,&TestCnt,1);	//读出保存值   读N个字节函数 最多255字节一次
    	TestCnt++;
    	if(TestCnt>100) TestCnt=0;  // 如果开机次数大于100,清零
    	
    	
    	EEPROM_SectorErase(EEP_address);  //扇区擦除函数,写入之前必须清楚扇区
    	EEPROM_write_n(EEP_address,&TestCnt,1);	//写1个字节函数 最多255字节一次。把开机次数写入EEPROM中
    	shi=TestCnt/10;ge=TestCnt%10;
    
    	// 显示开机次数
    	LcdWriteCom(0x80);
    	for(shu=0;shu<=3;shu++)
    	{
    		LcdWriteData(zf[shu]);	
    	}
    	while(1)
    	{
    		LcdWriteCom(0x05+0x80);LcdWriteData(ASCII[shi]);
    		LcdWriteCom(0x06+0x80);LcdWriteData(ASCII[ge]);	
    	}
    }
    

    完整程序工程的链接:https://pan.baidu.com/s/1feodD7Oa5lkkOm-nmuLnYA?pwd=1234 提取码:1234

    4.内置ADC

    STC12C5A60AD/S2系列带A/D转换的单片机的A/D转换口在P1口(P1.7-P1.0), 有8路10位高速A/D转换器,速度可达到250KHz(25万次/秒)。

    4.1 相关的寄存器

    内置AD转换的过程总结为4步:1.设置指定的I/O口为模拟输入模式;2.使能;3.设置ADC的通道及转换速度;4.转换结果的处理

    1.设置指定的I/O口为模拟输入模式

    2.使能

    3.设置ADC的通道及转换速度

    ADC_START(低3位):1:开始转换;0:转换结束

    ADC_FLAG:1:转换结束(需软件清零)

    ADC_POWER:ADC电源;1:打开ADC转换的电源,0:关闭ADC转换的电源

    4.转换结果的处理:

    常用的方法是设置ADRJ=0,AD转换的结果=ADC_RES的值<<2 | ADC_RESL的值

    4.2 实验程序

    4.2.1 AD一路非中断方式

    /*************	本程序功能说明	**************
    
    P1.0做ADC输入,从串口输出结果(ASCII),9600,8,N,1.
    
    用户只需要更改 MAIN_Fosc 来适应自己的系统。
    
    ******************************************/
    
    
    /*************	用户系统配置	**************/
    
    //#define MAIN_Fosc		22118400L	//定义主时钟, 模拟串口和和延时会自动适应。5~35MHZ
    #define MAIN_Fosc		11095200L	//定义主时钟, 模拟串口和和延时会自动适应。5~35MHZ
    
    /*************	以下宏定义用户请勿修改	**************/
    #include	"reg51.H"
    #define	uchar	unsigned char
    #define uint	unsigned int
    /******************************************/
    
    sfr P1ASF     = 0x9D;	//12C5A60AD/S2系列模拟输入(AD或LVD)选择
    sfr ADC_CONTR = 0xBC;	//带AD系列
    sfr ADC_RES   = 0xBD;	//带AD系列
    sfr ADC_RESL  = 0xBE;	//带AD系列
    
    //								            7       6      5       4         3      2    1    0   Reset Value
    //sfr ADC_CONTR = 0xBC;		ADC_POWER SPEED1 SPEED0 ADC_FLAG ADC_START CHS2 CHS1 CHS0 0000,0000	//AD 转换控制寄存器 
    #define ADC_OFF()	ADC_CONTR = 0
    #define ADC_ON		(1 << 7)
    #define ADC_90T		(3 << 5)
    #define ADC_180T	(2 << 5)
    #define ADC_360T	(1 << 5)
    #define ADC_540T	0
    #define ADC_FLAG	(1 << 4)	//软件清0
    #define ADC_START	(1 << 3)	//自动清0
    #define ADC_CH0		0
    #define ADC_CH1		1
    #define ADC_CH2		2
    #define ADC_CH3		3
    #define ADC_CH4		4
    #define ADC_CH5		5
    #define ADC_CH6		6
    #define ADC_CH7		7
    
    
    /************* 本地变量声明 **************/
    sbit	P_TXD1 = P3^1;
    
    
    /************* 本地函数声明 **************/
    void	Tx1Send(uchar dat);
    void	PrintString(unsigned char code *puts);
    void  	delay_ms(unsigned char ms);
    uint	adc10_start(uchar channel);	//channel = 0~7
    
    
    
    
    /********************* 主函数 *************************/
    void main(void)
    {
    	uint	j;
    
    	PrintString("****** STC12C5A60S2系列ADC程序 2011-02-27 ******\r\n");	//上电后串口发送一条提示信息
    
    	P1ASF = (1 << ADC_CH0);			//12C5A60AD/S2系列模拟输入(AD)选择
    	ADC_CONTR = ADC_360T | ADC_ON;
    
    	while(1)
    	{
    		delay_ms(250);
    		delay_ms(250);
    		delay_ms(250);
    		delay_ms(250);
    		
    		j = adc10_start(0);		// P1.0 ADC
    		Tx1Send('A');
    		Tx1Send('D');
    		Tx1Send('0');
    		Tx1Send('=');
    		Tx1Send(j/1000 + '0');
    		Tx1Send(j%1000/100 + '0');
    		Tx1Send(j%100/10 + '0');
    		Tx1Send(j%10 + '0');
    		Tx1Send(0x0d);
    		Tx1Send(0x0a);
    		
    	}
    }
    
    /********************* 做一次ADC转换 *******************/
    uint	adc10_start(uchar channel)	//channel = 0~7
    {
    	uint	adc;
    	uchar	i;
    
    	ADC_RES = 0;
    	ADC_RESL = 0;
    
    	ADC_CONTR = (ADC_CONTR & 0xe0) | ADC_START | channel; 
    
    //	for(i=0; i<250; i++)		//13T/loop, 40*13=520T=23.5us @ 22.1184M
    	i = 250;
    	do{
    		if(ADC_CONTR & ADC_FLAG)
    		{
    			ADC_CONTR &= ~ADC_FLAG;
    		//	adc = 0;
    		//	adc = (ADC_RES << 8) | ADC_RESL;	//ADRJ_enable()   
    			//ADC_RES:结果的高8位;ADC_RESL:结果的低2位
    			adc = (uint)ADC_RES;
    			adc = (adc << 2) | (ADC_RESL & 3);
    			return	adc;
    		}
    	}while(--i);
    	return	1024;
    }
    
    //========================================================================
    // 函数: void  delay_ms(unsigned char ms)
    // 描述: 延时函数。
    // 参数: ms,要延时的ms数.
    // 返回: none.
    // 版本: VER1.0
    // 日期: 2010-12-15
    // 备注: 
    //========================================================================
    void  delay_ms(unsigned char ms)
    {
         unsigned int i;
    	 do{
    	      i = MAIN_Fosc / 14000;
    		  while(--i)	;   //14T per loop
         }while(--ms);
    }
    
    
    /********************** 模拟串口相关函数************************/
    
    void	BitTime(void)	//位时间函数
    {
    	uint i;
    	i = ((MAIN_Fosc / 100) * 104) / 140000L - 1;		//根据主时钟来计算位时间
    	while(--i);
    }
    
    //模拟串口发送
    void	Tx1Send(uchar dat)		//9600,N,8,1		发送一个字节
    {
    	uchar	i;
    	EA = 0;
    	P_TXD1 = 0;
    	BitTime();
    	for(i=0; i<8; i++)
    	{
    		if(dat & 1)		P_TXD1 = 1;
    		else			P_TXD1 = 0;
    		dat >>= 1;
    		BitTime();
    	}
    	P_TXD1 = 1;
    	EA = 1;
    	BitTime();
    	BitTime();
    }
    
    void PrintString(unsigned char code *puts)		//发送一串字符串
    {
        for (; *puts != 0;	puts++)  Tx1Send(*puts); 	//遇到停止符0结束
    }
    

    4.2.2 AD一路中断方式

    /*************	本程序功能说明	**************
    
    P1.0做ADC输入,中断方式读ADC, 从串口输出结果(ASCII),9600,8,N,1.
    
    用户只需要更改 MAIN_Fosc 来适应自己的系统。
    
    ******************************************/
    
    
    /*************	用户系统配置	**************/
    
    //#define MAIN_Fosc		22118400L	//定义主时钟, 模拟串口和和延时会自动适应。5~35MHZ
    #define MAIN_Fosc	   11059200UL	//定义主时钟, 模拟串口和和延时会自动适应。5~35MHZ
    
    /*************	以下宏定义用户请勿修改	**************/
    #include	"reg51.H"
    #define	uchar	unsigned char
    #define uint	unsigned int
    /******************************************/
    
    sfr P1ASF     = 0x9D;	//12C5A60AD/S2系列模拟输入(AD或LVD)选择
    sfr ADC_CONTR = 0xBC;	//带AD系列
    sfr ADC_RES   = 0xBD;	//带AD系列
    sfr ADC_RESL  = 0xBE;	//带AD系列
    sbit EADC = IE^5;	//ADC 中断 允许位
    
    //								7       6      5       4         3      2    1    0   Reset Value
    //sfr ADC_CONTR = 0xBC;		ADC_POWER SPEED1 SPEED0 ADC_FLAG ADC_START CHS2 CHS1 CHS0 0000,0000	//AD 转换控制寄存器 
    #define ADC_OFF()	ADC_CONTR = 0
    #define ADC_ON		(1 << 7)
    #define ADC_90T		(3 << 5)
    #define ADC_180T	(2 << 5)
    #define ADC_360T	(1 << 5)
    #define ADC_540T	0
    #define ADC_FLAG	(1 << 4)	//软件清0
    #define ADC_START	(1 << 3)	//自动清0
    #define ADC_CH0		0
    #define ADC_CH1		1
    #define ADC_CH2		2
    #define ADC_CH3		3
    #define ADC_CH4		4
    #define ADC_CH5		5
    #define ADC_CH6		6
    #define ADC_CH7		7
    
    
    /************* 本地变量声明 **************/
    sbit	P_TXD1 = P3^1;
    
    unsigned int adc;
    bit		B_ADC_OK;
    
    
    
    /************* 本地函数声明 **************/
    void	Tx1Send(uchar dat);
    void	PrintString(unsigned char code *puts);
    void  	delay_ms(unsigned char ms);
    uint	adc10_start(uchar channel);	//channel = 0~7
    
    
    
    
    /********************* 主函数 *************************/
    void main(void)
    {
    	uint	j;
    
    	PrintString("****** STC12C5A60S2系列ADC程序 2011-02-27 ******\r\n");	//上电后串口发送一条提示信息
    
    	P1ASF = (1 << ADC_CH0);			//12C5A60AD/S2系列模拟输入(AD)选择
    	ADC_CONTR = ADC_360T | ADC_ON;
    	EADC = 1;	//enable ADC interrupt
    	EA   = 1;	//enable all interrupt
    	
    	while(1)
    	{
    		delay_ms(250);
    		delay_ms(250);
    		delay_ms(250);
    		delay_ms(250);
    		
    		ADC_RES  = 0;
    		ADC_RESL = 0;
    		B_ADC_OK = 0;
    		ADC_CONTR = (ADC_CONTR & 0xe0) | ADC_START | ADC_CH0; 
    		while(!B_ADC_OK);	//等待ADC完成
    
    		j = adc;
    		Tx1Send('A');
    		Tx1Send('D');
    		Tx1Send('0');
    		Tx1Send('=');
    		Tx1Send(j/1000 + '0');
    		Tx1Send(j%1000/100 + '0');
    		Tx1Send(j%100/10 + '0');
    		Tx1Send(j%10 + '0');
    		Tx1Send(0x0d);
    		Tx1Send(0x0a);
    		
    	}
    }
    
    /********************* ADC转换中断 *******************/
    void	adc_interrupt(void)	interrupt 5		//channel = 0~7
    {
    	ADC_CONTR &= ~ADC_FLAG;		//clear ADC flag
    	adc = (uint)ADC_RES;
    	adc = (adc << 2) | (ADC_RESL & 3);
    	B_ADC_OK = 1;		//标志ADC已结束
    }
    
    //========================================================================
    // 函数: void  delay_ms(unsigned char ms)
    // 描述: 延时函数。
    // 参数: ms,要延时的ms数.
    // 返回: none.
    // 版本: VER1.0
    // 日期: 2010-12-15
    // 备注: 
    //========================================================================
    void  delay_ms(unsigned char ms)
    {
         unsigned int i;
    	 do{
    	      i = MAIN_Fosc / 14000;
    		  while(--i)	;   //14T per loop
         }while(--ms);
    }
    
    
    /********************** 模拟串口相关函数************************/
    
    void	BitTime(void)	//位时间函数
    {
    	uint i;
    	i = ((MAIN_Fosc / 100) * 104) / 140000L - 1;		//根据主时钟来计算位时间
    	while(--i);
    }
    
    //模拟串口发送
    void	Tx1Send(uchar dat)		//9600,N,8,1		发送一个字节
    {
    	uchar	i;
    
    	P_TXD1 = 0;
    	BitTime();
    	for(i=0; i<8; i++)
    	{
    		if(dat & 1)		P_TXD1 = 1;
    		else			P_TXD1 = 0;
    		dat >>= 1;
    		BitTime();
    	}
    	P_TXD1 = 1;
    	BitTime();
    	BitTime();
    }
    
    void PrintString(unsigned char code *puts)		//发送一串字符串
    {
        for (; *puts != 0;	puts++)  Tx1Send(*puts); 	//遇到停止符0结束
    }
    
    
    5. PCA定时器做软件定时的功能

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

    5.1 相关的寄存器

    PCA工作模式寄存器CMOD:

    ECF: PCA计数溢出中断使能位。当ECF = 0时,禁止寄存器CCON中CF位的中断;当ECF = 1时,允许寄存器CCON中CF位的中断。

    控制寄存器CCON :

    比较/捕获寄存器CCAPM0和CCAPM1 :


    PCA的16位计数器 :低8位CL和高8位CH 。CL和CH地址分别为E9H和F9H,复位值均为00H,用于保存PCA的装载值。

    PCA捕捉/比较寄存器 — CCAPnL(低位字节)和CCAPnH(高位字节) :

    16位软件定时器模式 :

    5.2 实验程序
    #include"stc12c5a60s2.h"	   
    
    typedef unsigned int uint;
    typedef unsigned char uchar;
    
    #define fosc 11059200L
    #define t100hz (fosc/12/100)		  
    
    sbit p20=P2^0;
    
    uint value;
    uchar cnt;
    
    void pcs_ds() interrupt 7
    {
    	CCF0=0;		//中断标志位清0
    	CCAP0L=value;	  //比较寄存器程序赋值(比上次加1倍)
    	CCAP0H=value>>8;
    	value=value+t100hz;	 //	value一直增加不会溢出??会CL、CH也会溢出。溢出后的数值会丢弃二进制中超过最高位的
    	if(cnt--==0)	   //1s  t100hz=0.01s  0.01*100=1s
    	{
    		cnt=100;
    		p20=~p20;
    	}
    }
    
    void main()
    {
    	CCON=0;		   //cf cr ccf1 ccf0为0
    	CL=0;		//pca计数值初始为0
    	CH=0;
    	CMOD=0X00;	 //CIDL=0、SYSclk/12、ECF = 0
    	value=t100hz;  //0.01s
    	CCAP0L=value;	//给比较寄存器赋值
    	CCAP0H=value>>8;
    	value=value+t100hz;	 //value加一倍
    	CCAPM0=0x49;	  //ecmo0 mat0 eccf0为1;ECOM0= 1时,允许比较器功能;匹配将置位CCON寄存;使能CCF0中
    	CR=1;  //起动 PCA计数器阵列计数,CCON寄存器的一位
    	EA=1;
    	cnt=0;
    	while(1)
    	{
    		
    	}
    }
    
    6. PCA定时器做PWM输出的功能(一路输出)

    STC12C5A60S2系列单片机有2路可编程计数器阵列PCA/PWM(通过AUXR1寄存器可以设置PCA/ PWM从P1口切换到P4口)

    6.1 相关寄存器

    PCA捕捉/比较寄存器 — CCAPnL(低位字节)和CCAPnH(高位字节) :

    PCA模块PWM寄存器PCA_PWM0和PCA_PWM1 :

    辅助寄存器AUXR1:

    PCA定时器的PWM模式的工作原理:

    当EPCnL = 0及ECCAPnL = 00H时,PWM固定输出高当EPCnL = 1及CCAPnL = 0FFH时,PWM固定输出低 。

    6.2 实验程序
    #include"stc12c5a60s2.h"
    
    typedef unsigned int uint;
    typedef unsigned char uchar;
    
    #define fosc 11059200L
    
    void main()
    {
    	CCON=0;	   //CF(pca计数溢出标志) CR(是否启动pca计数) CCF1、CCF0(pca模块中断标志)  全清0
    	CL=0;  //pca计数值 只有CL计数 8位
    	CH=0;  //CH不计数
    	CMOD=0X02;	  //选择pca/pwm时钟频率(决定了输出pwm波的频率)  是否中断
    	CCAP0H=CCAP0L=0X80;	 //pwm输出通道0(CCP0)   50%占空比  CL与之比较
    	CCAPM0=0X42;	 //ecom0 pwm0 置1(使能pwm通道0)
    
    	CCAP1H=CCAP1L=0XFF;	 //pwm输出通道0(CCP1)   0占空比	    CL与之比较
    	PCA_PWM1=0X03;		 //固定输出0
    	CCAPM1=0X42;	 //ecom1 pwm1 置1(使能pwm通道1)
    
    	CR=1;	//启动pca定时器
    	while(1)
    	{
    		
    	}
    }
    
    7. PCA定时器做PWM输出的功能(两路输出)

    在第6小节的基础上再多定义CCAP1H、CCAPM1寄存器即可实现2路PWM输出

    实验程序:

    /*************	本程序功能说明	**************
    
    两路PWM测试.
    
    *********************************************/
    
    
    #include	<reg51.h>
    #include<intrins.h>
    #define	uchar	unsigned char
    #define uint	unsigned int
    
    #define		PCA_IDLE_DISABLE	0		//1: MCU在IDLE模式时禁止PCA工作。	0:  MCU在IDLE模式时允许PCA工作。
    #define		PCA_SOURCE_SELECT	4		//选择PCA的基准时钟源。
    										//0:系统时钟Fosc/12。
    										//1:系统时钟Fosc/2。
    										//2:定时器0的溢出。
    										//3:ECI/P3.4脚的外部时钟输入(最大=Fosc/2)。
    										//4:系统时钟Fosc。
    										//5:系统时钟Fosc/4。
    										//6:系统时钟Fosc/6。
    										//7:系统时钟Fosc/8。
    
    sfr AUXR1 = 0xA2;
    sfr	AUXR = 0x8E;
    sfr CCON = 0xD8;
    sfr CMOD = 0xD9;
    sfr CCAPM0 = 0xDA;	//PCA module 0 work mode
    sfr CCAPM1 = 0xDB;	//PCA module 1 work mode
    sfr CL     = 0xE9;	//PCA counter
    sfr CCAP0L = 0xEA;	//PCA模块0的捕捉/比较寄存器低8位。
    sfr CCAP1L = 0xEB;	//PCA模块1的捕捉/比较寄存器低8位。
    sfr PCA_PWM0 = 0xF2;	//PCA模块0 PWM寄存器。
    sfr PCA_PWM1 = 0xF3;	//PCA模块1 PWM寄存器。
    sfr CH     = 0xF9;
    sfr CCAP0H = 0xFA;		//PCA模块0的捕捉/比较寄存器高8位。
    sfr CCAP1H = 0xFB;		//PCA模块1的捕捉/比较寄存器高8位。
    
    sbit CCF0  = CCON^0;	//PCA 模块0中断标志,由硬件置位,必须由软件清0。
    sbit CCF1  = CCON^1;	//PCA 模块1中断标志,由硬件置位,必须由软件清0。
    sbit CR    = CCON^6;	//1: 允许PCA计数器计数,必须由软件清0。
    sbit CF    = CCON^7;	//PCA计数器溢出(CH,CL由FFFFH变为0000H)标志。PCA计数器溢出后由硬件置位,必须由软件清0。
    
    #define		PWM0_NORMAL()	PCA_PWM0 = 0	//PWM0正常输出(默认)
    #define		PWM0_OUT_0()	PCA_PWM0 = 3	//PWM0一直输出0
    #define		PWM1_NORMAL()	PCA_PWM1 = 0	//PWM1正常输出(默认)
    #define		PWM1_OUT_0()	PCA_PWM1 = 3	//PWM1一直输出0
    
    
    /*************	本地函数声明	**************/
    
    uchar	pwm0,pwm1;
    
    void Delay10ms()		//@11.0592MHz
    {
    	unsigned char i, j;
    
    	_nop_();
    	_nop_();
    	i = 108;
    	j = 144;
    	do
    	{
    		while (--j);
    	} while (--i);
    }
    
    
    void pwm_kz()
    {
    	if(pwm0>255) pwm0=0;
    	if(pwm1>255) pwm1=0;
    }
    
    void	main(void)
    {
    	uint	i;
    
    	//AUXR1 |= (1<<6);	//PCA 从P1口切换到P4.2 P4.3
    
    	pwm0 = 128;			// PWM0 初始值 128
    	pwm1 = 64;			// PWM1 初始值 64
    
    	CCAP0H = pwm0;		//改变占空比
    	CCAPM0 = 0x42;		//ecom0 pwm0 置1(使能pwm通道0)
    
    	CCAP1H = pwm1;		//改变占空比
    	CCAPM1 = 0x42;		//ecom1 pwm1 置1(使能pwm通道1)
    
    	CMOD = (PCA_IDLE_DISABLE << 7) | (PCA_SOURCE_SELECT << 1);	//初始化PCA模式寄存器。
    	CL = 0x00;		//pca计数值 只有CL计数 8位
    	CH = 0x00;		//CH不计数
    	CR = 1;			//启动pca定时器
    
    	while (1)
    	{
    		for(i=0;i<=255; i++);	//延时一下	255
    		{
    		 	pwm0++;
    			pwm1++;
    			pwm_kz();
    			CCAP0H = pwm0;		//set PWM wide
    			CCAP1H = pwm1;		//set PWM wide
    			Delay10ms();
    		}		
    	}
    }
    
    
    8. PCA定时器用于扩展外部中断

    主要的寄存器:PCA比较/捕获寄存器CCAPM0和CCAPM1 :

    设置好CAPN0、ECCF0位

    实验程序:

    #include"stc12c5a60s2.h"
    typedef unsigned int uint;
    typedef unsigned char uchar;
    
    sbit p10=P1^0;
    
    void pca_bh() interrupt 7
    {
    	if(CCF0==1)
    	{
    		CCF0=0;
    		p10=~p10;
    	}
    }
    
    void main()
    {
    	CCON=0;	  			//CF(pca计数溢出标志) CR(是否启动pca计数) CCF1、CCF0(pca模块中断标志)  全清0
    	CL=CH=0;  			//pca计数值
    	CMOD=0X00; 			//选择pca/pwm时钟频率(决定了输出pwm波的频率)  是否中断
    	CCAPM0=0X11;	 //下降沿捕获
    	//CCAPM0=0X21;	 //上升沿捕获
    	//CCAPM0=0X21;	 //下降、上升都捕获
    	CR=1;	  //启动pca定时器
    	EA=1;	  //开启总中断
    	while(1)
    	{
    		
    	}
    }
    
    9. PCA定时器用于输入捕获

    工作原理:

    实验程序:

    /*************	本程序功能说明	**************
    
    STC12C5A60S2系列 从P1.3 P1.4 做捕捉输入,从串口输出结果(ASCII),9600,8,N,1.
    STC12C5204AD系列 从P3.7 P3.5 做捕捉输入,从串口输出结果(ASCII),9600,8,N,1.
    
    ******************************************/
    
    
    /*************	用户系统配置	**************/
    
    //#define MAIN_Fosc		22118400L	//定义主时钟, 模拟串口和和延时会自动适应。5~35MHZ
    #define MAIN_Fosc		11059200L	//定义主时钟, 模拟串口和和延时会自动适应。5~35MHZ
    
    #define		PCA_IDLE_DISABLE	0			//1: MCU在IDLE模式时禁止PCA工作。	0:  MCU在IDLE模式时允许PCA工作。
    #define		PCA_SOURCE_SELECT	0			//选择PCA的基准时钟源。
    											//0:系统时钟Fosc/12。
    											//1:系统时钟Fosc/2。
    											//2:定时器0的溢出。
    											//3:ECI/P3.4脚的外部时钟输入(最大=Fosc/2)。
    											//4:系统时钟Fosc。
    											//5:系统时钟Fosc/4。
    											//6:系统时钟Fosc/6。
    											//7:系统时钟Fosc/8。
    #define		PCA_ECF				1		//1: 允许PCA计数器溢出中断,0: 禁止
    
    
    /*************	以下宏定义用户请勿修改	**************/
    #include	"reg51.H"
    #define	uchar	unsigned char
    #define uint	unsigned int
    /******************************************/
    
    sfr CCON = 0xD8;	//STC12C5A60S2系列
    sfr CMOD = 0xD9;	//STC12C5A60S2系列
    sfr CCAPM0 = 0xDA;	//PCA模块0的工作模式寄存器。
    sfr CCAPM1 = 0xDB;	//PCA模块1的工作模式寄存器。
    
    sfr CL     = 0xE9;	//
    sfr CCAP0L = 0xEA;	//PCA模块0的捕捉/比较寄存器低8位。
    sfr CCAP1L = 0xEB;	//PCA模块1的捕捉/比较寄存器低8位。
    
    sfr PCA_PWM0 = 0xF2;	//PCA模块0 PWM寄存器。
    sfr PCA_PWM1 = 0xF3;	//PCA模块1 PWM寄存器。
    sfr CH     = 0xF9;
    sfr CCAP0H = 0xFA;		//PCA模块0的捕捉/比较寄存器高8位。
    sfr CCAP1H = 0xFB;		//PCA模块1的捕捉/比较寄存器高8位。
    
    sbit CCF0  = CCON^0;	//PCA 模块0中断标志,由硬件置位,必须由软件清0。
    sbit CCF1  = CCON^1;	//PCA 模块1中断标志,由硬件置位,必须由软件清0。
    sbit CR    = CCON^6;	//1: 允许PCA计数器计数,必须由软件清0。
    sbit CF    = CCON^7;	//PCA计数器溢出(CH,CL由FFFFH变为0000H)标志。PCA计数器溢出后由硬件置位,必须由软件清0。
    
    /************* 本地变量声明 **************/
    uint	CCAP0_tmp,CCAP1_tmp;
    uint	CCAP0_Last,CCAP1_Last;
    bit		B_Cap0,B_Cap1;
    sbit	P_TXD1 = P3^1;
    
    sbit	P10 = P1^0;
    
    /************* 本地函数声明 **************/
    void	Tx1Send(uchar dat);
    void	PrintString(unsigned char code *puts);
    
    
    void tx(uchar id,uint dat)
    {
    	Tx1Send('P');
    	Tx1Send('C');
    	Tx1Send('A');
    	Tx1Send(id + '0');
    	Tx1Send('=');
    	Tx1Send(dat/10000 + '0');
    	Tx1Send(dat%10000/1000 + '0');
    	Tx1Send(dat%1000/100 + '0');
    	Tx1Send(dat%100/10 + '0');
    	Tx1Send(dat%10 + '0');
    	Tx1Send(0x0d);
    	Tx1Send(0x0a);
    }
    
    
    
    /********************* 主函数 *************************/
    void main(void)
    {
    	CCAPM0 = 0x11;	//CCAP0下降沿捕捉,允许中断		下降沿捕捉: 0x11,  上升沿捕捉: 0x21, 上升下降沿捕捉: 0x31
    	CCAPM1 = 0x11;	//CCAP1下降沿捕捉,允许中断		下降沿捕捉: 0x11,  上升沿捕捉: 0x21, 上升下降沿捕捉: 0x31
    	CMOD = (PCA_IDLE_DISABLE << 7) | (PCA_SOURCE_SELECT << 1) | PCA_ECF;	//初始化PCA模式寄存器。
    	CL = 0;					//清空PCA基本计数器。
    	CH = 0;
    	CR = 1;					//Start CR
    	EA = 1;
    
    	PrintString("****** STC12C5A60S2系列MCU捕捉程序 2011-02-25 ******\r\n");	//上电后串口发送一条提示信息
    
    	while(1)	 
    	{
    		if(B_Cap0)
    		{
    			B_Cap0 = 0;
    			tx(0, CCAP0_tmp - CCAP0_Last);
    			CCAP0_Last = CCAP0_tmp;	  //为什么要减去上次值,CL和CH没有清零
    		}
    		if(B_Cap1)
    		{
    			B_Cap1 = 0;
    			tx(1, CCAP1_tmp - CCAP1_Last);
    			CCAP1_Last = CCAP1_tmp;
    		}
    	}
    }
    
    
    
    //========================================================================
    // 函数: void PCA_interrupt (void) interrupt 7
    // 描述: PCA中断服务程序。
    // 参数: 无。
    // 返回: 无。
    // 版本: VER1.0
    // 日期: 2009-12-30
    // 备注: 
    //========================================================================
    void PCA_interrupt (void) interrupt 7
    {
    	if(CCF0 == 1)		//PCA模块0中断
    	{
    		CCF0 = 0;		//清PCA模块0中断标志
    		CCAP0_tmp = CCAP0H;	//读CCAP0H (CH CL值会自动装载到CCAP0H  CCAP0L)
    		CCAP0_tmp = (CCAP0_tmp << 8) + CCAP0L;
    		B_Cap0 = 1;
    	}
    
    	if(CCF1 == 1)	//PCA模块1中断
    	{
    		CCF1 = 0;		//清PCA模块1中断标志
    		CCAP1_tmp = CCAP1H;	//读CCAP0H
    		CCAP1_tmp = (CCAP1_tmp << 8) + CCAP1L;
    		B_Cap1 = 1;
    	}
    
    	if(CF == 1)	//PCA溢出中断
    	{
    		CF = 0;			//清PCA溢出中断标志
    		P10 = ~P10;		//14HZ at 22.1184MHZ
    	}
    }
    
    /********************** 模拟串口相关函数************************/
    
    void	BitTime(void)	//位时间函数
    {
    	uint i;
    	i = ((MAIN_Fosc / 100) * 104) / 140000L - 1;		//根据主时钟来计算位时间
    	while(--i);
    }
    
    //模拟串口发送
    void	Tx1Send(uchar dat)		//9600,N,8,1		发送一个字节
    {
    	uchar	i;
    	P_TXD1 = 0;
    	BitTime();
    	for(i=0; i<8; i++)
    	{
    		if(dat & 1)		P_TXD1 = 1;
    		else			P_TXD1 = 0;
    		dat >>= 1;
    		BitTime();
    	}
    	P_TXD1 = 1;
    	BitTime();
    	BitTime();
    }
    
    void PrintString(unsigned char code *puts)		//发送一串字符串
    {
        for (; *puts != 0;	puts++)  Tx1Send(*puts); 	//遇到停止符0结束
    }
    
    
    10. PCA定时器用于生成高速脉冲

    工作原理:

    实验程序:

    /*************	本程序功能说明	**************
    
    PCA中断测试程序.
    PCA计数器溢出中断,从P1.0输出一个方波(168.75HZ at 22.1184MHZ).
    CCAP0模块中断,重装比较值,输出方波(1KHZ  at 22.1184MHZ).
    CCAP1模块中断,重装比较值,输出方波(2KHZ  at 22.1184MHZ).
    
    
    ******************************************/
    
    #include	"PCA.h"
    
    
    sbit	P10 = P1^0;
    sbit	P11 = P1^1;
    
    unsigned int CCAP0_tmp,CCAP1_tmp;
    
    void	PWMn_init(void);
    
    
    /******************** 主函数 **************************/
    //========================================================================
    // 函数: void main(void)
    // 描述: 空函数。
    // 参数: 无。
    // 返回: 无。
    // 版本: VER1.0
    // 日期: 2009-12-30
    // 备注: 
    //========================================================================
    void main(void)
    {
    	PWMn_init();	//初始化PCA
    	
    	while(1)
    	{
    	
    	}
    
    }
    
    
    //========================================================================
    // 函数: void	PWMn_init(void)
    // 描述: 初始化程序。
    // 参数: 无。
    // 返回: 无。
    // 版本: VER1.0
    // 日期: 2009-12-30
    // 备注: 
    //========================================================================
    void	PWMn_init(void)
    {
    	#ifdef	STC12C5201AD
    		P3M1 &= ~0xA0,	P3M0 |=  0xA0;		//CCAP0、CCAP1使用PUSH-PULL输出模式,STC12C5201AD系列。
    	#else
    		P1M1 &= ~0x18,	P1M0 |=  0x18;		//CCAP0、CCAP1使用PUSH-PULL输出模式,STC12C5A60S2系列。
    	#endif
    	CCON = 0;					//清除CF、CR、CCF0、CCF1
    	IPH |= 0x80;				//PCA中断使用最高优先级
    	PPCA = 1;
    	CMOD = (PCA_IDLE_DISABLE << 7) | (PCA_SOURCE_SELECT << 1) | PCA_ECF;	//初始化PCA模式寄存器。(决定高速脉冲频率)
    	CCAPM0 = 0x4D;				//高速输出模式,允许比较匹配中断(ECCF0=1)。
    	CCAPM1 = 0x4D;				//高速输出模式,允许比较匹配中断(ECCF1=1)。
    	CL = 0;						//清空PCA基本计数器。
    	CH = 0;
    	CCAP0_tmp = 0;				//清空CCAP0重装载影射寄存器。
    	CCAP1_tmp = 0;				//清空CCAP0重装载影射寄存器。
    	EA = 1;						//允许总中断
    	CR = 1;						//启动PCA。
    }
    
    //========================================================================
    // 函数: void PCA_interrupt (void) interrupt 7
    // 描述: PCA中断服务程序。
    // 参数: 无。
    // 返回: 无。
    // 版本: VER1.0
    // 日期: 2009-12-30
    // 备注: 
    //========================================================================
    void PCA_interrupt (void) interrupt 7
    {
    	if(CCF0 == 1)		//PCA模块0中断
    	{
    		CCF0 = 0;		//清PCA模块0中断标志
    		CCAP0_tmp +=2765 ;		//1KHZ  at   11.0592M  2765		
    		CCAP0L = CCAP0_tmp%256;			//将影射寄存器写入捕获寄存器,先写CCAP0L   (自己改的是对的!!!!!)
    		CCAP0H = CCAP0_tmp/256;	//后写CCAP0H
    	}
    
    	if(CCF1 == 1)	//PCA模块1中断
    	{
    		CCF1 = 0;		//清PCA模块1中断标志
    		CCAP1_tmp +=28;		//2KHZ at 22.1184MHZ 5530			  11.0592M   28	    (这里例程是错的!!,自己计算的28)
    		CCAP1L = (unsigned char)CCAP1_tmp;			//将影射寄存器写入捕获寄存器,先写CCAP0L
    		CCAP1H = (unsigned char)(CCAP1_tmp >> 8);	//后写CCAP0H
    	}
    	if(CF == 1)	//PCA溢出中断
    	{
    		CF = 0;			//清PCA溢出中断标志
    		P10 = ~P10;		//168.75HZ at 22.1184MHZ
    	}
    }
    

    作者:小石头-

    物联沃分享整理
    物联沃-IOTWORD物联网 » STC12C5A60S2单片机总结及应用指南

    发表回复