STC8H系列单片机是不需要外部晶振和外部复位的单片机。在相同的工作频率下,STC8H系列单片机比传统的8051快约12倍,是宽电压/高速/高可靠/低功耗/强抗静电/较强干扰的新一代8051单片机。
CORE:包含51的官方interrupt.asm和STARTUP.A51
BSP:主要包含驱动文件,如GPIO、uart
APP:目前只存放main.c和stc8hxx_it.c中断文件
OBJ:存放编译生成文件
Header:存放全部.h格式头文件
③选择Options For Target
①将Output和Listing的生成文件夹变成OBJ
②将Target–Memory Model设置成Large
③在C51–include Paths添加头文件路径,记得将其修改成相对路径
"sfr"指令:用来直接描述硬件地址。小白先解成一组IO口的起始地址中数据 sfr P0 = 0x80; P0=0;//也就是对P0口全部给0
"sbit"指令:对应可位导址空间的一个位,小白先理解成"一个IO口/针脚"的地址中的数据。
sfr P0 = 0x80;
sbit P00 = P0^0;
注意:实际上了解一下就好,一般封装在官方头文件stc8h.h中
这个最好直接问公司前辈,因为下载方式不唯一,而且问人比较快
STC8H系列单片机的程序存储器和数据存储器各自独立编址的。
单片机复位后,程序计数器(PC)的内容为00001,从 0000H 单元开始执行程序。另外中断服务程序的入口地址(又称中断向量)也位于程序存储器单元。
在程序存储器中,每个中断都有一个固定的入口地址,当中断发生并得到响应后,单片机就会自动跳转到相应的中断入口地址去执行程序。外部中断0(INT0)的中断服务程序的入口地址是0003H,定时器/计数器0(TIMER0)中断服务程序的入口地址是 000BH,外部中断1(TNT1)的中断服务程序的入口地址是0013H,定时器/计数器|(TIMERI)的中断服务程序的入口地址是001BH等。
其内部集成大容量的数据存储器,有两个地址空间:内部RAM(256字节)和内部扩展RAM
内部RAM共256字节,可分为2个部分:低128字节RAM和高128字节RAM。低128字节的数据存储器与传统8051兼容,既可直接寻址也可间接寻址。高128字节RAM(在8052中扩展了高128字节RAM)与特殊功能寄存器区共用相同的逻辑地址,都使用8OH~FFH,但在物理上是分别独立的,使用时通过不同的寻址方式加以区分。高128字节RAM只能间接寻址,特殊功能寄存器区只可直接寻址。
低128字节RAM 也称通用 RAM 区。通用RAM 区又可分为工作寄存器组区,可位寻址区,用户RAM区和堆栈区。工作寄存器组区地址从00H1FH共32字节单元,分为4组,每一组称为一个寄存器组,每组包含8个8位的工作寄存器,编号均为ROR7,但属于不同的物理空间。通过使用工作寄存器组,可以提高运算速度。RO~R7是常用的寄存器,提供4组是因为1组往往不够用。程序状态字PSW寄存器中的RS1和RSO组合决定当前使用的工作寄存器组,见下面PSW寄存器的介绍。
写0:输出低电平到端口缓冲区
写1:输出高电平到端口缓冲区
读:直接读端口管脚上的电平
配置端口模式
注意:
①当有I/O口被选择为ADC输入通道时,必须设置PxM0/PxM1寄存器将I/O口模式设置成输入模式。另外如果MCU进入掉电模式/时钟停振模式后,仍需使能ADC通道,则需要设置PxIE寄存器关闭数字输入,才能保证不会有额外的耗电
②虽然每个I/O口在弱上拉(准双向口)/强推挽输出/开漏模式时都能承受20mA的灌电流(还是要加限流电阻),但整个芯片的工作电流推荐不要超过70mA
0:禁止端口内部的4.1K上拉电阻
1:使能端口内部的4.1K上拉电阻
void GPIO_Init(void)
{
P10 = 1;
P11 = 1;
P0M0 = 0x00; //设置P0.9~P0.7为双向口模式
P0M1 = 0x00;
P1M0 = 0xff; //设置P1.1~P1.7为推挽输出模式
P1M1 = 0x00;
P2M0 = 0x00; //设置P2.0~P2.7为高阻输入模式
P2M1 = 0xff;
P3M0 = 0xff; //设置P3.0~P3.7为开漏模式
P3M1 = 0xff;
P1PU |= 0x03; //bBit(0) | bBit(1);即使能1.1和1.0内部上拉电阻
/****************I/O读写模式*********************/
P10 = 1; //P1.0口输出高电平
P10 = 0; //P1.0口输出低电平
P10 = 1; //读取端口前先使能内部弱上拉电阻
//当使用_nop_()函数(可理解为软件延时)时,必须在开头添加头文件#include。
//_nop_()函数相当于一个空操作(可以理解为NOP空操作指令),而_nop_()函数的空操作产生的时间与晶振有关:大概1个时钟周期
_nop_();
CY = P00; //读取端口状态
}
STC8H1K08有3个定时器,定时器/计数器0有4种工作模式:模式0(16位自动重装载模式),模式1(16位不可重装载模式),模式2(8位自动重装模式),模式3(不可屏蔽中断的16位自动重装载模式)。
定时器/计数器1除模式3外,其他工作模式与定时器/计数器0相同。T1 在模式3时无效,停止计数。
定时器T2的工作模式固定为16位自动重装载模式。T2可以当定时器使用,也可以当串口的波特率发生器和可编程时钟输出。
主要以定时器0为主
TF1:T1溢出中断标志。T1被允许计数以后,从初值开始加1计数。当产生溢出时由硬件将TF1位置“1”,并向CPU请求中断,一直保持到CPU响应中断时,才由硬件清“0”(也可由查询软件清“0”)。
TR1:定时器T1的运行控制位。该位由软件置位和清零。当GATE(TMOD.7)=0,TR1=1时就允许T1开始计数,TR1-0时禁止T1计数。当GATE(TMOD.7)=1,TR1=1且INT1输入高电平时,才允许T1计数。
TF0:TO溢出中断标志。TO被允许计数以后,从初值开始加1计数,当产生溢出时,由硬件置“1”TFO,向CPU请求中断,一直保持CPU响应该中断时,才由硬件清0(也可由查询软件清0)。
TR0:定时器T0的运行控制位。该位由软件置位和清零。当GATE(TMOD.3)-0,TRO=1时就允许T0开始计数,TR0-0时禁止T0计数。当GATE(TMOD.3)=1,TRO=1且INT0输入高电平时,才允许TO计数,TRO-0时禁止T0计数。
IE1:外部中断1请求源(DNT1/P3.3)标志。IE1=1,外部中断向CPU请求中断,当CPU响应该中断时由硬件清“0”IE1。
IT1:外部中断源1触发控制位。IT1=0,上升沿或下降沿均可触发外部中断1。IT1=1,外部中断1程控为下降沿触发方式。
IE0:外部中断0请求源(INTO/P3.2)标志。IE0=1外部中断0向CPU请求中断,当CPU响应外部中断时,由硬件清“0”IE0(边沿触发方式)。
IT0:外部中断源0触发控制位。IT0-0,上升沿或下降沿均可触发外部中断0。ITO=1,外部中断0程控为下降沿触发方式。
T1_GATE:控制定时器1,置1时只有在INT1脚为高及TR1控制位置1时才可打开定时器/计数器1。
T0_GATE:控制定时器0,置1时只有在INT0脚为高及TR0控制位置1时才可打开定时器/计数器0。
T1_C/T:控制定时器1用作定时器或计数器,清0则用作定时器(对内部系统时钟进行计数),置1用作计数器(对引脚T1/P3.5外部脉冲进行计数)
T0_C/T:控制定时器0用作定时器或计数器,清0则用作定时器(对内部系统时钟进行计数),置1用作计数器(对引脚T0/P3.4外部脉冲进行计数)。
T1_MI/T1_M0:定时器定时器/计数器1模式选择
定时器/计数器0模式选择
T0_M1 | T0_M0 | 定时器/计数器工作模式 |
---|---|---|
0 | 0 | 16位自动重载模式 |
0 | 1 | 16位不自动重载模式 |
1 | 0 | 8位自动重载模式 |
1 | 1 | 不可屏蔽中断的16位自动重载模式 |
注意:自动重载模式在计数器值溢出后,系统会将内部重载寄存器的值装进去;不自动重载会从0开始计数
(1)当GATE=0(TMOD.3)时,如TRO-1,则定时器计数。GATE=1 时,允许由外部输入INTO控制定时器0,这样可实现脉宽测量。TRO为TCON 寄存器内的控制位,TCON寄存器各位的具体功能描述见上节TCON寄存器的介绍。
(2)当CT-0时,多路开关连接到系统时钟的分频输出,To对内部系统时钟计数,TO 工作在定时方式。当c/T=1 时,多路开关连接到外部脉冲输入P3.4/T0,即TO工作在计数方式。
(3)STC 单片机的定时器 0 有两种计数速率:一种是 12T模式,每 12个时钟加 1,与传统 8051 单片机相同;另外一种是1T模式,每个时钟加1,速度是传统8051 单片机的12 倍。TO的速率由特殊功能寄存器 AUXR中的TOx12 决定,如果TOx12-0,TO则工作在12T模式;如果 TOx12=1,TO 则工作在1T模式
(4)定时器0有两个隐藏的寄存器 RL_THO 和 RL_TLO。RL_THO 与THO 共有同一个地址,RL_TLO与 TLO共有同一个地址。当TRO-0即定时器/计数器0被禁止工作时,对TLO写入的内容会同时写入RL_TLO,对THO 写入的内容也会同时写入 RL_THO。当 TRO=1 即定时器/计数器 0被允许工作少,对TL0写入内容,实际上不是写入当前寄存器TLO中,而是写入隐藏的寄存器 RL_TLO中,对THO写入内容,实际上也不是写入当前寄存器 THO 中,而是写入隐藏的寄存器 RL_THO,这样可以巧妙地实现16位重装载定时器。当读 THO和TLO的内容时,所读的内容就是THO和TL0的内容,而不是RL_THO和 RL_TLO的内容。
(5)当定时器0工作在模式 0(TMOD[1:0]/[M1,MO]=00B)时,[THO,TLO]的溢出不仅置位TFO,而且会自动将[RL_THO,RL_TLO]的内容重新装入[THO,TLO]。
(6)当TOCLKO/INT_CLKO.0=1 时,P3.5/T1管脚配置为定时器0的时钟输出 TOCLKO。输出时钟频率为T0溢出率/2。
当定时器/计数器0工作在16位模式时,TL0和TH0组合成一个16位寄存器,TL0为低字节,TH0为高字节。
符号 | 地址 | B7 | B6 | B5 | B4 | B3 | B2 | B1 | B0 |
---|---|---|---|---|---|---|---|---|---|
AUXR | 8EH | T0x12 | T1x12 | UART_M0x6 | T2R | T2_C/T | T2x12 | EXTRAM | S1ST2 |
T0x12(地址B7,即最高位):定时器0速度控制位
0:12T模式,即CPU时钟12分频
1:1T模式,即CPU时钟不分频
T0CLKO:定时器0时钟输出控制
0:关闭时钟输出
1:使能P3.5口的是定时器0时钟输出功能。当定时器0计数发生溢出时,P3.5口的电平自动发生翻转
void TM0_Rountine(void) interrupt 1;
EA:总中断允许控制位。EA的作用是使中断允许形成多级控制。即各中断源首先受EA控制:其次还受各中断源自己的中断允许控制位控制。
0:CPU屏蔽所有的中断申请
1:CPU开放中断
ELVD:低压检测中断允许位。
0:禁止低压检测中断
1:允许低压检测中断
EADC:AD转换中断允许位。
0:禁止AD转换中断
1:允许AD转换中断
ES:串行口1中断允许位。
0:禁止串行口1中断
1:允许串行口1中断
ET1:定时/计数器T1的溢出中断允许位。
0:禁止T1中断
1:允许T1中断
EX1:外部中断1中断允许位。
0:禁止NT1中断
1:允许NT1中断
ET0:定时/计数器T0的溢出中断允许位。
0:禁止T0中断
1:允许T0中断
EX0:外部中断0中断允许位。
0:禁止NT0中断
1:允许NT0中断
TF1:定时器1溢出中断标志。中断服务程序中,硬件自动清零。TF0:定时器0溢出中断标志。中断服务程序中,硬件自动清零。IE1:外部中断1中断请求标志。中断服务程序中,硬件自动清零
IE0:外部中断0中断请求标志。中断服务程序中,硬件自动清零
INT4IF:外部中断4中断请求标志。中断服务程序中硬件自动清零INT3IF:外部中断3中断请求标志。中断服务程序中硬件自动清零INT2IF:外部中断2中断请求标志。中断服务程序中硬件自动清零
T4F:定时器4溢出中断标志。中断服务程序中硬件自动清零(注意:此位为只写寄存器,不可读)。
T3IF:定时器3溢出中断标志。中断服务程序中硬件自动清零(注意:此位为只写寄存器,不可读)
T2IF:定时器2溢出中断标志。中断服务程序中硬件自动清零(注意:此位为只写寄存器,不可读)。
//定时器配置
void Timer0Init(void) //默认时钟24MHz
{
AUXR |= 0x80; //设置定时器为IT模式
TMOD |= 0x00; //设置成模式0
TL0 = 0x9A; //设置初始值
TH0 = 0xA9; //@(65536-[TH0,TL0])/sysclk,即多少分频
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
ET0 = 1; //使能定时器中断
EA = 1; //开启全局中断
}
//定时器中断函数,用来处理事件
void TIM0_ISR(void) interrupt 1
{
if((TCON & 0x20) != 0) //清TIM0溢出中断标志
{
TCON &= ~0x20;
}
if(my_Timer.U16_Timer_Power_On > 0)
my_Timer.U16_Timer_Power_On-- ;
}
STC8H系列单片机具有4个全双工异步串行通信接口。每个串行口由2个数据缓冲器、一个移位寄存器、一个串行控制寄存器和一个波特率发生器等组成。每个串行口的数据缓冲器由2个互相独立的接收、发送缓冲器构成,可以同时发送和接收数据。
STC8系列单片机的串口1有4种工作方式,其中两种方式的波特率是可变的,另两种是固定的,以供不同应用场合选用。串口2/串口3/串口4都只有两种工作方式,这两种方式的波特率都是可变的。用户可用软件设置不同的波特率和选择不同的工作方式。主机可通过查询或中断方式对接收/发送进行程序处理,使用十分灵活。
其相关寄存器如下:
S2SM2:允许串口2在模式1时允许多机通信控制位。在模式1时,如果S2SM2位为1且S2REN位为1,则接收机处于地址帧筛选状态。此时可以利用接收到的第9位(即S2RB8)来筛选地址帧:①若 S2RB8=1,说明该帧是地址帧,地址信息可以进入 S2BUF,并使 S2RI为1,进而在中断服务程序中再进行地址号比较;
②若S2RB8=0,说明该帧不是地址帧,应丢掉且保持S2RI=0。在模式1 中,如果S2SM2位为0且S2REN位为1,接收收机处于地址帧筛选被禁止状态。
不论收到的S2RB8 为0或1,均可使接收到的信息进入 S2BUF,并使 S2RI=1,此时 S2RB8通常为校验位。模式0 为非多机通信方式,在这种方式时,要设置S2SM2应为0。
S2REN:允许/禁止串口接收控制位
0:禁止串口接收数据
1:允许串口接收数据
S2TB8:当串口2使用模式1时,S2TB8为要发送的第9位数据,一般用作校验位或者地址帧/数据帧标志位,按需要由软件置位或清0。在模式0中,该位不用。
S2RB8:当串口2使用模式1时,S2RB8为接收到的第9位数据,一般用作校验位或者地址帧/数据帧标志位。在模式0中,该位不用。
S2TI:串口2发送中断请求标志位。在停止位开始发送时由硬件自动将S2TI置1,向CPU发请求中断,响应中断后S2TI必须用软件清零
S2RI:串口2接收中断请求标志位。串行接收到停止位的中间时刻由硬件自动将S2RI置1,向CPU发中断申请,响应中断后S2RI必须由软件清零
S2BUF:串口1数据接收(发送缓冲区。S2BUF实际是2个缓冲器,读缓冲器和写缓冲器,两个操作分别对应两个不同的寄存器,1个是只写寄存器(写缓冲器),1个是只读寄存器(读缓冲器)。对S2BUF 进行读操作,实际是读取串口接收缓冲区,对S2BUF进行写操作则是触发串口开始发送数据。
T2R:定时器2的运行控制位
0:定时器2停止计数
1:定时器2开始计数
T2x12:定时器2速度控制位
0:12T模式,即CPU时钟12分频(FOSC/12)
1: 1T模式,即CPU时钟不分频分频(FOSC/1)
S1ST2:串口1波特率发生器选择位**(串口2不需要)**
0:选定时器1作为波特率发生器
1:选定时器2作为波特率发生器
串行口2的模式0为8位数据位可变波特率UART工作模式。此模式一帧信息为10位:1位起始位,8位数据位(低位在先)和1位停止位。波特率可变,可根据需要进行设置波特率。TxD2为数据发送口,RxD2为数据接收口,串行口全双工接受/发送。
串口2的波特率是可变的,其波特率由定时器2产生。当定时器采用1T模式时(12倍速),相应的波特率的速度也会相应提高12倍。
ES2:串行口2中断允许位
0:禁止串口2中断
1:允许串口2中断
TI:串口1发送完成中断请求标志。需要软件清零。
RI:串口1接收完成中断请求标志。需要软件清零。
S2TI:串口2发送完成中断请求标志。需要软件清零。
S2RI:串口2接收完成中断请求标志。需要软件清零。
S3TI:串口3发送完成中断请求标志。需要软件清零。
S3RI:串口3接收完成中断请求标志。需要软件清零。
S4TI:串口4发送完成中断请求标志。需要软件清零。
S4RI:串口4接收完成中断请求标志。需要软件清零。
PS2H,PS2:串口2中断优先级控制位
00:串口2中断优先级为0级(最低级)
01:串口2中断优先级为1级(较低级)
10:串口2中断优先级为2级(较高级)
11:串口2中断优先级为3级(最高级)
bit busy; //定义二进制数据
char wptr;
char rptr;
char buffer[16];
//串口初始化程序
void UartInit(void) //[email protected]
{
IP2 |= 0x01; //中断优先级,优先级为3最高级
IP2H |= 0x01;
T2L = BRT; //BRT为独立波特率发生器的溢出率。
T2H = BRT >> 8; //设置定时器初始值
AUXR = 0x14; //开启定时器2的IT模式
S2CON = 0x10; //使能串口接收
IE2 |= 0x01; //使能串口中断
EA = 1; //开启全局中断
}
//串口发送字节数据
void Uart2Send(char dat)
{
while(busy);
busy = 1;
S2BUF = dat;
}
//串口发送字符串
void Uart2SendStr(char *p)
{
while(*p)
{
Uart2Send(*p++)
}
}
//串口中断程序
void UART2_ISR(void) interrupt 8
{
if(S2CON & 0x02)
{
S2CON &= (uint8)(~0x02); //清中断标志位
busy = 0;
}
if(S2CON & 0x01)
{
S2CON &= (uint8)(~0x01); //清中断标志位
buffer[wptr++] = S2BUF;
wptr &= 0x0f;
}
}
主机设置:SSIG设置为1,MSTR设置为1,固定为主机模式。主机可以使用任意端口连接从机的SS管脚,拉低从机的SS脚即可使能从机
从机设置:SSIG设置为0,SS管脚作为从机的片选信号。
设置方法1:两个设备初始化时都设置为SSIG设置为0,MSTR 设置为1,且将SS 脚设置为双向口模式输出高电平。此时两个设备都是不忽略SS的主机模式。当其中一个设备需要启动传输时,可将自己的SS脚设置为输出模式并输出低电平,拉低对方的SS脚,这样另一个设备就被强行设置为从机模式了。
设置方法2:两个设备初始化时都将自己设置成忽略SS的从机模式,即将SSIG设置为1,MSTR设置为0。当其中一个设备需要启动传输时,先检测SS管脚的电平,如果时候高电平,就将自己设置成忽略SS的主模式,即可进行数据传输了。
主机设置:SSIG设置为1,MSTR设置为1,固定为主机模式。主机可以使用任意端口分别连接各个从机的SS管脚,拉低其中一个从机的SS脚即可使能相应的从机设备
从机设置:SSIG设置为0,SS管脚作为从机的片选信号。
SPIF:SPI中断标志位。
当发送/接收完成1字节的数据后,硬件自动将此位置1,并向CPU提出中断请求。当SPI处于主机模式且SSIG位被设置为0时,如果SS管脚的输入电平被驱动为低电平时,此标志位也会被硬件自动置1,以标志设备模式发生变化。
注意:此标志位必须用户通过软件方式向此位写1进行清零。
WCOL:SPI写冲突标志位。
当SPI在进行数据传输的过程中写SPDAT寄存器时,硬件将此位置1。
注意:此标志位必须用户通过软件方式向此位写1进行清零。
SSIG:SS引脚功能控制位
0:SS引脚确定器件是主机还是从机
1:忽略SS引脚功能,使用MSTR确定器件是主机还是从机SPEN:SPI使能控制位
0:关闭SPI功能
1:使能SPI功能
DORD:SPI数据位发送/接收的顺序
0:先发送/接收数据的高位(MSB)
1:先发送/接收数据的低位(LSB)
MSTR:器件主/从模式选择位
设置主机模式:
若SSIG=0,则SS管脚必须为高电平且设置MSTR为1
若SSIG=1,则只需要设置MSTR为1(忽略SS管脚的电平)
设置从机模式:
若SSIG=0,则SS管脚必须为低电平(与MSTR位无关)
若SSIG=1,则只需要设置MSTR为0(忽略SS管脚的电平)CPOL:SPI时钟极性控制
0:SCLK空闲时为低电平,SCLK的前时钟沿为上升沿,后时钟沿为下降沿
1:SCLK空闲时为高电平,SCLK的前时钟沿为下降沿,后时钟沿为上升沿
CPHA:SPI时钟相位控制
0:数据SS管脚为低电平驱动第一位数据并在SCLK的后时钟沿改变数据,前时钟沿采样数据(必
须SSIG=0)
1:数据在SCLK的前时钟沿驱动,后时钟沿采样
(1)从机模式的注意事项:
当CPHA=0时,SSIG必须为0(即不能忽略SS 脚)。在每次串行字节开始还发送前SS 脚必须拉低,并且在串行字节发送完后须重新设置为高电平。SS管脚为低电平时不能对SPDAT寄存器执行写操作,否则将导致一个写冲突错误。CPHA=0且SSIG=1时的操作未定义。
当CPHA=1时,SSIG可以置1(即可以忽略脚)。如果SSIG=0,SS 脚可在连续传输之间保持低有效(即一直固定为低电平)。这种方式适用于固定单主单从的系统。
(2)主机模式的注意事项:
在SPI中,传输总是由主机启动的。如果SPI使能(SPEN=1)并选择作为主机时,主机对SPI数据寄存器SPDAT的写操作将启动SPI时钟发生器和数据的传输。在数据写入SPDAT之后的半个到一个SPI 位时间后,数据将出现在 MOSI脚。写入主机 SPDAT寄存器的数据从 MOSI 脚移出发送到从机的 MOSI 脚。同时从机SPDAT寄存器的数据从MISO脚移出发送到主机的MISO脚。
传输完一个字节后,SPI时钟发生器停止,传输完成标志(SPIF)置位,如果SPI中断使能则会产生一个SPI中断。主机和从机 CPU的两个移位寄存器可以看作是一个16位循环移位寄存器。当数据从主机移位传送到从机的同时,数据也以相反的方向移入。这意味着在一个移位周期中,主机和从机的数据相互交换。
(3)通过SS改变模式
如果SPEN=1,SSIG=0且MSTR=l,SPI使能为主机模式,并将SS 脚可配置为输入模式化或准双向口模式。这种情况下,另外一个主机可将该脚驱动为低电平,从而将该器件选择为SPI从机并向其发送数据。为了避免争夺总线,SPI系统将该从机的MSTR清零,MOSI和SCLK强制变为输入模式,而MISO则变为输出模式,同时SPSTAT的SPIF标志位置1。
用户软件必须一直对MSTR位进行检测,如果该位被一个从机选择动作而被动清零,而用户想继续将SPI作为主机,则必须重新设置MSTR位,否则将一直处于从机模式。
(4)写冲突
SPI 在发送时为单缓冲,在接收时为双缓冲。这样在前一次发送尚未完成之前,不能将新的数据写入移位寄存器。当发送过程中对数据寄存器 SPDAT进行写操作时,WCOL 位将被置1以指示发生数据写冲突错误。在这种情况下,当前发送的数据继续发送,而新写入的数据将丢失。
当对主机或从机进行写冲突检测时,主机发生写冲突的情况是很罕见的,因为主机拥有数据传输的完全控制权。但从机有可能发生写冲突,因为当主机启动传输时,从机无法进行控制。
接收数据时,接收到的数据传送到一个并行读数据缓冲区,这样将释放移位寄存器以进行下一个数据的接收。但必须在下个字符完全移入之前从数据寄存器中读出接收到的数据,否则,前一个接收数据将丢失。
WCOL可通过软件向其写入"1"清零。
//SPI初始化
void SPI_Init(void)
{
SPCTL = 0x50; //使能SPI主机模式
// SPCTL = 0x40; //使能SPI从机模式
SPSTAT = 0xC0; //清中断标志
// IE2 = ESPI; //使能SPI中断,没有即查询模式
EA = 1;
}
//SPI中断函数
void SPI_ISR() interrupt 9
{
SPSTAT = 0xC0;
SS = 1;
busy = 0;
LED = !LED;
}
//查询方式函数
while(1)
{
SS = 0; //拉低从机SS管脚
SPDAT = 0x5a;
while(!(SPSTAT & 0x80));
SPSTAT = 0xc0;
SS = 1;
LED = !LED;
}
STC8H 系列单片机内部集成了大容量的 EEPROM。利用 ISP/IAP技术可将内部 Data Flash 当EEPROM,擦写次数在10万次以上。EEPROM可分为若干个扇区,每个扇区包含512字节。
注意:EEPROM的写操作只能将字节中的1写为0,当需要将字节中的0写为1,则必须执行扇区擦除操作。EEPROM的读/写操作是以1字节为单位进行,而EEPROM擦除操作是以1扇区(512字节)为单位讲行,在执行擦除操作时,如果目标扇区中有需要保留的数据,则必须预先将这些数据读取到 RAM 中暂存,待擦除完成后再将保存的数据和需要更新的数据一起再写回EEPROM/DATA-FLASH。
所以在使用 EEPROM 时,建议同一次修改的数据放在同一个扇区,不是同一次修改的数据放在不同的扇区,不一定要用满。数据存储器的擦除操作是按扇区进行的(每扇区512字节)。
EEPROM可用于保存一些需要在应用过程中修改并且掉电不丢失的参数数据。在用户程序中,可以对EEPROM进行字节读/字节编程/扇区擦除操作。在工作电压偏低时,建议不要进行EEPROM操作,以免发送数据丢失的情况。
EEPROM操作时间
■ 读取1字节:4个系统时钟(使用MOVC指令读取更方便快捷)
■ 编程1字节:约30~40us(实际的编程时间为6~7.5us,但还需要加上状态转换时间和各种控制信号的SETUP和HOLD时间)
■ 擦除1扇区(512字节):约4~6ms
EEPROM操作所需时间是硬件自动控制的,用户只需要正确设置IAP TPS寄存器即可。IAP_TPS=系统工作频率/1000000(小数部分四舍五入进行取整)
例如:系统工作频率为12MHz,则IAP_TPS设置为12
在进行EEPROM的读操作时,命令执行完成后读出的EEPROM数据保存在IAPDATA寄存器中。
在进行EEPROM的写操作时,在执行写命令前,必须将待写入的数据存放在IAPDATA寄存器中,再发送写命令。擦除 EEPROM命令与IAP DATA寄存器无关。
EEPROM进行读、写、擦除操作的目标地址寄存器。IAP ADDRH保存地址的高字节,IAP ADDRL 保存地址的低字节
CMD[1:0]:发送EEPROM操作命令
00:空操作
01:读EEPROM命令。读取目标地址所在的1字节。
10:写EEPROM命令。写目标地址所在的1字节。注意:写操作只能将目标字节中的1写为0,而不能将0写为1。一般当目标字节不为FFH时,必须先擦除。
11:擦除 EEPROM。擦除目标地址所在的1页(1扇区/512字节)。注意:擦除操作会一次擦除1个扇区(512字节),整个扇区的内容全部变成FFH。
设置完成 EEPROM 读、写、擦除的命令寄存器、地址寄存器、数据寄存器以及控制寄存器后,需要向触发寄存器IAP TRIG依次写入5AH、A5H(顺序不能交换)两个触发命令来触发相应的读、写、擦除操作。操作完成后,EEPROM地址寄存器 IAP ADDRH、IAP ADDRL 和 EEPROM 命令寄存器IAP_CMD 的内容不变。如果接下来要对下一个地址的数据进行操作,需手动更新地址寄存器IAP_ADDRH和寄存器IAP_ADDRL的值。
注意:每次EEPROM操作时,都要对IAPTRIG先写入5AH,再写入A5H,相应的命令才会生效。写完触发命令后,CPU会处于IDLE等待状态,直到相应的IAP操作执行完成后CPU才会从IDLE状态返回正常状态继续执行CPU指令。
IAPEN:EEPROM操作使能控制位
0:禁止EEPROM操作
1:使能EEPROM操作
SWBS:软件复位选择控制位,(需要与SWRST配合使用)
0:软件复位后从用户代码开始执行程序
1:软件复位后从系统ISP监控代码区开始执行程序
SWRST:软件复位控制位
0:无动作
1:产生软件复位
CMD FAIL:EEPROM操作失败状态位,需要软件清零
0:EEPROM操作正确
1:EEPROM操作失败
需要根据工作频率进行设置
若工作频率为12MHz,则需要将IAP_TPS设置为12;若工作频率为24MHz,则需要将IAP_TPS设置为24,其他频率以此类推。
STC8H系列单片机内部均有用于保存用户数据的EEPROM。内部的EEPROM有3 操作方式 :读、写和擦除,其中擦除操作是以扇区为单位进行操作,每扇区为512字节,即每执行一次擦除命令就会擦除一个扇区,而读数据和写数据都是以字节为单位进行操作的,即每执行一次读或者写命令时只能读出或者写入一个字节。
STC8H系列单片机内部的EEPROM的访问方式有两种:IAP方式和MOVC方式。IAP方式可对EEPROM执行读、写、擦除操作,但MOVC只能对EEPROM进行读操作,而不能讲行写和擦除操作。无论是使用IAP方式还是使用MOVC方式访问 EEPROM,首先都需要设置正确的目标地址。IAP方式时,目标地址与EEPROM实际的物理地址是一致的,均是从地址0000H开始访问,但若要使用 MOVC 指令进行读取 EEPROM数据时,目标地址必须是在 EEPROM实际的物理地址的基础上还有加上程序大小的偏移。下面以STC8H1K16这个型号为例,对目标地址进行详细说明:
STC8H1K16的程序空间为16K字节(0000h~3FFFh),EEPROM空间为12K(0000h-2FFh)。当需要对 EEPROM物理地址1234h的单元进行读、写、擦除时,若使用IAP方式进行访问时,设置的目标地址为1234h,即IAP ADDRH设置12h,IAP ADDRL设置34h,然后设置相应的触发命令即可对1234h 单元进行正确操作了。但若是使用MOVC方式读取EEPROM的1234h单元,则必须在1234h的基础上还有加上ROM空间的大小4000h,即必须将DPTR设置为5234h,然后才能使用MOVC指令进行读取。
注意:由于擦除是以512字节为单位进行操作的,所以执行擦除操作时所设置的目标地址的低9位是无意义的。例如: 执行擦除命令时,设置地址1234H/1200H/1300H/13FFH,最终执行擦除的动作都是相同的,都是擦除1200H~13FFH这512字节。
不同型号内部EEPROM的大小及访问地址会存在差异,针对各个型号EEPROM的详细大小和地址请参考下表
//关闭IAP
void IapIdle()
{
IAP_CONTR = 0; //关闭IAP功能
IAP_CMD = 0; //清除命令寄存器
IAP_TRIG = 0; //清除触发寄存器
IAP_ADDRH = 0x80; //将地址设置到非IAP区域
IAP_ADDRL = 0;
}
BYTE IapReadByte(WORD addr)
{
BYTE dat; //
IAP_CONTR = 0x80; //使能IAP
IAP_TPS = 12; //设置等待参数12MHz
IAP_CMD = CMD_READ; //设置IAP读命令
IAP_ADDRL = addr; //设置IAP低地址
IAP_ADDRH = addr >> 8; //设置IAP高地址
IAP_TRIG = 0x5a; //写触发命令(0x5a)
IAP_TRIG = 0xa5;
_nop_();
dat = IAP_DATA; //读IAP数据
IapIdle(); //关闭IAP功能
return dat;
}
void IapProgramByte(WORD addr, BYTE dat)
{
IAP_CONTR = 0x80; //使能IAP
IAP_TPS = IAP_DELAY; //设置等待参数
IAP_CMD = CMD_PROGRAM; //设置IAP写命令
IAP_ADDRL = addr; //设置IAP低地址
IAP_ADDRH = addr >> 8; //设置IAP高地址
IAP_DATA = dat; //写IAP数据
IAP_TRIG = 0x5a; //写触发命令(0x5a)
IAP_TRIG = 0xa5;
IAP_TRIG = 0xa5;
_nop_();
IapIdle();
}
oid IapEraseSector(WORD addr)
{
IAP_CONTR = 0x80; //使能IAP
IAP_TPS = IAP_DELAY; //设置等待参数
IAP_CMD = CMD_ERASE; //设置IAP擦除命令
IAP_ADDRL = addr; //设置IAP低地址
IAP_ADDRH = addr >> 8; //设置IAP高地址
IAP_TRIG = 0x5a; //写触发命令(0x5a)
IAP_TRIG = 0xa5;
_nop_();
IapIdle();
}
参考手册