wuzhengmin 发表于 2025-10-20 13:09:18

综合操作: (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就配置好了。

wuzhengmin 发表于 2025-10-20 13:11:16

下面看看定时器波特率计算:       SetTimer2Baudraye((u16)(65536UL - (MAIN_Fosc / 4) / Baudrate2));//配置定时器2是怎么来的?

串口 2 的波特率是可变的,其波特率固定由定时器 2 产生。当定时器采用 1T 模式时(12 倍速),相应的波特率的速度也会相应提高 12 倍。








wuzhengmin 发表于 2025-10-20 13:13:55

#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张贴图,一个是理论,一个实物

以前在学校长期不重视这些实操图纸,导致动手能力很弱

wuzhengmin 发表于 2025-10-20 13:14:57

接下来看中断:
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;         //允许中断

wuzhengmin 发表于 2025-10-20 13:18:38

//========================================================================
// 函数: 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的重装值.

wuzhengmin 发表于 2025-10-20 13:25:14

接着看中断函数:


//========================================================================
// 函数: 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;
    }
}
这个就相对简单很多,主要负责接受数据,清除中断标志位,发送中断标志位,发送忙标志。

wuzhengmin 发表于 2025-10-20 13:31:30

<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发送字符串函数。

wuzhengmin 发表于 2025-10-20 15:08:46

注意我们从串口助手输入的是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
}


wuzhengmin 发表于 2025-10-21 09:37:53





这里要先获取要读出数据的字节数:
/**************** 获取要读出数据的字节数 ****************************/
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);
}


wuzhengmin 发表于 2025-10-21 10:25:32




串口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,就说明我们这段语句正常执行了。

页: 1 [2] 3 4 5 6 7 8 9
查看完整版本: 25.SPI读写W25X40CL - 按k1(20251026已经解决)