wuzhengmin 发表于 2025-10-21 15:26:29

u8Hex2Ascii(u8 dat)   //Hex 转Ascii
{
    dat &= 0x0f;
    if(dat < 10)    return (dat+'0');
    return (dat-10+'A');
}

代码解释1.         dat &=0x0f;:这行代码的作用是将 dat 的高四位清零,只保留低四位。这通常用于提取一个字节中的低四位十六进制数。2.         if(dat< 10) return (dat+'0');:如果 dat 的值小于 10,则返回对应的 ASCII 数字字符。例如,如果 dat 是 5,则返回 ‘5’。3.       return(dat-10+'A');:如果 dat 的值大于等于 10,则返回对应的 ASCII 大写字母字符。例如,如果 dat 是 10,则返回 ‘A’,如果 dat 是 15,则返回 ‘F’。

wuzhengmin 发表于 2025-10-21 15:32:09

/**************** ASCII码转BIN ****************************/
u8CheckData(u8 dat)
{
    if((dat >= '0') && (dat <= '9'))      return (dat-'0');
    if((dat >= 'A') && (dat <= 'F'))      return (dat-'A'+10);
    return 0xff;
}



是用于检查字符 dat 是否是十六进制数字(0-9, a-f),并返回相应的数值。这通常是十六进制(Hex)到 ASCII 转换:代码解释1.   检查是否为数字(0-9):2.if((dat>= '0') && (dat <= '9')) return (dat-'0');这段代码检查 dat 是否是字符 ‘0’ 到 ‘9’ 之间的字符。如果是,则返回 dat 减去 ‘0’ 的 ASCII 值,这将得到对应的数字(例如,‘5’ - ‘0’ = 5)。3.   检查是否为大写字母(A-F):4.if((dat>= 'A') && (dat <= 'F'))这段代码检查 dat 是否是字符 ‘A’ 到 ‘F’ 之间的字符。如果 dat 是这些字符之一,则需要进一步处理以返回对应的数值(例如,‘A’ 对应 10,‘B’ 对应 11,依此类推)。

wuzhengmin 发表于 2025-10-21 15:47:42

/**************** 获取写入地址 ****************************/
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);//ASCII码转BIN,字符转数字
            if(j >= 0x10)   return 0x80000000;//一位不可能大于16error
            address = (address << 4) + j;   //左移4位累加24位地址
      }
      return (address);//返回得到的32位正确地址

    }
    return0x80000000; //error
}

这样注释,很好理解吧?


wuzhengmin 发表于 2025-10-21 16:15:29

/**************** 获取要读出数据的字节数 ****************************/
u8GetDataLength(void)
{
    u8i;
    u8length;

    length = 0;
    for(i=11; i<RX2_Cnt; i++)//最短的命令R 0X000100 是11位,后面开始是要读出几个数
    {
      if(CheckData(RX2_Buffer) >= 10)break;//10进制每一位不能大于10
      length = length * 10 + CheckData(RX2_Buffer); //每读到一位就把上次的进一位,累加得到要读的数据长度
    }
    return (length);
}



读这个之前,要先读串口2 中断函数:


//========================================================================
// 函数: 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_Cnt在++
                  //直到假如超过UART2_BUF_LENGTH才清零,准备下一个轮回

      RX2_TimeOut = 5; //设置接受时间溢出
    }

    if(S2TI)
    {
      S2TI = 0;    //Clear Tx flag
      B_TX2_Busy = 0;
    }
}



要能从if(++RX2_Cnt >= UART2_BUF_LENGTH)   RX2_Cnt = 0;这句看出RX2_Cnt在++直到假如超过UART2_BUF_LENGTH才清零,准备下一个轮回



wuzhengmin 发表于 2025-10-21 16:27:11

冲哥哥的第一个SPI的作业,快做完啦


回到主函数:


