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’。
/**************** 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,依此类推)。
/**************** 获取写入地址 ****************************/
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
}
这样注释,很好理解吧?
/**************** 获取要读出数据的字节数 ****************************/
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才清零,准备下一个轮回
冲哥哥的第一个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处理数据
都能看懂的,自己写就不行啦
到此为止,我们把STC的例子程序读了一遍,编译烧录进去试验箱:
要连接个STC-link1D ,自动下载(不用平时USB——CDC串口)
结果如下:
记得主频是22.1184 !
这个是UART2不是USB-CDC ! 主频不到,就乱码一堆
反复试验了删除读取写入
都不会死机,初步实验成功!
上面是用了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);
复制代码
从思路上看,确实原来有漏洞
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
2. 写入前等待擦除完成(轮询 BUSY 位);
这个我们有查询忙,应该不是这里的问题
3. 写入数据:
c
W25X40CLPageProgram(W25X40CLADDRESS, buffer, length);
也确实有写入动作,应该也不是这里的问题
4. 写入后等待写入完成;
我们有查询忙,应该不是这里的问题
5. 读取验证:
c
W25X40CLReadData(W25X40CLADDRESS, readbuffer, length);
这个就没有考虑,要增加比较函数