找回密码
 立即注册
楼主: wuzhengmin

25.SPI读写W25X40CL - 按k1

[复制链接]
  • 打卡等级:偶尔看看III
  • 打卡总天数:47
  • 最近打卡:2025-10-21 08:57:14
已绑定手机

4

主题

356

回帖

627

积分

高级会员

积分
627
发表于 前天 13:09 | 显示全部楼层
综合操作:
(s2con & 0x3f) |0x40: 这行代码的综合效果是保留 S2CON 的低 6 位,并将第 6 位设置为 1
总结起来,这行代码的作用是确保 S2CON 寄存器的低 6 位保持不变,同时将第 6 位设置为 1
其实就是先把前2位清0后第6位置1,其他位不变!
这样就是选模式1--可变波特率8位数据方式(此时第三位B5要设置为0
B4S2REN = 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:是S2TI  B0:是S2RI
S2TI:串口 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就配置好了。

回复

使用道具 举报 送花

  • 打卡等级:偶尔看看III
  • 打卡总天数:47
  • 最近打卡:2025-10-21 08:57:14
已绑定手机

4

主题

356

回帖

627

积分

高级会员

积分
627
发表于 前天 13:11 | 显示全部楼层
下面看看定时器波特率计算:
       SetTimer2Baudraye((u16)(65536UL - (MAIN_Fosc / 4) / Baudrate2));//配置定时器2
是怎么来的?

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









截图202510201310591570.jpg
截图202510201311116924.jpg
回复

使用道具 举报 送花

  • 打卡等级:偶尔看看III
  • 打卡总天数:47
  • 最近打卡:2025-10-21 08:57:14
已绑定手机

4

主题

356

回帖

627

积分

高级会员

积分
627
发表于 前天 13:13 | 显示全部楼层
#define    Baudrate2           115200L
我们采用的波特率为Baudrate2时的定时器2重载值:65536UL - (MAIN_Fosc / 4) / Baudrate2
很明显我们是1T模式。
接下来看看串口2脚位设置:


串口1就有4组脚位切换,串口2就只有2组脚位:我们程序选1
S2_S = 1;         //UART2 switch to: 0:P1.0 P1.1,  1: P4.6 P4.7

所以引脚硬件连线,我们要找P4.6 –RxD2  P4.7 T xD2




主要注意看我下面的2张贴图,一个是理论,一个实物

以前在学校长期不重视这些实操图纸,导致动手能力很弱
截图202510201313337258.jpg
截图202510210907278810.jpg
截图202510210908102703.jpg
回复

使用道具 举报 送花

  • 打卡等级:偶尔看看III
  • 打卡总天数:47
  • 最近打卡:2025-10-21 08:57:14
已绑定手机

4

主题

356

回帖

627

积分

高级会员

积分
627
发表于 前天 13:14 | 显示全部楼层
接下来看中断:

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;         //允许中断

截图202510201314331463.jpg
回复

使用道具 举报 送花

  • 打卡等级:偶尔看看III
  • 打卡总天数:47
  • 最近打卡:2025-10-21 08:57:14
已绑定手机

4

主题

356

回帖

627

积分

高级会员

积分
627
发表于 前天 13:18 | 显示全部楼层
  1. //========================================================================
  2. // 函数: SetTimer2Baudraye(u16 dat)
  3. // 描述: 设置Timer2做波特率发生器。
  4. // 参数: dat: Timer2的重装值.
  5. // 返回: none.
  6. // 版本: VER1.0
  7. // 日期: 2014-11-28
  8. // 备注:
  9. //========================================================================
  10. void SetTimer2Baudraye(u16 dat)  // 选择波特率, 2: 使用Timer2做波特率, 其它值: 无效.
  11. {
  12.     T2R = 0;        //Timer stop
  13.     T2_CT = 0;        //Timer2 set As Timer
  14.     T2x12 = 1;        //Timer2 set as 1T mode
  15.     T2H = (u8)(dat / 256);
  16.     T2L = (u8)(dat % 256);
  17.     ET2 = 0;    //禁止中断
  18.     T2R = 1;        //Timer run enable
  19. }
复制代码
描述: 设置Timer2做波特率发生器。
参数: dat: Timer2的重装值.
主要是设置对应#define     Baudrate2           115200L的Timer2的重装值.

回复

使用道具 举报 送花

  • 打卡等级:偶尔看看III
  • 打卡总天数:47
  • 最近打卡:2025-10-21 08:57:14
已绑定手机

4

主题

356

回帖

627

积分

高级会员

积分
627
发表于 前天 13:25 | 显示全部楼层
接着看中断函数:


  1. //========================================================================
  2. // 函数: void UART2_int (void) interrupt UART2_VECTOR
  3. // 描述: UART2中断函数。
  4. // 参数: nine.
  5. // 返回: none.
  6. // 版本: VER1.0
  7. // 日期: 2014-11-28
  8. // 备注:
  9. //========================================================================
  10. void UART2_int (void) interrupt 8
  11. {
  12.     if(S2RI)
  13.     {
  14.         S2RI = 0;    //Clear Rx flag
  15.         RX2_Buffer[RX2_Cnt] = S2BUF;
  16.         if(++RX2_Cnt >= UART2_BUF_LENGTH)   RX2_Cnt = 0;
  17.         RX2_TimeOut = 5;
  18.     }
  19.     if(S2TI)
  20.     {
  21.         S2TI = 0;    //Clear Tx flag
  22.         B_TX2_Busy = 0;
  23.     }
  24. }
复制代码
这个就相对简单很多,主要负责接受数据,清除中断标志位,发送中断标志位,发送忙标志。
回复

使用道具 举报 送花

  • 打卡等级:偶尔看看III
  • 打卡总天数:47
  • 最近打卡:2025-10-21 08:57:14
已绑定手机

4

主题

356

回帖

627

积分

高级会员

积分
627
发表于 前天 13:31 | 显示全部楼层
  1. <div class="blockcode"><blockquote>//========================================================================
  2. // 函数: void UART2_TxByte(u8 dat)
  3. // 描述: 发送一个字节.
  4. // 参数: 无.
  5. // 返回: 无.
  6. // 版本: V1.0, 2014-6-30
  7. //========================================================================
  8. void UART2_TxByte(u8 dat)
  9. {
  10.     S2BUF = dat;
  11.     B_TX2_Busy = 1;
  12.     while(B_TX2_Busy);
  13. }
  14. //========================================================================
  15. // 函数: void PrintString2(u8 *puts)
  16. // 描述: 串口2发送字符串函数。
  17. // 参数: puts:  字符串指针.
  18. // 返回: none.
  19. // 版本: VER1.0
  20. // 日期: 2014-11-28
  21. // 备注:
  22. //========================================================================
  23. void PrintString2(u8 *puts) //发送一个字符串
  24. {
  25.     for (; *puts != 0;  puts++) UART2_TxByte(*puts);    //遇到停止符0结束
  26. }
复制代码

UART2发送一个字节. 和串口2发送字符串函数。
回复

使用道具 举报 送花

  • 打卡等级:偶尔看看III
  • 打卡总天数:47
  • 最近打卡:2025-10-21 08:57:14
已绑定手机

4

主题

356

回帖

627

积分

高级会员

积分
627
发表于 前天 15:08 | 显示全部楼层
注意我们从串口助手输入的是ASCII码,要转成BIN二进制码才能使用

先看懂下面2个函数:
  1. /**************** ASCII码转BIN (ASCII码转二进制码)****************************/
  2. u8  CheckData(u8 dat)
  3. {
  4.     if((dat >= '0') && (dat <= '9'))        return (dat-'0');
  5.     if((dat >= 'A') && (dat <= 'F'))        return (dat-'A'+10);
  6.     return 0xff;
  7. }
  8. /**************** 获取写入地址 ****************************/
  9. u32 GetAddress(void)
  10. {
  11.     u32 address;
  12.     u8  i,j;
  13.    
  14.     address = 0;
  15.     if((RX2_Buffer[2] == '0') && (RX2_Buffer[3] == 'X'))
  16.     {
  17.         for(i=4; i<10; i++)
  18.         {
  19.             j = CheckData(RX2_Buffer[i]);
  20.             if(j >= 0x10)   return 0x80000000;  //error
  21.             address = (address << 4) + j;
  22.         }
  23.         return (address);
  24.     }
  25.     return  0x80000000; //error
  26. }
复制代码


回复

使用道具 举报 送花

  • 打卡等级:偶尔看看III
  • 打卡总天数:47
  • 最近打卡:2025-10-21 08:57:14
已绑定手机

4

主题

356

回帖

627

积分

高级会员

积分
627
发表于 昨天 09:37 | 显示全部楼层




这里要先获取要读出数据的字节数:
  1. /**************** 获取要读出数据的字节数 ****************************/
  2. u8  GetDataLength(void)
  3. {
  4.     u8  i;
  5.     u8  length;
  6.    
  7.     length = 0;
  8.     for(i=11; i<RX2_Cnt; i++)
  9.     {
  10.         if(CheckData(RX2_Buffer[i]) >= 10)  break;
  11.         length = length * 10 + CheckData(RX2_Buffer[i]);
  12.     }
  13.     return (length);
  14. }
复制代码

回复

使用道具 举报 送花

  • 打卡等级:偶尔看看III
  • 打卡总天数:47
  • 最近打卡:2025-10-21 08:57:14
已绑定手机

4

主题

356

回帖

627

积分

高级会员

积分
627
发表于 昨天 10:25 | 显示全部楼层



串口2 处理函数略微复杂:

  1. /**************** 串口2处理函数 ****************************/
  2. void RX2_Check(void)
  3. {
  4.     u8  i,j;
  5.     if((RX2_Cnt == 1) && (RX2_Buffer[0] == 'C'))    //发送C强制允许操作
  6.     {
  7.         B_FlashOK = 1;
  8.         PrintString2("强制允许操作FLASH!\r\n");
  9.     }
  10.     if(!B_FlashOK)
  11.     {
  12.         PrintString2("PM25LV040/W25X40CL/W25Q80BV不存在, 不能操作FLASH!\r\n");
  13.         return;
  14.     }
  15.    
  16.     F0 = 0;
  17.     if((RX2_Cnt >= 10) && (RX2_Buffer[1] == ' '))   //最短命令为10个字节
  18.     {
  19.         for(i=0; i<10; i++)
  20.         {
  21.             if((RX2_Buffer[i] >= 'a') && (RX2_Buffer[i] <= 'z'))    RX2_Buffer[i] = RX2_Buffer[i] - 'a' + 'A';//小写转大写
  22.         }
  23.         Flash_addr = GetAddress();
  24.         if(Flash_addr < 0x80000000)
  25.         {
  26.             if(RX2_Buffer[0] == 'E')    //擦除
  27.             {
  28.                 FlashSectorErase(Flash_addr);
  29.                 PrintString2("已擦掉当前扇区内容!\r\n");
  30.                 F0 = 1;
  31.             }
  32.             else if((RX2_Buffer[0] == 'W') && (RX2_Cnt >= 12) && (RX2_Buffer[10] == ' '))   //写入N个字节
  33.             {
  34.                 j = RX2_Cnt - 11;
  35.                 for(i=0; i<j; i++)  tmp[i] = 0xff;      //检测要写入的空间是否为空
  36.                 i = SPI_Read_Compare(Flash_addr,tmp,j);
  37.                 if(i > 0)
  38.                 {
  39.                     PrintString2("要写入的地址为非空,不能写入,请先擦掉!\r\n");
  40.                 }
  41.                 else
  42.                 {
  43.                     SPI_Write_Nbytes(Flash_addr,&RX2_Buffer[11],j);     //写N个字节
  44.                     i = SPI_Read_Compare(Flash_addr,&RX2_Buffer[11],j); //比较写入的数据
  45.                     if(i == 0)
  46.                     {
  47.                         PrintString2("已写入");
  48.                         if(j >= 100)    {UART2_TxByte((u8)(j/100+'0'));   j = j % 100;}
  49.                         if(j >= 10)     {UART2_TxByte((u8)(j/10+'0'));    j = j % 10;}
  50.                         UART2_TxByte((u8)(j%10+'0'));
  51.                         PrintString2("字节内容!\r\n");
  52.                     }
  53.                     else        PrintString2("写入错误!\r\n");
  54.                 }
  55.                 F0 = 1;
  56.             }
  57.             else if((RX2_Buffer[0] == 'R') && (RX2_Cnt >= 12) && (RX2_Buffer[10] == ' '))   //读出N个字节
  58.             {
  59.                 j = GetDataLength();
  60.                 if((j > 0) && (j < EE_BUF_LENGTH))
  61.                 {
  62.                     SPI_Read_Nbytes(Flash_addr,tmp,j);
  63.                     PrintString2("读出");
  64.                     if(j>=100)  UART2_TxByte((u8)(j/100+'0'));
  65.                     UART2_TxByte((u8)(j%100/10+'0'));
  66.                     UART2_TxByte((u8)(j%10+'0'));
  67.                     PrintString2("个字节内容如下:\r\n");
  68.                     for(i=0; i<j; i++)  UART2_TxByte(tmp[i]);
  69.                     UART2_TxByte(0x0d);
  70.                     UART2_TxByte(0x0a);
  71.                     F0 = 1;
  72.                 }
  73.             }
  74.         }
  75.     }
  76.     if(!F0) PrintString2("命令错误!\r\n");
  77. }
复制代码
这里 if((RX2_Cnt >= 10) && (RX2_Buffer[1] == ' '))   //最短命令“E 0x001234”为10个字节且第二个字符是空格
意思是合格的命令才进来
而F0:
//在STC32G.h里有   sbit   F0 =  PSW^5;


看看手册:

原来是状态寄存器PSW的第5 F0:通用标志
我们先给F0 软件置0 ,等我们执行一段语句命令后,F0还能成功保持是0,就执行if(!F0) PrintString2("命令错误!\r\n"); 打印“命令错误!”,不成功保持0,就说明我们这段语句正常执行了。


截图202510211006526252.jpg
回复

使用道具 举报 送花

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|手机版|深圳国芯人工智能有限公司 ( 粤ICP备2022108929号-2 )

GMT+8, 2025-10-22 06:55 , Processed in 0.136941 second(s), 93 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

快速回复 返回顶部 返回列表