STC12C5A60S2单片机总结及应用指南
STC12C5A60S2单片机的总结
文章目录
引言:
STC12C5A60S2系列主要性能:
无需操作EEPROM
P4.2/CCP0), P1.4/CCP1 (或P4.3/CCP1), EX_LVD/P4.6
1280字节片内RAM数据存储器
可设置成四种模式:准双向口/弱上拉,强推挽/强上拉,仅为输入/高阻,开漏
每个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
}
}
作者:小石头-