矽杰微MCU开发------XC8P9521(GPIO)
XC8P9521这款芯片内存1KROM、48字节SRAM。并且是OTP的ROM意味只能烧写一次程序。外设只有一些最基本的GPIO、PWM、TCC、WDT等。系统框架介绍MCU运行逻辑。
芯片介绍
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
更多推荐



所有评论(0)