芯片介绍

XC8P9521这款芯片内存1KROM、48字节SRAM。并且是OTP的ROM意味只能烧写一次程序。

外设只有一些最基本的GPIO、PWM、TCC、WDT等。

系统框架介绍MCU运行逻辑

1、存储器结构

2.1 ROM程序存储器(0x0000 ~ 0x03FF

存放用户程序(指令),烧录时写入,运行时只读

芯片一上电或复位后,程序计数器 PC 被硬件清零因此上电之后永远从 0x0000 开始执行

触发中断时,PC自动跳转到固定中断向量地址0x08,执行中断服务程序(ISR)

2.2 SRAM数据存储器(0x00~0x3F

主要由特殊功能寄存器用户变量组成,程序运行时随时读写

特殊功能寄存器:0x00~0x0F 地址段特殊功能寄存器 如表2.1

通用 RAM(用户变量区48字节):0x10–0x3F地址段,你可以随意放全局变量、静态变量

表2.1

地址 R-Page 名称 IOC-Page 名称 功能
0x00 R0 / IAR 间接寻址指针
0x01 R1 / TCC CONT 定时器/看门狗控制
0x02 R2 / PC 程序计数器(低8位)
0x03 R3 / STATUS 状态标志寄存器
0x04 R4 / RSR RAM 选择寄存器(间接寻址)
0x05 R5 / PORT5 IOC5 / P5CR P5 数据 & 方向控制
0x06 R6 / PORT6 IOC6 / P6CR P6 数据 & 方向控制
0x07 R7 / LVDCON 低电压检测控制
0x08 R8 / PWMCON PWM 总控制
0x09 R9 / PRD IOC9 / PHDCR PWM 周期 & 上下拉总控
0x0A RA / PDC1 PWM1 占空比
0x0B RB / PDC2 IOCB / PDCR PWM2 占空比 & 下拉控制
0x0C RC / PDC3 PWM3 占空比
0x0D RD / ICIECR IOCD / PHCR P6 状态变化中断 & 上拉控制
0x0E RE / CPUCON IOCE / WDTCR CPU 模式 & 看门狗使能
0x0F RF / ISR IOCF / IMR 中断标志 & 中断使能

2、OPTION配置表

主要用来配置一些系统时钟等,只需要打开软件IDE,点击 我们可以看到里面有很多配置

主要用来配置系统时钟其他的了解即可

看门狗:禁止/使能

Clocks分频:2/4/8/16/32 Clocks   执行一条指令所需要的周期

振荡模式:IRC/HXT/LXT模式    选择系统的时钟来源。有内部RC、外部低速、外部高速三种

IRC频率:910K/1M/8M      只有振荡模式选择IRC此选项才能选择

振荡器倍频选项:禁止/使能    没有倍频选项禁止即可

低压复位:选择复位电压

代码加密:没啥用

复位端口上拉:资源有限一般不用复位口

P63端口:GPIO/RST/GPI    输出口/复位口/通用输入口三种选项一般使用GPIO

复位时间:选择复位时间

查表范围:1/4K只能查 0x0000–0x00FF  1K可以查 0x0000–0x03FF 全部 1024 个地址

端口特性:用户手册给的是SMT、HSMT、EMT、INV这四种特性

SMT 标准施密特 经典迟滞,抗干扰一般 普通按键、LED
HSMT 高灵敏度施密特 迟滞窗口更宽,噪声容限高 长线、电机环境
EMT 增强型门限 门限电压固定、更陡峭 模拟信号边沿检测
INV 反相器 输入直接反相,无迟滞

P6端口唤醒:选择非独立控制 只要 P6 口的 8 个引脚(P60–P67)中任意一个发生电平跳变,就

能自动把 MCU 唤醒。

RTC 禁止:低速定时时钟 功能超低功耗定时/计数

P5、P6上下拉控制:选择上拉下拉控制

驱动增强:所有 P5、P6 口 高电平 14 mA / 低电平 26 mA

休眠唤醒设置:休眠功能

IRC振荡器电压源选择:VDD、LDO 2.1V

VDD供电方式:这个一般调试才使用

3、功能描述

主要介绍一下寄存器的作用和功能

3.1 操作寄存器

3.1.1 RPAGE~R0/IAR (间接寻址寄存器)

XC8P9521 的指令 没有“直接”读写 RAM 的指令。因此通过IAR间接寻址寄存器进行读写。

RSR(RAM选择寄存器)来选择地址。从而实现对RAM读写。

下面的代码就是将0x00-0x3FSRAM数据存储器地址全部清零。

void file_clrRam()
{
	for(RSR=0xD0;RSR<0xFF;RSR++)	//清零 BANK0 RAM  IAR-R0,RSR-R4
									//RSR的高两位默认只读为1					
	{IAR = 0;}
	 IAR = 0;
}

3.1.2 RPAGE~R1/TCC(定时计数器)

TCC是一个8Bit上行计数器,TCC可读可写,计数溢出可形成中断。

通过CONT(控制寄存器)Bit<2 : 0>:PSR2~PSR0-TCC/WDT选择定时器的分频系数

通过OPTION配置表选择IRC(系统时钟频率)8M和Clocks(指令周期)4Clocks

在中断中函数中检测ISR(中断标志寄存器)Bit1位TCIF查看是否产生中断,执行完后清除中断

CONTW(0x02);			//TCC 8分频
公式:1/IRC频率 * 预分频 * (256-初值)=进入中断时间
TCC = 6;				//1/2 * 8 * (256-6) = 1000us

//中断处理
void int_isr(void) __interrupt 
{    	 
	__asm__("org 0x08");			        //中断入口地址			
	PUSH(_A_BUFF,_R3_BUFF);			        //中断入栈保护
    if(TCIF)					            //判断TCIF是否为1
    {
    	ISR    = 0xfe;				        //清TC0中断标志位
    	PORT6 ^= 0X10;//PORT6_4 = !PORT6_4;	//P64口翻转
    }	
	POP(_A_BUFF,_R3_BUFF);			//中断出栈保护恢复
}

3.1.3 RPAGE~R2/PC(程序计数器)

PC相当于单片机的导航,永远指向下一条要执行的指令地址 

是用于记录每个指令周期中CPU所要处理的指令的指针,PC将指令指针推进程序存储器,然后

指针自增1以进入下一个周期。XC8P9521拥有 一个10位宽度的程序计数器(0x0000–0x03FF)

可以通过直接修改程序计数器(PC)的值,可以改变程序的执行流程,跳转到指定的代码位置

3.1.4 RPAGE~R3/STATUS(状态寄存器)

主要用来检测单片机的一些状态,可以读出当前状态,具体功能查看寄存器

3.1.5 RPAGE~R4/RSR(RAM选择寄存器)

在间接寻址方式中用于选择RAM寄存器地址(寻址范围:0X00~0X3F)

3.1.6 RPAGE~R5/P0RT5(P5数据寄存器)

低四位有效 Bit3:0 从低到高对应P53~P50口   置1为输入 置0为输入

IOCP_W(P5CR,0x0);		//P5口设为输出
IOCP_W(P5CR,0xC);		//P50 P51口设为输出 P52 P53设置为输入

3.1.7 RPAGE~R6/P0RT6(P6数据寄存器)

Bit7:0  从高到低对应P67~P60口   置1为输入 置0为输入

IOCP_W(P6CR,0x08);		//P6口设为输出

3.1.8 RPAGE~R7/LVDCON(LVD控制寄存器)

LVDCON(低电压检测控制寄存器) 的主要作用是 配置和控制低电压检测(LVD)功能。LVD 功

能用于检测芯片的供电电压(VDD)是否低于某个预设的阈值,当电压低于这个阈值时,可以触发

中断或复位,以保护系统免受低电压的影响

Bit7:LVDEN   1:LVD使能 0:LVD禁止 

Bit6:VDET     1:LVD低于预设电压点 0:LVD高于预设电压点

Bit5:EXVEN  1:LVD检测电压选择P63端口输入 0:LVD检测电压选择VDD

Bit4:0:LVDSEL   LVD检测点从00000到11011,分别对应从2.0V到4.7V 每0.1V一个检测点。

 CONTW(0x80);    // 设置Bit<7>:LVDEN 位 置1使能             
                 //Bit<5>:EXVEN置0选择VDD电压 
                 // Bit<4:0>:LVDSEL[4:0] 设置检测电压,3.0V 对应 0b00110
 IOCP_W(LVDCON, 0x18);  // LVDSEL[4:0] = 0b00110
 if (LVDCON & 0x40) {  // 检查 Bit<6>:VDET 位
            // VDET 位为 1,表示 VDD 低于预设的 LVD 电压
            // 执行低电压处理逻辑
        } else {
            // VDET 位为 0,表示 VDD 高于或等于预设的 LVD 电压
            // 正常运行
        }

3.1.9 RPAGE~R8/PWMCON(PWM控制寄存器)

用来控制PWM计数器PWM1、PWM2、PWM3使能,以及分频系数的选择

Bit7:T1EN -T1/PWM计数器使能控制 1:使能 0:禁止

Bit6:4:PWM3EN~PWM1EN - PWM3~PWM1使能控制位

1:使能(PWM3(P60),PWM2(P61),PWM1(P62),相应端口设置为输出) 0:禁止

Bit3:T1PTEN -T1预分频选择控制位

Bit2:0:T1PSR2~T1PSR1分频系数选择位:

IOCP_W(IMR,0x09);		//TCC+PWM周期中断,根据需要调用	
PWMCON = 0xf8;			//使能3路pwm输出,pwm=1/8*2(分频)*4clock*100=100us
//PWM进入中断的时间=1/时钟频率IRC*PWM分频*时钟分频Clocks*PWM周期
PRD = 100;				//PWM周期寄存器

3.1.10 RPAGE~R9/PRD(PWM周期寄存器)

Bit:PRD-PWM周期八位数据

3.1.11 RPAGE~RA/PDC1(PWM1占空比寄存器)

Bit:PDC1-PWM1占空比八位数据

3.1.12 RPAGE~RB/PDC2(PWM2占空比寄存器)

Bit:PDC2-PWM2占空比八位数据

3.1.13 RPAGE~RC/PDC3(PWM3占空比寄存器)

Bit:PDC3-PWM3占空比八位数据

3.1.14 RPAGE~RD/ICIECR(输入状态变化中断使能)

这个寄存器允许你选择哪些 P6 引脚的状态变化可以触发中断

Bit7:0:IEN-P6输入状态变化中断使能控制位 1:使能 0:禁止ICIF

//=========端口改变中断程序===============//
void int_isr(void) __interrupt 
{    	
	__asm__("org 0x08");			//中断入口地址			
	PUSH(_A_BUFF,_R3_BUFF);			//中断入栈保护
    if(ICIF)					//判断ICIF是否为1,端口状态改变中断标志位 
	{
	    ISR    = 0xfd;				//清ICIF中断标志位
	    //处理想做的事
	}	 
	POP(_A_BUFF,_R3_BUFF);			//中断出栈保护恢复
}
void file_init(void)
{
    ICIECR=0xff//P6全部设为输入中断
    IOCP_W(P6CR,0xff);		//P6口设为输入
	IOCP_W(IMR,0x02);		//中断使能控制寄存器 ICIE
}
void main(void)
{
    file_init();//初始化
    EI();
    while(1)
    {
        
    }
}

3.1.15 RPAGE~RE/CPUCON(CPU模式控制寄存器)

用于控制 CPU 的工作模式和一些系统级的功能

3.1.16 RPAGE~RF/ISR(中断标志寄存器)

低四位有效,检测是否产生中断

Bit3:T1IF -T1/PWM周期中断标志

Bit2:EXIF-外部端口中断标志位

Bit1:ICIF-P6端口状态改变中断标志位

Bi0t:TCIF-TCC中断标志位

1:有中断,软件清0

0:无中断

3.2控制寄存器

3.2.1 CONT(控制寄存器)

CONT是非常重要的寄存器,用于控制和配置多种系统功能,定时计数器分频系数(TCC)、看门

狗定时器分频系数(WDT)、时钟信号源选择等。

Bit7:RTCS-TCC信号源选择 1:选择外部晶振时钟  0:TCC时钟由TCCCKS和TS决定

Bit6:INT-中断使能标志位 0:由指令或硬件禁止中断 1:由指令使能中断

Bit5:TS-TCC信号源选择位 0:内部指令周期时钟 1:外部输入信号(P62需要设置为输入口)

Bit4:TE-TCC信号边沿选择位

0:TCC引脚信号发生由低到高变化加1

1:TCC引脚信号发生由高到低变化加1

Bit3:PAB-预分频器分配位 0:预分频器分给TCC 1:预分频器分给WDT

Bit2:PSR2~PSR0-TCC/WDT预分频选择控制位

CONTW(0x02); //配置内部时钟
//RTCS-TCC信号源选择   0:TCC时钟由TCCCKS和TS决定
//INT-中断使能标志位   0:由指令或硬件禁止中断 
//TS-TCC信号源选择位   0:内部指令周期时钟 
//TE-TCC信号边沿选择位 0:TCC引脚信号发生由低到高变化加1 
//PAB-预分频器分配位   0:预分频器分给TCC 
//<2:0>PSR2~PSR0-TCC/WDT预分频选择控制位 010 TCC8分频

3.2.2 IOPAGE~IOC5/P5CR(P5方向控制寄存器)

配置P5为输入模式或输出模式

Port5方向控制位 1:输入 0:输出

IOCP_W(P5CR,0x00);	//P5口设为输出

3.2.3 IOPAGE~IOC6/P6CR(P6方向控制寄存器)

配置P6为输入模式或输出模式

Port6方向控制位 1:输入 0:输出

IOCP_W(P6CR,0x08);		//P6口设为输出

3.2.4 IOPAGE~IOC9/PHDCR(端口上下拉控制寄存器)

配置端口上拉下拉使能,配置上拉下拉需要在OPTION选项【P5、P6上下拉控制】选择【使能】

Bit7:4:P6下拉使能控制  0:使能 1:禁止

Bit3:0:P5上拉使能控制  0:使能 1:禁止

3.2.5 IOPAGE~IOCB/PDCR(端口下拉控制寄存器)

配置端口下拉控制

Bit6:4:P6PD下拉使能控制 0:使能 1:禁止

Bit3:0:P5PD下拉使能控制 0:使能 1:禁止

3.2.6 IOPAGE~IOCD/PHCR(端口上拉控制寄存器)

配置端口上拉控制

Bit7:0:P6PH上拉控制 0:使能 1:禁止

3.2.7 IOPAGE~IOCE/WDTCR(WDT使能控制寄存器)

配置看门狗使能、外部中断端口使能

Bit7:WDTEN-WDT使能控制  1:使能 0:使能必须配置OPTION选项【看门狗】选择【使能】

Bit6:EIS-P60外部中断端口使能位

1:使能,在这种情况下,P60的I/O控制位(P6CR的Bit0)必须设为“1”,管脚 的状态可以由P6端口读取,P60下降沿触发外部中断

0:禁止,P60为双向I/O管脚,EXINT通道被屏蔽

3.2.8 IOPAGF~IOCF/IMR(中断使能控制寄存器)

只使用了低四位,配置中断使能

Bit3:T1IE-T1中断使能控制   1:使能 0:禁止

Bit2:EXIE-外部中断使能控制 1:使能 0:禁止

Bit1:P6ICIE-P6端口状态改变中断使能控制 1:使能 0:禁止

Bit0:TCIE-TCC溢出中断使能控制 1:使能 0:禁止

IOCP_W(IMR,0x01); //定时器TCC中断使能

4、例程开发

Demo文件资料-无锡矽杰微电子有限公司https://www.xjmcu.com/about_download/1Demowjzl546.html

打开上面网址下载XC8P9521_Demo,随便打开一个例程,这里以PWM为例

点击Option Code 这里不用配置系统会默认配置好 特定需求自己再配置即可

XJ_9521Define.h头文件

这些宏定义实际上是将一些常用的汇编指令封装成更易于使用的 C 语言宏

#define EI()  __asm__(" ei ")        //开启全局中断
#define DI()  __asm__(" di ")        //关闭全局中断
#define NOP() __asm__(" nop ")       //执行一个空操作
#define CWDT() __asm__(" CWDT ")     //清除看门狗定时器
#define SLEEP() __asm__(" sleep ")   //使系统进入睡眠模式

//IOC页就是控制寄存器
#define CONTW(VAL)			__asm__("mov a,@"#VAL"\n ctw")		//CTW = VAL:CONT寄存器赋值
#define IOCP_W(REG,VAL)		__asm__("mov a,@"#VAL"\n iw "#REG)	//REG = VAL:IOC页寄存器赋值
#define IOCP_R(RAM,REG)		__asm__("ir "#REG"\n mov "#RAM",a")	//RAM = REG:IOC页寄存器读值

//用于对 IOC 页寄存器进行按位与和按位或操作。
#define IOCP_W_AND(REG,VAL) __asm__("ir "#REG"\n and a,@"#VAL"\n iw "#REG) 
#define IOCP_W_OR(REG,VAL)  __asm__("ir "#REG"\n or a,@"#VAL"\n iw "#REG)

//中断入栈保护 保护当前寄存器的状态,放置中断处理过程中修改这些寄存器的值	
#define PUSH(A_REG,R3_REG)	__asm__("mov "#A_REG",a\n swap "#A_REG"\n swapa STATUS\n mov "#R3_REG",a")	
//中断出栈保护恢复
#define POP(A_REG,R3_REG)	__asm__("swapa "#R3_REG"\n mov STATUS,a\n swapa "#A_REG)

XC8P9521.h"头文件

这段代码定义了一系列的 寄存器地址 和 位操作结构体

将每个寄存器的地址映射到一个唯一的地址,并且通过这些宏可以直接访问和操作 IAR 寄存器

的某一 位

//定义寄存器地址
#define IAR_ADDR          0X00

//这些 extern 声明将每个寄存器定义为一个特殊的函数寄存器(__sfr),并使用 __at 关键字将它们放置在特定的地址上。这样,你可以通过这些寄存器的名称直接访问它们,就像访问普通的变量一样
extern __sfr __at (IAR_ADDR)         IAR;         
        
//这段代码定义了一个联合体(union),其中包含一个结构体,用于访问 IAR 寄存器的各个位。通过这种方式,你可以直接操作 IAR 寄存器的特定位
typedef union {
    struct {
        unsigned char IAR_0:1;
        unsigned char IAR_1:1;
        unsigned char IAR_2:1;
        unsigned char IAR_3:1;    
        unsigned char IAR_4:1;
        unsigned char IAR_5:1;
        unsigned char IAR_6:1;
        unsigned char IAR_7:1;        
    };
} __IARbits_t;

//定义一个__IARbits_t 类型的变量IARbits,地址指向IAR_ADDR
extern volatile __IARbits_t __at(IAR_ADDR) IARbits;

//这样通过宏定义可以访问 IAR地址0x00的第一位          
#define IAR_0          IARbits.IAR_0    /* bit 0 */
#define IAR_1          IARbits.IAR_1    /* bit 1 */
#define IAR_2          IARbits.IAR_2    /* bit 2 */
#define IAR_3          IARbits.IAR_3    /* bit 3 */
#define IAR_4          IARbits.IAR_4    /* bit 4 */
#define IAR_5          IARbits.IAR_5    /* bit 5 */
#define IAR_6          IARbits.IAR_6    /* bit 6 */
#define IAR_7          IARbits.IAR_7    /* bit 7 */ 

主函数

主函数这里主要做一些初始化,前面都提到过了

//===================================//
//中断服务程序
//===================================//
void int_isr(void) __interrupt 
{    	
	__asm__("org 0x08");			//中断入口地址			
	PUSH(_A_BUFF,_R3_BUFF);			//中断入栈保护
//=========Tcc中断程序===============//
	if(TCIF)					//判断TCIF是否为1
	{
		TCC	  += 6;			//1/2 * 8 * (256-6) = 1000us	公式:1/IRC频率 * 预分频 * (256-初值)
		ISR    = 0xfe;				//清TC0中断标志位
	 PORT6 ^= 0X10;//PORT6_4 = !PORT6_4;			//P64口翻转
	}	 
//======== PWM周期中断程序===============//
	if(T1IF)					//判断T1IF是否为1,pwm周期中断
	{	 
		ISR    = 0xf7;				//ISR的bit3:T1IF清零
		PORT6 ^= 0X20;//PORT6_5 = !PORT6_5;			//P65口翻转
	}
	POP(_A_BUFF,_R3_BUFF);			//中断出栈保护恢复
}
//===================================//
//初始化RAM:10H~3FH
//===================================//
void file_clrRam()
{
	for(RSR=0xD0;RSR<0xFF;RSR++)	//清零 BANK0 RAM  IAR-R0,RSR-R4
	    							//RSR的高两位默认只读为1					
	{IAR = 0;}
	 IAR = 0;
}
//===================================//
//端口初始化
//===================================//
void file_init(void)
{
	IOCP_W(WDTCR,0x00);		//WDT 使能控制寄存器	
	CONTW(0x02);			//TCC 8分频
	TCC = 6;				//1/2 * 8 * (256-6) = 1000us	公式:1/IRC频率 * 预分频 * (256-初值)
	PORT5 = 0;				//P5口输出低
	PORT6 = 0;				//P6口输出低
	IOCP_W(P5CR,0x0);		//P5口设为输出
	IOCP_W(P6CR,0x08);		//P6口设为输出
	IOCP_W(PHDCR,0xff);		//端口上下拉控制寄存器,bit7-bit4对应P67-P64下拉;bit3-bit0对应P53-P50上拉 
	IOCP_W(PDCR,0xff);		//端口下拉控制寄存器,  bit6-bit4对应P62-P60,bit3-bit0对应P53-P50
	IOCP_W(PHCR,0xff);		//P6端口上拉控制寄存器  bit7-bit0对应P67-P60

	IOCP_W(IMR,0x01);		//中断使能控制寄存器 
	ISR = 0x0;				//清TC0中断标志位 
}

//===================================//
//TC0初始化
//===================================//
void file_project_init(void)
{	
//---------------------------------
//PWM设置,3路pwm是共周期的
//---------------------------------
//file_pwm_set:   
	IOCP_W(IMR,0x09);		//TCC+PWM周期中断,根据需要调用	
	PWMCON = 0xf8;			//使能3路pwm输出,pwm=1/8*2(分频)*4clock*100=100us
	PRD = 100;				//PWM周期寄存器
	PDC1 = 20;				//pwm1占空比
	PDC2 = 40;				//pwm2占空比
	PDC3 = 80;				//pwm3占空比
}
//===================================//
//MAIN主程序
//===================================//
void main()
{
	file_clrRam();					//清RAM
	file_init();					//io寄存器初始化
	file_project_init();			//程序所需功能设置
	EI();							//打开总中断
	while(1)
	{
	//	PORT6_1 = !PORT6_1;	
		NOP();
		NOP();
		NOP();
	}
}

 以上只是按照自己的理解,实际开发请参考官方用户手册XC8P9521-V1.3.pdf

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