/********************* 主函数 *************************/
void main(void)
{
    WTST = 0;//设置程序指令延时参数,赋值为0可将CPU执行指令的速度设置为最快
    EAXFR = 1; //扩展寄存器(XFR)访问使能
    CKCON = 0; //提高访问XRAM速度

    P0M1 = 0x30;   P0M0 = 0x30;   //设置P0.4、P0.5为漏极开路(实验箱加了上拉电阻到3.3V)
    P1M1 = 0x32;   P1M0 = 0x32;   //设置P1.1、P1.4、P1.5为漏极开路(实验箱加了上拉电阻到3.3V), P1.1在PWM当DAC电路通过电阻串联到P2.3
    P2M1 = 0x00;   P2M0 = 0x00;   //设置为准双向口
    P3M1 = 0x50;   P3M0 = 0x50;   //设置P3.4、P3.6为漏极开路(实验箱加了上拉电阻到3.3V)
    P4M1 = 0x3c;   P4M0 = 0x3c;   //设置P4.2~P4.5为漏极开路(实验箱加了上拉电阻到3.3V)
    P5M1 = 0x0c;   P5M0 = 0x0c;   //设置P5.2、P5.3为漏极开路(实验箱加了上拉电阻到3.3V)
    P6M1 = 0xff;   P6M0 = 0xff;   //设置为漏极开路(实验箱加了上拉电阻到3.3V)
    P7M1 = 0x00;   P7M0 = 0x00;   //设置为准双向口

    delay_ms(10);
    UART2_config(2);    // 选择波特率, 2: 使用Timer2做波特率, 其它值: 无效.
   
    EA = 1;                     //打开总中断
   
    PrintString2("命令设置:\r\n");
    PrintString2("E 0x001234            --> 扇区擦掉十六进制地址\r\n");
    PrintString2("W 0x001234 1234567890 --> 写入操作十六进制地址写入内容\r\n");
    PrintString2("R 0x001234 10         --> 读出操作十六进制地址读出字节\r\n");
    PrintString2("C                     --> 如果检测不到PM25LV040/W25X40CL/W25Q80BV, 发送C强制允许操作.\r\n\r\n");

    SPI_init();
    FlashCheckID();
    FlashCheckID();
   
    if(!B_FlashOK)PrintString2("未检测到PM25LV040/W25X40CL/W25Q80BV!\r\n");
    else
    {
      if(B_FlashOK == 1)
      {
            PrintString2("检测到PM25LV040!\r\n");
      }
      else if(B_FlashOK == 2)
      {
            PrintString2("检测到W25X40CL!\r\n");
      }
      else if(B_FlashOK == 3)
      {
            PrintString2("检测到W25Q80BV!\r\n");
      }
      PrintString2("制造商ID1 = 0x");
      UART2_TxByte(Hex2Ascii(PM25LV040_ID1 >> 4));
      UART2_TxByte(Hex2Ascii(PM25LV040_ID1));
      PrintString2("\r\n      ID2 = 0x");
      UART2_TxByte(Hex2Ascii(PM25LV040_ID2 >> 4));
      UART2_TxByte(Hex2Ascii(PM25LV040_ID2));
      PrintString2("\r\n   设备ID = 0x");
      UART2_TxByte(Hex2Ascii(PM25LV040_ID >> 4));
      UART2_TxByte(Hex2Ascii(PM25LV040_ID));
      PrintString2("\r\n");
    }

    while(1)
    {
      delay_ms(1);
               
      if(RX2_TimeOut > 0)
      {
            if(--RX2_TimeOut == 0)//超时,则串口接收结束
            {
                if(RX2_Cnt > 0)
                {
                  RX2_Check();    //串口2处理数据
                }
                RX2_Cnt = 0;
            }
      }
    }
}


其实 还是在运行 RX2_Check();    //串口2处理数据
都能看懂的,自己写就不行啦

wuzhengmin 发表于 2025-10-21 16:58:29

到此为止,我们把STC的例子程序读了一遍,编译烧录进去试验箱:

要连接个STC-link1D ,自动下载(不用平时USB——CDC串口)

结果如下:




记得主频是22.1184 !

这个是UART2不是USB-CDC ! 主频不到,就乱码一堆

wuzhengmin 发表于 2025-10-21 17:00:15

反复试验了删除读取写入


都不会死机,初步实验成功!

wuzhengmin 发表于 2025-10-24 07:48:31

上面是用了UART2来和PC机通讯
那么我们原来是用STC-USB-CDC的,能否回去STC-USB-CDC串口呢?

看看STC的提示:

1. 擦除目标扇区(4KB):

[*]c
[*]   W25X40CLEraseSector(W25X40CLADDRESS & 0xFFFFF000); // 对齐到扇区边界

复制代码
2. 写入前等待擦除完成(轮询 BUSY 位);
3. 写入数据:

[*]c
[*]   W25X40CLPageProgram(W25X40CLADDRESS, buffer, length);

复制代码4. 写入后等待写入完成;
5. 读取验证:

[*]c
[*]   W25X40CLReadData(W25X40CLADDRESS, readbuffer, length);

复制代码

wuzhengmin 发表于 2025-10-24 07:57:32

从思路上看,确实原来有漏洞

1 W25X40CLEraseSector(W25X40CLADDRESS & 0xFFFFF000); // 对齐到扇区边界

实际address & 0xfffff000时,这通常是为了将地址对齐到某个特定的块或页面边界。

地址对齐:0xfffff000是一个掩码,用于将地址对齐到4KB的边界。这是因为W25X40CL的页大小通常是256字节或更大的块。对齐到4KB边界可以确保你在进行块操作(如擦除)时,地址是正确的。

具体操作:

address是你要操作的地址。
address & 0xfffff000将地址对齐到4KB边界。
例如,如果你的地址是0x12345678,那么对齐后的地址将是:

0x12345678 & 0xfffff000 = 0x12345000

W25X40CLEraseSector(W25X40CLADDRESS & 0xFFFFF000); // 对齐到扇区边界

这样写地址,就保证了4KB对齐,首先地址不出错。

4K 实际是 4*1024=4096 = 0X1000



wuzhengmin 发表于 2025-10-24 08:02:55

2. 写入前等待擦除完成(轮询 BUSY 位);

这个我们有查询忙,应该不是这里的问题

3. 写入数据:

c
   W25X40CLPageProgram(W25X40CLADDRESS, buffer, length);

也确实有写入动作,应该也不是这里的问题

4. 写入后等待写入完成;

我们有查询忙,应该不是这里的问题

5. 读取验证:

c
   W25X40CLReadData(W25X40CLADDRESS, readbuffer, length);


这个就没有考虑,要增加比较函数


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