综合操作: (s2con & 0x3f) |0x40: 这行代码的综合效果是保留 S2CON 的低 6 位,并将第 6 位设置为 1。总结起来,这行代码的作用是确保 S2CON 寄存器的低 6 位保持不变,同时将第 6 位设置为 1。其实就是先把前2位清0后第6位置1,其他位不变!这样就是选模式1--可变波特率8位数据方式(此时第三位B5要设置为0)B4:S2REN = 1; //再把第4位置1允许接收S2CON到此前4位变成了0101。后4位B3是S2TB8,B2是S2RB8
S2TB8:当串口 2 使用模式 2 或模式 3 时,S2TB8 为要发送的第 9 位数据,按需要由软件置位或清 0。在模式 0 和模式 1 中,该位不用。S2RB8:当串口 2 使用模式 2 或模式 3 时,S2RB8 为接收到的第 9 位数据,一般用作校验位或者地址帧/数据帧标志位。在模式9和模式1中,该位不用。B1:是S2TIB0:是S2RIS2TI:串口 2 发送中断请求标志位。在模式 0 中,当串口发送数据第 8 位结束时,由硬件自动将 S2TI置1,向主机请求中断,响应中断后S2TI必须用软件清零。在其他模式中,则在停止位开始发送时由硬件自动将S2TI置1,向CPU发请求中断,响应中断后S2TI必须用软件清零。S2RI:串口 2 接收中断请求标志位。在模式 0 中,当串口接收第 8 位数据结束时,由硬件自动将 S2RI置1,向主机请求中断,响应中断后S2RI必须用软件清零。在其他模式中,串行接收到停止位的中间时刻由硬件自动将S2RI置1,向CPU发中断申请,响应中断后S2RI必须由软件清零。所以后4位中的前2位模式1不用管,后2位在中断函数处理
到此 s2con就配置好了。
下面看看定时器波特率计算: SetTimer2Baudraye((u16)(65536UL - (MAIN_Fosc / 4) / Baudrate2));//配置定时器2是怎么来的?
串口 2 的波特率是可变的,其波特率固定由定时器 2 产生。当定时器采用 1T 模式时(12 倍速),相应的波特率的速度也会相应提高 12 倍。
#define Baudrate2 115200L我们采用的波特率为Baudrate2时的定时器2重载值:65536UL - (MAIN_Fosc / 4) / Baudrate2很明显我们是1T模式。接下来看看串口2脚位设置:
串口1就有4组脚位切换,串口2就只有2组脚位:我们程序选1S2_S = 1; //UART2 switch to: 0:P1.0 P1.1,1: P4.6 P4.7
所以引脚硬件连线,我们要找P4.6 –RxD2P4.7 T xD2
主要注意看我下面的2张贴图,一个是理论,一个实物
以前在学校长期不重视这些实操图纸,导致动手能力很弱
接下来看中断:
EUSB:USB中断允许位。(注意:此位为只写位,不可读取,读取始终为0)0:禁止 USB 中断 1:允许 USB 中断ET4:定时/计数器T4的溢出中断允许位。0:禁止 T4 中断 1:允许 T4 中断ET3:定时/计数器T3的溢出中断允许位。0:禁止 T3 中断 1:允许 T3 中断ES4:串行口 4 中断允许位。0:禁止串行口 4 中断 1:允许串行口 4 中断ES3:串行口 3 中断允许位。0:禁止串行口 3 中断1:允许串行口 3 中断 ET2:定时/计数器T2的溢出中断允许位。0:禁止 T2 中断 1:允许 T2 中断ESPI:SPI中断允许位。0:禁止 SPI 中断 1:允许 SPI 中断ES2:串行口 2 中断允许位。0:禁止串行口 2 中断 1:允许串行口 2 中断
我们这里是:ES2 = 1; //允许中断
//========================================================================
// 函数: SetTimer2Baudraye(u16 dat)
// 描述: 设置Timer2做波特率发生器。
// 参数: dat: Timer2的重装值.
// 返回: none.
// 版本: VER1.0
// 日期: 2014-11-28
// 备注:
//========================================================================
void SetTimer2Baudraye(u16 dat)// 选择波特率, 2: 使用Timer2做波特率, 其它值: 无效.
{
T2R = 0; //Timer stop
T2_CT = 0; //Timer2 set As Timer
T2x12 = 1; //Timer2 set as 1T mode
T2H = (u8)(dat / 256);
T2L = (u8)(dat % 256);
ET2 = 0; //禁止中断
T2R = 1; //Timer run enable
}
描述: 设置Timer2做波特率发生器。 参数: dat: Timer2的重装值. 主要是设置对应#define Baudrate2 115200L的Timer2的重装值.
接着看中断函数:
//========================================================================
// 函数: void UART2_int (void) interrupt UART2_VECTOR
// 描述: UART2中断函数。
// 参数: nine.
// 返回: none.
// 版本: VER1.0
// 日期: 2014-11-28
// 备注:
//========================================================================
void UART2_int (void) interrupt 8
{
if(S2RI)
{
S2RI = 0; //Clear Rx flag
RX2_Buffer = S2BUF;
if(++RX2_Cnt >= UART2_BUF_LENGTH) RX2_Cnt = 0;
RX2_TimeOut = 5;
}
if(S2TI)
{
S2TI = 0; //Clear Tx flag
B_TX2_Busy = 0;
}
}
这个就相对简单很多,主要负责接受数据,清除中断标志位,发送中断标志位,发送忙标志。
<div class="blockcode"><blockquote>//========================================================================
// 函数: void UART2_TxByte(u8 dat)
// 描述: 发送一个字节.
// 参数: 无.
// 返回: 无.
// 版本: V1.0, 2014-6-30
//========================================================================
void UART2_TxByte(u8 dat)
{
S2BUF = dat;
B_TX2_Busy = 1;
while(B_TX2_Busy);
}
//========================================================================
// 函数: void PrintString2(u8 *puts)
// 描述: 串口2发送字符串函数。
// 参数: puts:字符串指针.
// 返回: none.
// 版本: VER1.0
// 日期: 2014-11-28
// 备注:
//========================================================================
void PrintString2(u8 *puts) //发送一个字符串
{
for (; *puts != 0;puts++) UART2_TxByte(*puts); //遇到停止符0结束
}
UART2发送一个字节. 和串口2发送字符串函数。
注意我们从串口助手输入的是ASCII码,要转成BIN二进制码才能使用
先看懂下面2个函数:
/**************** ASCII码转BIN (ASCII码转二进制码)****************************/
u8CheckData(u8 dat)
{
if((dat >= '0') && (dat <= '9')) return (dat-'0');
if((dat >= 'A') && (dat <= 'F')) return (dat-'A'+10);
return 0xff;
}
/**************** 获取写入地址 ****************************/
u32 GetAddress(void)
{
u32 address;
u8i,j;
address = 0;
if((RX2_Buffer == '0') && (RX2_Buffer == 'X'))
{
for(i=4; i<10; i++)
{
j = CheckData(RX2_Buffer);
if(j >= 0x10) return 0x80000000;//error
address = (address << 4) + j;
}
return (address);
}
return0x80000000; //error
}
这里要先获取要读出数据的字节数:
/**************** 获取要读出数据的字节数 ****************************/
u8GetDataLength(void)
{
u8i;
u8length;
length = 0;
for(i=11; i<RX2_Cnt; i++)
{
if(CheckData(RX2_Buffer) >= 10)break;
length = length * 10 + CheckData(RX2_Buffer);
}
return (length);
}
串口2 处理函数略微复杂:
/**************** 串口2处理函数 ****************************/
void RX2_Check(void)
{
u8i,j;
if((RX2_Cnt == 1) && (RX2_Buffer == 'C')) //发送C强制允许操作
{
B_FlashOK = 1;
PrintString2("强制允许操作FLASH!\r\n");
}
if(!B_FlashOK)
{
PrintString2("PM25LV040/W25X40CL/W25Q80BV不存在, 不能操作FLASH!\r\n");
return;
}
F0 = 0;
if((RX2_Cnt >= 10) && (RX2_Buffer == ' ')) //最短命令为10个字节
{
for(i=0; i<10; i++)
{
if((RX2_Buffer >= 'a') && (RX2_Buffer <= 'z')) RX2_Buffer = RX2_Buffer - 'a' + 'A';//小写转大写
}
Flash_addr = GetAddress();
if(Flash_addr < 0x80000000)
{
if(RX2_Buffer == 'E') //擦除
{
FlashSectorErase(Flash_addr);
PrintString2("已擦掉当前扇区内容!\r\n");
F0 = 1;
}
else if((RX2_Buffer == 'W') && (RX2_Cnt >= 12) && (RX2_Buffer == ' ')) //写入N个字节
{
j = RX2_Cnt - 11;
for(i=0; i<j; i++)tmp = 0xff; //检测要写入的空间是否为空
i = SPI_Read_Compare(Flash_addr,tmp,j);
if(i > 0)
{
PrintString2("要写入的地址为非空,不能写入,请先擦掉!\r\n");
}
else
{
SPI_Write_Nbytes(Flash_addr,&RX2_Buffer,j); //写N个字节
i = SPI_Read_Compare(Flash_addr,&RX2_Buffer,j); //比较写入的数据
if(i == 0)
{
PrintString2("已写入");
if(j >= 100) {UART2_TxByte((u8)(j/100+'0')); j = j % 100;}
if(j >= 10) {UART2_TxByte((u8)(j/10+'0')); j = j % 10;}
UART2_TxByte((u8)(j%10+'0'));
PrintString2("字节内容!\r\n");
}
else PrintString2("写入错误!\r\n");
}
F0 = 1;
}
else if((RX2_Buffer == 'R') && (RX2_Cnt >= 12) && (RX2_Buffer == ' ')) //读出N个字节
{
j = GetDataLength();
if((j > 0) && (j < EE_BUF_LENGTH))
{
SPI_Read_Nbytes(Flash_addr,tmp,j);
PrintString2("读出");
if(j>=100)UART2_TxByte((u8)(j/100+'0'));
UART2_TxByte((u8)(j%100/10+'0'));
UART2_TxByte((u8)(j%10+'0'));
PrintString2("个字节内容如下:\r\n");
for(i=0; i<j; i++)UART2_TxByte(tmp);
UART2_TxByte(0x0d);
UART2_TxByte(0x0a);
F0 = 1;
}
}
}
}
if(!F0) PrintString2("命令错误!\r\n");
}
这里 if((RX2_Cnt >= 10) && (RX2_Buffer == ' ')) //最短命令“E 0x001234”为10个字节且第二个字符是空格
意思是合格的命令才进来
而F0:
//在STC32G.h里有 sbit F0 =PSW^5;
看看手册:
原来是状态寄存器PSW的第5位 F0:通用标志 我们先给F0 软件置0 ,等我们执行一段语句命令后,F0还能成功保持是0,就执行if(!F0) PrintString2("命令错误!\r\n"); 打印“命令错误!”,不成功保持0,就说明我们这段语句正常执行了。